diff --git a/.editorconfig b/.editorconfig index 2e3045fb17..f579ff5d3d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -104,8 +104,8 @@ dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:war dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning dotnet_style_parentheses_in_other_operators = always_for_clarity:suggestion # Expression-level preferences -dotnet_style_object_initializer = true:warning -dotnet_style_collection_initializer = true:warning +dotnet_style_object_initializer = true:error +dotnet_style_collection_initializer = true:error dotnet_style_explicit_tuple_names = true:warning dotnet_style_prefer_inferred_tuple_names = true:warning dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning @@ -135,9 +135,9 @@ csharp_style_prefer_null_check_over_type_check = true:warning # https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules [*.{cs,csx,cake}] # 'var' preferences -csharp_style_var_for_built_in_types = false:warning -csharp_style_var_when_type_is_apparent = false:warning -csharp_style_var_elsewhere = false:warning +csharp_style_var_for_built_in_types = false:error +csharp_style_var_when_type_is_apparent = false:error +csharp_style_var_elsewhere = false:error # Expression-bodied members csharp_style_expression_bodied_methods = true:warning csharp_style_expression_bodied_constructors = true:warning @@ -160,7 +160,10 @@ csharp_style_pattern_local_over_anonymous_function = true:warning csharp_style_deconstructed_variable_declaration = true:warning csharp_style_prefer_index_operator = true:warning csharp_style_prefer_range_operator = true:warning -csharp_style_implicit_object_creation_when_type_is_apparent = true:warning +csharp_style_implicit_object_creation_when_type_is_apparent = true:error +# ReSharper inspection severities +resharper_arrange_object_creation_when_type_evident_highlighting = error +resharper_arrange_object_creation_when_type_not_evident_highlighting = error # "Null" checking preferences csharp_style_throw_expression = true:warning csharp_style_conditional_delegate_call = true:warning @@ -172,6 +175,11 @@ 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 +# Collection preferences +dotnet_style_prefer_collection_expression = true:error +resharper_use_collection_expression_highlighting =true:error ########################################## # Unnecessary Code Rules diff --git a/.gitattributes b/.gitattributes index 3647a7063d..031420e539 100644 --- a/.gitattributes +++ b/.gitattributes @@ -84,19 +84,16 @@ # treat as binary ############################################################################### *.basis binary +*.a binary *.dll binary -*.eot binary +*.dylib binary *.exe binary -*.otf binary *.pdf binary *.ppt binary *.pptx binary *.pvr binary +*.so binary *.snk binary -*.ttc binary -*.ttf binary -*.woff binary -*.woff2 binary *.xls binary *.xlsx binary ############################################################################### @@ -126,6 +123,7 @@ *.dds filter=lfs diff=lfs merge=lfs -text *.ktx filter=lfs diff=lfs merge=lfs -text *.ktx2 filter=lfs diff=lfs merge=lfs -text +*.astc filter=lfs diff=lfs merge=lfs -text *.pam filter=lfs diff=lfs merge=lfs -text *.pbm filter=lfs diff=lfs merge=lfs -text *.pgm filter=lfs diff=lfs merge=lfs -text @@ -133,3 +131,22 @@ *.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 +*.heic filter=lfs diff=lfs merge=lfs -text +*.hif filter=lfs diff=lfs merge=lfs -text +*.avif filter=lfs diff=lfs merge=lfs -text +############################################################################### +# Handle ICC files by git lfs +############################################################################### +*.icc filter=lfs diff=lfs merge=lfs -text +############################################################################### +# Handle font files by git lfs +############################################################################### +*.eot filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text +*.ttc filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.woff filter=lfs diff=lfs merge=lfs -text +*.woff2 filter=lfs diff=lfs merge=lfs -text diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..5f9f694351 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,3 @@ +# GitHub Copilot Instructions + +Read and follow [AGENTS.md](../AGENTS.md) as the repository-wide source of coding, performance, and verification requirements. Prefer existing local patterns and repository configuration whenever generated code or suggestions are accepted. diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e17b8d724e..634b904aee 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -11,64 +11,124 @@ on: branches: - main - release/* - types: [ labeled, opened, synchronize, reopened ] + types: [ opened, synchronize, reopened ] + jobs: + # Prime a single LFS cache and expose the exact key for the matrix + WarmLFS: + runs-on: ubuntu-latest + outputs: + lfs_key: ${{ steps.expose-key.outputs.lfs_key }} + steps: + - name: Git Config + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + + - name: Git Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + submodules: recursive + + # Deterministic list of LFS object IDs, then compute a portable key: + # - `git lfs ls-files -l` lists all tracked LFS objects with their SHA-256 + # - `awk '{print $1}'` extracts just the SHA field + # - `sort` sorts in byte order (hex hashes sort the same everywhere) + # This ensures the file content is identical regardless of OS or locale + - name: Git Create LFS id list + shell: bash + run: git lfs ls-files -l | awk '{print $1}' | sort > .lfs-assets-id + + - name: Git Expose LFS cache key + id: expose-key + shell: bash + env: + LFS_KEY: lfs-${{ hashFiles('.lfs-assets-id') }}-v1 + run: echo "lfs_key=$LFS_KEY" >> "$GITHUB_OUTPUT" + + - name: Git Setup LFS Cache + uses: actions/cache@v6 + with: + path: .git/lfs + key: ${{ steps.expose-key.outputs.lfs_key }} + + - name: Git Pull LFS + shell: bash + run: git lfs pull + Build: + needs: WarmLFS strategy: matrix: - isARM: - - ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }} options: - os: ubuntu-latest - framework: net7.0 - sdk: 7.0.x + framework: net10.0 + sdk: 10.0.x sdk-preview: true runtime: -x64 codecov: false - - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable - framework: net7.0 - sdk: 7.0.x + - os: macos-26 + framework: net10.0 + sdk: 10.0.x sdk-preview: true runtime: -x64 codecov: false - os: windows-latest - framework: net7.0 - sdk: 7.0.x + framework: net10.0 + sdk: 10.0.x sdk-preview: true runtime: -x64 codecov: false - - os: buildjet-4vcpu-ubuntu-2204-arm - framework: net7.0 - sdk: 7.0.x + - os: ubuntu-22.04-arm + framework: net10.0 + sdk: 10.0.x sdk-preview: true runtime: -x64 codecov: false + - os: ubuntu-latest - framework: net6.0 - sdk: 6.0.x + framework: net8.0 + sdk: 8.0.x runtime: -x64 codecov: false - - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable - framework: net6.0 - sdk: 6.0.x + - os: macos-26 + framework: net8.0 + sdk: 8.0.x runtime: -x64 codecov: false - os: windows-latest - framework: net6.0 - sdk: 6.0.x + framework: net8.0 + sdk: 8.0.x + runtime: -x64 + codecov: false + - os: ubuntu-22.04-arm + framework: net8.0 + sdk: 8.0.x runtime: -x64 codecov: false - exclude: - - isARM: false - options: - os: buildjet-4vcpu-ubuntu-2204-arm - runs-on: ${{matrix.options.os}} + runs-on: ${{ matrix.options.os }} steps: - name: Install libgdi+, which is required for tests running on ubuntu - if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }} - run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev + if: ${{ contains(matrix.options.os, 'ubuntu') }} + run: | + sudo apt-get update + sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev + + - name: Install libgdi+, which is required for tests running on macos + if: ${{ contains(matrix.options.os, 'macos-26') }} + run: | + brew update + brew install mono-libgdiplus + # Create symlinks to make libgdiplus discoverable + sudo mkdir -p /usr/local/lib + sudo ln -sf $(brew --prefix)/lib/libgdiplus.dylib /usr/local/lib/libgdiplus.dylib + # Verify installation + ls -la $(brew --prefix)/lib/libgdiplus* || echo "libgdiplus not found in brew prefix" + ls -la /usr/local/lib/libgdiplus* || echo "libgdiplus not found in /usr/local/lib" - name: Git Config shell: bash @@ -77,30 +137,27 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 submodules: recursive - # See https://github.com/actions/checkout/issues/165#issuecomment-657673315 - - name: Git Create LFS FileList - run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - + # Use the warmed key from WarmLFS. Do not recompute or recreate .lfs-assets-id here. - name: Git Setup LFS Cache - uses: actions/cache@v3 - id: lfs-cache + uses: actions/cache@v6 with: path: .git/lfs - key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 + key: ${{ needs.WarmLFS.outputs.lfs_key }} - name: Git Pull LFS + shell: bash run: git lfs pull - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v4 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v6 id: nuget-cache with: path: ~/.nuget @@ -109,17 +166,17 @@ jobs: - name: DotNet Setup if: ${{ matrix.options.sdk-preview != true }} - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v6 with: dotnet-version: | - 6.0.x + 8.0.x - name: DotNet Setup Preview if: ${{ matrix.options.sdk-preview == true }} - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v6 with: dotnet-version: | - 7.0.x + 10.0.x - name: DotNet Build if: ${{ matrix.options.sdk-preview != true }} @@ -152,7 +209,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip @@ -160,11 +217,8 @@ jobs: Publish: needs: [Build] - runs-on: ubuntu-latest - if: (github.event_name == 'push') - steps: - name: Git Config shell: bash @@ -173,16 +227,16 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 submodules: recursive - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v4 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v6 id: nuget-cache with: path: ~/.nuget @@ -205,4 +259,3 @@ jobs: run: | dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate - diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index e551afbd6d..00f489b403 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -4,19 +4,26 @@ on: schedule: # 2AM every Tuesday/Thursday - cron: "0 2 * * 2,4" + jobs: Build: strategy: matrix: options: - os: ubuntu-latest - framework: net6.0 + framework: net8.0 runtime: -x64 codecov: true - runs-on: ${{matrix.options.os}} + runs-on: ${{ matrix.options.os }} steps: + - name: Install libgdi+, which is required for tests running on ubuntu + if: ${{ contains(matrix.options.os, 'ubuntu') }} + run: | + sudo apt-get update + sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev + - name: Git Config shell: bash run: | @@ -24,30 +31,35 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 submodules: recursive - # See https://github.com/actions/checkout/issues/165#issuecomment-657673315 - - name: Git Create LFS FileList - run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id + # Deterministic list of LFS object IDs, then compute a portable key: + # - `git lfs ls-files -l` lists all tracked LFS objects with their SHA-256 + # - `awk '{print $1}'` extracts just the SHA field + # - `sort` sorts in byte order (hex hashes sort the same everywhere) + # This ensures the file content is identical regardless of OS or locale + - name: Git Create LFS id list + shell: bash + run: git lfs ls-files -l | awk '{print $1}' | sort > .lfs-assets-id - name: Git Setup LFS Cache - uses: actions/cache@v3 + uses: actions/cache@v6 id: lfs-cache with: path: .git/lfs - key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 + key: lfs-${{ hashFiles('.lfs-assets-id') }}-v1 - name: Git Pull LFS run: git lfs pull - name: NuGet Install - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v4 - name: NuGet Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v6 id: nuget-cache with: path: ~/.nuget @@ -55,33 +67,34 @@ jobs: restore-keys: ${{ runner.os }}-nuget- - name: DotNet Setup - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v6 with: dotnet-version: | - 6.0.x + 8.0.x - name: DotNet Build shell: pwsh - run: ./ci-build.ps1 "${{matrix.options.framework}}" + run: ./ci-build.ps1 "${{ matrix.options.framework }}" env: SIXLABORS_TESTING: True - name: DotNet Test shell: pwsh - run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" + run: ./ci-test.ps1 "${{ matrix.options.os }}" "${{ matrix.options.framework }}" "${{ matrix.options.runtime }}" "${{ matrix.options.codecov }}" env: SIXLABORS_TESTING: True XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip path: tests/Images/ActualOutput/ - name: Codecov Update - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v7 if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') with: flags: unittests + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index fadf36964c..a8d2917be7 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,5 @@ artifacts/ #lfs hooks/** lfs/** + +.dotnet diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..814e3ce263 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,41 @@ +# Six Labors AI Coding Guidelines + +These instructions apply to the entire repository. More-specific `AGENTS.md` files may add to or override them for their directory tree. + +## Working Practices + +- Inspect the relevant implementation, tests, benchmarks, project files, and nearby code before proposing or making changes. Do not infer current behavior when the source is available. +- Make the smallest complete change that solves the requested problem. Avoid unrelated cleanup, speculative abstractions, and formatting churn. +- Match established architecture, naming, formatting, documentation, and test patterns. Treat `.editorconfig`, analyzers, and repository build settings as authoritative. +- Preserve public API and observable behavior unless the task explicitly requires a change. Public API documentation must describe observable behavior, not implementation details. +- Do not use reflection against built assemblies, ad hoc assembly loading, or temporary probe projects unless explicitly requested. +- Build .NET projects in Release configuration unless explicitly instructed otherwise. + +## Performance + +- Treat throughput, latency, memory use, and binary size as design constraints, especially in pixel-processing, drawing, parsing, encoding, and other hot paths. +- Avoid unnecessary allocations, copies, boxing, closures, interface dispatch, repeated enumeration, and extra passes over data. +- Reuse the repository's existing memory ownership, pooling, span, vectorization, and parallelization patterns. Do not introduce a new mechanism when an established one fits. +- Keep hot loops simple and bounds-check-friendly. Hoist invariant work, preserve locality, and use the narrowest suitable data types without sacrificing correctness. +- Do not trade correctness or maintainability for assumed speed. Support non-obvious optimizations with measurements or clear evidence, and add or update benchmarks when performance is the purpose of the change. +- Consider all supported target frameworks and runtime capabilities. Do not regress fallback paths while optimizing newer runtimes. + +## C# Conventions + +- Follow the existing code around the change; local patterns take precedence over generic preferences. +- Do not use `record` or `record struct` types. +- Prefer established invariants over redundant guards. Validate at real external boundaries and do not add defensive checks for internally controlled states. +- Do not extract single-use helpers merely to name a block. Extract only for genuine reuse, an established local pattern, or meaningful complexity reduction. +- Add vertical whitespace after multi-line statements and declarations and between distinct logical stages. Never add trailing whitespace. +- Document every method, constructor, and property, regardless of whether it is public, internal, protected, or private. Keep public API documentation limited to observable behavior; use private and internal documentation to capture the contract and intent needed to maintain the code. +- Add inline comments throughout complex code. Explain algorithms, formulas, invariants, ownership, compatibility behavior, and performance tradeoffs at the operations and decisions they govern. Explain why the code is shaped that way rather than narrating the syntax. +- Document SIMD code especially thoroughly. Explain the vector layout, lane meaning, widening or narrowing, masks, shuffles, constants, alignment or remainder handling, supported instruction paths, scalar equivalence, and the reason each non-obvious operation is correct. +- Write algorithm and SIMD comments for a maintainer who is unfamiliar with the implementation. The reader should not need to reconstruct intent from external documentation, issue history, or benchmark results. + +## Verification + +- Add or update focused tests when behavior changes, following the test framework and conventions already used by the project. +- Never hack, weaken, skip, conditionally bypass, or otherwise manipulate a test to make it pass. Fix the production defect or the genuine test defect while preserving the test's intended coverage and sensitivity. +- Do not update golden files, reference images, snapshots, baselines, or expected-output artifacts to resolve a test failure. Treat a mismatch as evidence to investigate and correct the implementation. +- Run the narrowest relevant formatting, test, and Release build commands, then expand verification in proportion to the risk and scope of the change. +- Report what changed, the verification performed, and any remaining risks or unverified assumptions. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..5f08a449a6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude Code Instructions + +Read and follow [AGENTS.md](AGENTS.md) as the repository-wide source of coding, performance, and verification requirements. Apply any more-specific `AGENTS.md` or `CLAUDE.md` found below the files being changed. diff --git a/Directory.Build.props b/Directory.Build.props index 26b3cc5afc..8219d5a293 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -21,9 +21,21 @@ - - - preview + + + + + + + 12.0 + + + + 14.0 @@ -22,21 +23,19 @@ - - 3.1 + + 4.0 - net7.0;net6.0 - true + net8.0;net10.0 - net6.0 - true + net8.0 @@ -48,15 +47,24 @@ - + + + + + + True + True + InlineArray.tt + + True True - Block8x8F.Generated.tt + ImageExtensions.Save.tt - + True True - Block8x8F.Generated.tt + ImageMetadataExtensions.tt True @@ -133,26 +141,31 @@ True DefaultPixelBlenders.Generated.tt - + True True - PorterDuffFunctions.Generated.tt + AssociatedAlphaPixelBlenders.Generated.tt - + + True True + AssociatedAlphaPorterDuffFunctions.Generated.tt + + True - ImageExtensions.Save.tt + True + PorterDuffFunctions.Generated.tt - + TextTemplatingFileGenerator - Block8x8F.Generated.cs + InlineArray.cs - + + ImageMetadataExtensions.cs TextTemplatingFileGenerator - Block8x8F.Generated.cs TextTemplatingFileGenerator @@ -218,7 +231,15 @@ DefaultPixelBlenders.Generated.cs TextTemplatingFileGenerator - + + AssociatedAlphaPixelBlenders.Generated.cs + TextTemplatingFileGenerator + + + AssociatedAlphaPorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + TextTemplatingFileGenerator ImageExtensions.Save.cs diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index c24014e698..4881518c9c 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -2,9 +2,7 @@ // Licensed under the Six Labors Split License. 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; @@ -78,7 +76,7 @@ public Image(int width, int height) /// 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 ImageMetadata(), width, height) => this.frames = new ImageFrameCollection(this, width, height, default(TPixel)); /// @@ -92,7 +90,7 @@ internal Image( Configuration configuration, Buffer2D pixelBuffer, ImageMetadata metadata) - : this(configuration, pixelBuffer.FastMemoryGroup, pixelBuffer.Width, pixelBuffer.Height, metadata) + : this(configuration, pixelBuffer.FastMemoryGroup, pixelBuffer.Width, pixelBuffer.Height, pixelBuffer.RowStride, metadata) { } @@ -111,8 +109,29 @@ internal Image( int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata, width, height) - => this.frames = new ImageFrameCollection(this, width, height, memoryGroup); + : this(configuration, memoryGroup, width, height, width, metadata) + { + } + + /// + /// Initializes a new instance of the class + /// wrapping an external . + /// + /// The configuration providing initialization code which allows extending the library. + /// The memory source. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The number of elements between row starts. + /// The images metadata. + internal Image( + Configuration configuration, + MemoryGroup memoryGroup, + int width, + int height, + int rowStride, + ImageMetadata metadata) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata, width, height) + => this.frames = new ImageFrameCollection(this, width, height, rowStride, memoryGroup); /// /// Initializes a new instance of the class @@ -129,7 +148,7 @@ internal Image( int height, TPixel backgroundColor, ImageMetadata? metadata) - : base(configuration, PixelTypeInfo.Create(), metadata ?? new(), width, height) + : base(configuration, TPixel.GetPixelTypeInfo(), metadata ?? new ImageMetadata(), width, height) => this.frames = new ImageFrameCollection(this, width, height, backgroundColor); /// @@ -140,7 +159,7 @@ internal 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); /// @@ -161,7 +180,7 @@ internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable< /// /// Gets the root frame. /// - private IPixelSource PixelSourceUnsafe => this.frames.RootFrameUnsafe; + private ImageFrame PixelSourceUnsafe => this.frames.RootFrameUnsafe; /// /// Gets or sets the pixel at the specified position. @@ -288,16 +307,24 @@ public void ProcessPixelRows( } /// - /// Copy image pixels to . + /// Copy image pixels to using the root frame backing row layout. /// + /// + /// Destination length must be at least + /// ((Height - 1) * Frames.RootFrame.PixelBuffer.RowStride) + Width. + /// /// The to copy image pixels to. - public void CopyPixelDataTo(Span destination) => this.GetPixelMemoryGroup().CopyTo(destination); + public void CopyPixelDataTo(Span destination) => this.Frames.RootFrame.CopyPixelDataTo(destination); /// - /// Copy image pixels to . + /// Copy image pixels to using the root frame backing row layout. /// + /// + /// Destination length must be at least + /// (((Height - 1) * Frames.RootFrame.PixelBuffer.RowStride) + Width) * sizeof(TPixel) bytes. + /// /// The of to copy image pixels to. - public void CopyPixelDataTo(Span destination) => this.GetPixelMemoryGroup().CopyTo(MemoryMarshal.Cast(destination)); + public void CopyPixelDataTo(Span destination) => this.Frames.RootFrame.CopyPixelDataTo(destination); /// /// Gets the representation of the pixels as a in the source image's pixel format @@ -312,20 +339,10 @@ public void ProcessPixelRows( /// The referencing the image buffer. /// The indicating the success. public bool DangerousTryGetSinglePixelMemory(out Memory memory) - { - IMemoryGroup mg = this.GetPixelMemoryGroup(); - if (mg.Count > 1) - { - memory = default; - return false; - } - - memory = mg.Single(); - return true; - } + => this.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out memory); /// - /// Clones the current image + /// Clones the current image. /// /// Returns a new image with all the same metadata as the original. public Image Clone() => this.Clone(this.Configuration); @@ -396,22 +413,42 @@ internal override Task AcceptAsync(IImageVisitorAsync visitor, CancellationToken } /// - /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. + /// Switches the buffers used by the image and the pixel source meaning that the Image will + /// "own" the buffer from the pixelSource and the pixel source will now own the Image buffer. /// - /// The pixel source. - internal void SwapOrCopyPixelsBuffersFrom(Image pixelSource) + /// The pixel source. + internal void SwapOrCopyPixelsBuffersFrom(Image source) { - Guard.NotNull(pixelSource, nameof(pixelSource)); + Guard.NotNull(source, nameof(source)); this.EnsureNotDisposed(); - ImageFrameCollection sourceFrames = pixelSource.Frames; + ImageFrameCollection sourceFrames = source.Frames; for (int i = 0; i < this.frames.Count; i++) { this.frames[i].SwapOrCopyPixelsBufferFrom(sourceFrames[i]); } - this.UpdateSize(pixelSource.Size); + this.UpdateSize(source.Size); + } + + /// + /// Copies the metadata from the source image. + /// + /// The metadata source. + internal void CopyMetadataFrom(Image source) + { + Guard.NotNull(source, nameof(source)); + + this.EnsureNotDisposed(); + + ImageFrameCollection sourceFrames = source.Frames; + for (int i = 0; i < this.frames.Count; i++) + { + this.frames[i].CopyMetadataFrom(sourceFrames[i]); + } + + this.UpdateMetadata(source.Metadata); } private static Size ValidateFramesAndGetSize(IEnumerable> frames) @@ -420,9 +457,9 @@ private static Size ValidateFramesAndGetSize(IEnumerable> fra ImageFrame? rootFrame = frames.FirstOrDefault() ?? throw new ArgumentException("Must not be empty.", nameof(frames)); - Size rootSize = rootFrame.Size(); + Size rootSize = rootFrame.Size; - if (frames.Any(f => f.Size() != rootSize)) + if (frames.Any(f => f.Size != rootSize)) { throw new ArgumentException("The provided frames must be of the same size.", nameof(frames)); } diff --git a/src/ImageSharp/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/IndexedImageFrame{TPixel}.cs index 6807e77ad2..a88cdb524e 100644 --- a/src/ImageSharp/IndexedImageFrame{TPixel}.cs +++ b/src/ImageSharp/IndexedImageFrame{TPixel}.cs @@ -25,12 +25,12 @@ public sealed class IndexedImageFrame : IPixelSource, IDisposable /// Initializes a new instance of the class. /// /// - /// The configuration which allows altering default behaviour or extending the library. + /// The configuration which allows altering default behavior or extending the library. /// /// The frame width. /// The frame height. /// The color palette. - internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory palette) + public IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory palette) { Guard.NotNull(configuration, nameof(configuration)); Guard.MustBeLessThanOrEqualTo(palette.Length, QuantizerConstants.MaxColors, nameof(palette)); @@ -42,14 +42,14 @@ internal IndexedImageFrame(Configuration configuration, int width, int height, R this.Height = height; this.pixelBuffer = configuration.MemoryAllocator.Allocate2D(width, height); - // Copy the palette over. We want the lifetime of this frame to be independant of any palette source. + // Copy the palette over. We want the lifetime of this frame to be independent of any palette source. this.paletteOwner = configuration.MemoryAllocator.Allocate(palette.Length); palette.Span.CopyTo(this.paletteOwner.GetSpan()); this.Palette = this.paletteOwner.Memory[..palette.Length]; } /// - /// Gets the configuration which allows altering default behaviour or extending the library. + /// Gets the configuration which allows altering default behavior or extending the library. /// public Configuration Configuration { get; } diff --git a/src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs b/src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs new file mode 100644 index 0000000000..764bb37e82 --- /dev/null +++ b/src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; + +namespace SixLabors.ImageSharp.Memory; + +/// +/// Provides the tracked memory-owner contract required by . +/// +/// The element type. +/// +/// Custom allocators implement +/// and return a derived type. The base allocator attaches allocation tracking after the owner has been +/// created so custom implementations cannot forget, duplicate, or mismatch the reservation lifecycle. +/// +public abstract class AllocationTrackedMemoryManager : MemoryManager + where T : struct +{ + private AllocationTrackingState allocationTracking; + + /// + /// Releases resources held by the concrete tracked owner. + /// + /// + /// when the owner is being disposed deterministically; + /// otherwise, . + /// + /// + /// Implementations release their own resources here. Allocation tracking is released by the sealed base + /// dispose path after this method returns. + /// + protected abstract void DisposeCore(bool disposing); + + /// + protected sealed override void Dispose(bool disposing) + { + try + { + this.DisposeCore(disposing); + } + finally + { + this.ReleaseAllocationTracking(); + } + } + + /// + /// Attaches allocation tracking to this owner after allocation has succeeded. + /// + /// The allocator that owns the reservation for this instance. + /// The reserved allocation size, in bytes. + /// + /// calls this exactly once after AllocateCore returns. + /// Derived allocators should not call it themselves; they only construct the concrete owner. + /// + protected internal virtual void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) + => this.allocationTracking.Attach(allocator, lengthInBytes); + + /// + /// Releases any tracked allocation bytes associated with this instance. + /// + /// + /// Calling this more than once is safe; only the first call after tracking has been attached releases bytes. + /// + private void ReleaseAllocationTracking() => this.allocationTracking.Release(); +} diff --git a/src/ImageSharp/Memory/AllocationTrackingState.cs b/src/ImageSharp/Memory/AllocationTrackingState.cs new file mode 100644 index 0000000000..1e9a632ed8 --- /dev/null +++ b/src/ImageSharp/Memory/AllocationTrackingState.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Memory; + +/// +/// Tracks a single allocator reservation and releases it exactly once. +/// +/// +/// This type is intended to live as a mutable field on the owning object. It should not be copied +/// after tracking has been attached, because the owner relies on a single shared release state. +/// +internal struct AllocationTrackingState +{ + private MemoryAllocator? allocator; + private long lengthInBytes; + private int released; + + /// + /// Attaches allocator reservation tracking to the current owner. + /// + /// The allocator that owns the reservation. + /// The reserved allocation size, in bytes. + /// + /// Must complete-before the owning object's reference is observable to any other thread. + /// guarantees this by attaching synchronously on the allocating + /// thread before returning the owner; reference publication then provides the release fence + /// that makes these field writes visible to a subsequent on another thread. + /// + internal void Attach(MemoryAllocator allocator, long lengthInBytes) + { + this.allocator = allocator; + this.lengthInBytes = lengthInBytes; + } + + /// + /// Releases the attached allocator reservation once. + /// + internal void Release() + { + if (Interlocked.Exchange(ref this.released, 1) == 0 && this.allocator != null) + { + this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes); + this.allocator = null; + } + } +} diff --git a/src/ImageSharp/Memory/Allocators/AllocationOptionsExtensions.cs b/src/ImageSharp/Memory/Allocators/AllocationOptionsExtensions.cs index 3ead1c5df7..986ed7f7cf 100644 --- a/src/ImageSharp/Memory/Allocators/AllocationOptionsExtensions.cs +++ b/src/ImageSharp/Memory/Allocators/AllocationOptionsExtensions.cs @@ -1,9 +1,19 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Memory; +/// +/// Provides helper methods for working with . +/// internal static class AllocationOptionsExtensions { - public static bool Has(this AllocationOptions options, AllocationOptions flag) => (options & flag) == flag; + /// + /// Returns a value indicating whether the specified flag is set on the allocation options. + /// + /// The allocation options to inspect. + /// The flag to test for. + /// if is set; otherwise, . + public static bool Has(this AllocationOptions options, AllocationOptions flag) + => (options & flag) == flag; } diff --git a/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs b/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs index 9f34602fb1..c22e827a8a 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs @@ -47,7 +47,7 @@ public BasicArrayBuffer(T[] array) public override Span GetSpan() => this.Array.AsSpan(0, this.Length); /// - protected override void Dispose(bool disposing) + protected override void DisposeCore(bool disposing) { } diff --git a/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs index a6ed797d6d..84dd065f54 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Memory.Internals; /// Provides a base class for implementations by implementing pinning logic for adaption. /// /// The element type. -internal abstract class ManagedBufferBase : MemoryManager +internal abstract class ManagedBufferBase : AllocationTrackedMemoryManager where T : struct { private GCHandle pinHandle; diff --git a/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs index 4a202a96c3..b0fe0b6496 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/RefCountedMemoryLifetimeGuard.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Memory.Internals; /// internal abstract class RefCountedMemoryLifetimeGuard : IDisposable { + private AllocationTrackingState allocationTracking; private int refCount = 1; private int disposed; private int released; @@ -38,6 +39,14 @@ protected RefCountedMemoryLifetimeGuard() public void ReleaseRef() => this.ReleaseRef(false); + /// + /// Attaches allocator reservation tracking to this lifetime guard. + /// + /// The allocator that owns the reservation. + /// The reserved allocation size, in bytes. + public void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) + => this.allocationTracking.Attach(allocator, lengthInBytes); + public void Dispose() { int wasDisposed = Interlocked.Exchange(ref this.disposed, 1); @@ -69,6 +78,10 @@ private void ReleaseRef(bool finalizing) } this.Release(); + + // Guard-backed resources can be recovered by finalization, so their allocator + // reservation must follow the guard's actual release point instead of the owner object. + this.allocationTracking.Release(); } } } diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs index f9434ee941..97fab1b680 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs @@ -13,7 +13,7 @@ internal class SharedArrayPoolBuffer : ManagedBufferBase, IRefCounted where T : struct { private readonly int lengthInBytes; - private LifetimeGuard lifetimeGuard; + private readonly LifetimeGuard lifetimeGuard; public SharedArrayPoolBuffer(int lengthInElements) { @@ -24,7 +24,10 @@ public SharedArrayPoolBuffer(int lengthInElements) public byte[]? Array { get; private set; } - protected override void Dispose(bool disposing) + protected internal override void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) + => this.lifetimeGuard.AttachAllocationTracking(allocator, lengthInBytes); + + protected override void DisposeCore(bool disposing) { if (this.Array == null) { @@ -57,13 +60,7 @@ public void AddRef() [Conditional("DEBUG")] [MemberNotNull(nameof(Array))] - private void CheckDisposed() - { - if (this.Array == null) - { - throw new ObjectDisposedException("SharedArrayPoolBuffer"); - } - } + private void CheckDisposed() => ObjectDisposedException.ThrowIf(this.Array == null, this.Array); private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard { diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs index 24bf52b1f9..e3b73204de 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.LifetimeGuards.cs @@ -11,7 +11,7 @@ public UnmanagedBuffer CreateGuardedBuffer( bool clear) where T : struct { - var buffer = new UnmanagedBuffer(lengthInElements, new ReturnToPoolBufferLifetimeGuard(this, handle)); + UnmanagedBuffer buffer = new(lengthInElements, new ReturnToPoolBufferLifetimeGuard(this, handle)); if (clear) { buffer.Clear(); diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs index aa8bcd3859..d4ad22060d 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Memory.Internals; internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedExecution.CriticalFinalizerObject { private static int minTrimPeriodMilliseconds = int.MaxValue; - private static readonly List> AllPools = new(); + private static readonly List> AllPools = []; private static Timer? trimTimer; private static readonly Stopwatch Stopwatch = Stopwatch.StartNew(); @@ -337,6 +337,6 @@ public class TrimSettings public bool Enabled => this.Rate > 0; - public static TrimSettings Default => new TrimSettings(); + public static TrimSettings Default => new(); } } diff --git a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs index de09647261..3a729cdf2b 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Memory.Internals; /// access to unmanaged buffers allocated by . /// /// The element type. -internal sealed unsafe class UnmanagedBuffer : MemoryManager, IRefCounted +internal sealed unsafe class UnmanagedBuffer : AllocationTrackedMemoryManager, IRefCounted where T : struct { private readonly int lengthInElements; @@ -31,11 +31,14 @@ public UnmanagedBuffer(int lengthInElements, UnmanagedBufferLifetimeGuard lifeti public void* Pointer => this.lifetimeGuard.Handle.Pointer; + protected internal override void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) + => this.lifetimeGuard.AttachAllocationTracking(allocator, lengthInBytes); + public override Span GetSpan() { DebugGuard.NotDisposed(this.disposed == 1, this.GetType().Name); DebugGuard.NotDisposed(this.lifetimeGuard.IsDisposed, this.lifetimeGuard.GetType().Name); - return new(this.Pointer, this.lengthInElements); + return new Span(this.Pointer, this.lengthInElements); } /// @@ -52,7 +55,7 @@ public override MemoryHandle Pin(int elementIndex = 0) } /// - protected override void Dispose(bool disposing) + protected override void DisposeCore(bool disposing) { DebugGuard.IsTrue(disposing, nameof(disposing), "Unmanaged buffers should not have finalizer!"); diff --git a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs index 6b31cadf4f..632e1bec04 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs @@ -39,13 +39,13 @@ private UnmanagedMemoryHandle(IntPtr handle, int lengthInBytes) Interlocked.Increment(ref totalOutstandingHandles); } - public IntPtr Handle => this.handle; + public readonly IntPtr Handle => this.handle; - public bool IsInvalid => this.Handle == IntPtr.Zero; + public readonly bool IsInvalid => this.Handle == IntPtr.Zero; - public bool IsValid => this.Handle != IntPtr.Zero; + public readonly bool IsValid => this.Handle != IntPtr.Zero; - public unsafe void* Pointer => (void*)this.Handle; + public readonly unsafe void* Pointer => (void*)this.Handle; /// /// Gets the total outstanding handle allocations for testing purposes. @@ -121,9 +121,9 @@ public void Free() this.lengthInBytes = 0; } - public bool Equals(UnmanagedMemoryHandle other) => this.handle.Equals(other.handle); + public readonly bool Equals(UnmanagedMemoryHandle other) => this.handle.Equals(other.handle); - public override bool Equals(object? obj) => obj is UnmanagedMemoryHandle other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is UnmanagedMemoryHandle other && this.Equals(other); - public override int GetHashCode() => this.handle.GetHashCode(); + public override readonly int GetHashCode() => this.handle.GetHashCode(); } diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs index 8eaf0b6d69..591c3b9dd4 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -12,6 +12,10 @@ namespace SixLabors.ImageSharp.Memory; public abstract class MemoryAllocator { private const int OneGigabyte = 1 << 30; + private long accumulativeAllocatedBytes; + private long memoryGroupAllocationLimitBytes = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte; + private long accumulativeAllocationLimitBytes = long.MaxValue; + private int singleBufferAllocationLimitBytes = OneGigabyte; /// /// Gets the default platform-specific global instance that @@ -23,9 +27,68 @@ public abstract class MemoryAllocator /// public static MemoryAllocator Default { get; } = Create(); - internal long MemoryGroupAllocationLimitBytes { get; private set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte; + /// + /// Gets or sets the maximum number of bytes that can be allocated by a memory group. + /// A memory group backs the pixel buffer of a single image, so this limits the total image size. + /// + /// + /// The default limit is determined by the process architecture: 4 GB for 64-bit processes and + /// 1 GB for 32-bit processes. The setter is available to derived allocators and requires a positive value. + /// + /// The value is not greater than zero. + public long MemoryGroupAllocationLimitBytes + { + get => this.memoryGroupAllocationLimitBytes; + protected set + { + Guard.MustBeGreaterThan(value, 0, nameof(this.MemoryGroupAllocationLimitBytes)); + this.memoryGroupAllocationLimitBytes = value; + } + } - internal int SingleBufferAllocationLimitBytes { get; private set; } = OneGigabyte; + /// + /// Gets or sets the maximum accumulative size, in bytes, of all active allocations made through this allocator instance. + /// + /// + /// Defaults to , effectively imposing no limit on the accumulative total. + /// When set, this provides a safeguard against excessive memory consumption by capping the combined size of + /// outstanding allocations issued by this instance.
+ /// When the accumulative size of active allocations exceeds this limit, an will be thrown to + /// prevent further allocations and signal that the limit has been breached. + /// The setter is available to derived allocators and requires a positive value. + ///
+ /// The value is not greater than zero. + public long AccumulativeAllocationLimitBytes + { + get => this.accumulativeAllocationLimitBytes; + protected set + { + Guard.MustBeGreaterThan(value, 0, nameof(this.AccumulativeAllocationLimitBytes)); + this.accumulativeAllocationLimitBytes = value; + } + } + + /// + /// Gets or sets the maximum size, in bytes, that can be allocated for a single contiguous buffer. + /// This limit applies to and to contiguous image buffers + /// requested through . + /// + /// + /// The single buffer allocation limit is set to 1 GB by default. + /// A single contiguous buffer can never exceed bytes; larger images are + /// backed by discontiguous memory groups limited by . + /// The setter is available to derived allocators and requires a positive value. + /// + /// The value is not greater than zero. + public int SingleBufferAllocationLimitBytes + { + get => this.singleBufferAllocationLimitBytes; + protected set + { + Guard.MustBeGreaterThan(value, 0, nameof(this.SingleBufferAllocationLimitBytes)); + this.singleBufferAllocationLimitBytes = value; + } + } /// /// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes. @@ -47,13 +110,38 @@ public abstract class MemoryAllocator public static MemoryAllocator Create(MemoryAllocatorOptions options) { UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(options.MaximumPoolSizeMegabytes); + allocator.ApplyOptions(options); + return allocator; + } + + /// + /// Applies the supplied to this instance. + /// Derived allocators can call this from their constructors to accept user configuration. + /// + /// The options to apply. Properties left as are ignored. + /// + /// The applied single buffer limit is capped to , + /// because a single contiguous buffer can never be larger than the total allocation limit. + /// + protected void ApplyOptions(MemoryAllocatorOptions options) + { if (options.AllocationLimitMegabytes.HasValue) { - allocator.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L; - allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes); + this.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L; } - return allocator; + if (options.SingleBufferAllocationLimitMegabytes.HasValue) + { + // The option setter caps the value at 2047 MB, so converting to bytes cannot overflow. + this.SingleBufferAllocationLimitBytes = (int)(options.SingleBufferAllocationLimitMegabytes.Value * 1024L * 1024L); + } + + this.SingleBufferAllocationLimitBytes = (int)Math.Min(this.SingleBufferAllocationLimitBytes, this.MemoryGroupAllocationLimitBytes); + + if (options.AccumulativeAllocationLimitMegabytes.HasValue) + { + this.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L; + } } /// @@ -63,15 +151,60 @@ public static MemoryAllocator Create(MemoryAllocatorOptions options) /// Size of the buffer to allocate. /// The allocation options. /// A buffer of values of type . - /// When length is zero or negative. - /// When length is over the capacity of the allocator. - public abstract IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + /// When length is negative or over the capacity of the allocator. + public IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + where T : struct + { + long lengthInBytes = this.GetValidatedAllocationLengthInBytes(length); + bool shouldTrack = this.AccumulativeAllocationLimitBytes != long.MaxValue && lengthInBytes != 0; + if (shouldTrack) + { + this.ReserveAllocation(lengthInBytes); + } + + try + { + AllocationTrackedMemoryManager owner = this.AllocateCore(length, options); + if (shouldTrack) + { + owner.AttachAllocationTracking(this, lengthInBytes); + } + + return owner; + } + catch + { + if (shouldTrack) + { + this.ReleaseAccumulatedBytes(lengthInBytes); + } + + throw; + } + } + + /// + /// Allocates a tracked memory owner for . + /// + /// Type of the data stored in the buffer. + /// Size of the buffer to allocate. + /// The allocation options. + /// A tracked memory owner of values of type . + /// + /// Implementations should only allocate and initialize the concrete owner. The base allocator + /// reserves bytes, attaches tracking to the returned owner, and releases the reservation if allocation fails. + /// + protected abstract AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) where T : struct; /// /// Releases all retained resources not being in use. /// Eg: by resetting array pools and letting GC to free the arrays. /// + /// + /// This does not dispose active allocations; callers are responsible for disposing all + /// instances to release memory. + /// public virtual void ReleaseRetainedResources() { } @@ -102,11 +235,109 @@ internal MemoryGroup AllocateGroup( InvalidMemoryOperationException.ThrowAllocationOverLimitException(totalLengthInBytes, this.MemoryGroupAllocationLimitBytes); } - // Cast to long is safe because we already checked that the total length is within the limit. - return this.AllocateGroupCore(totalLength, (long)totalLengthInBytes, bufferAlignment, options); + long totalLengthInBytesLong = (long)totalLengthInBytes; + bool shouldTrack = this.AccumulativeAllocationLimitBytes != long.MaxValue && totalLengthInBytesLong != 0; + if (shouldTrack) + { + this.ReserveAllocation(totalLengthInBytesLong); + } + + try + { + MemoryGroup group = this.AllocateGroupCore(totalLength, totalLengthInBytesLong, bufferAlignment, options); + if (shouldTrack) + { + group.AttachAllocationTracking(this, totalLengthInBytesLong); + } + + return group; + } + catch + { + if (shouldTrack) + { + this.ReleaseAccumulatedBytes(totalLengthInBytesLong); + } + + throw; + } } internal virtual MemoryGroup AllocateGroupCore(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options) where T : struct => MemoryGroup.Allocate(this, totalLengthInElements, bufferAlignment, options); + + /// + /// Allocates a single segment for construction. + /// + /// Type of the data stored in the buffer. + /// Size of the segment to allocate. + /// The allocation options. + /// A segment owner for the requested buffer length. + /// + /// The default implementation validates the segment size then calls + /// directly so group construction can reserve and release the total allocation once. + /// + internal virtual IMemoryOwner AllocateGroupBuffer(int length, AllocationOptions options = AllocationOptions.None) + where T : struct + { + _ = this.GetValidatedAllocationLengthInBytes(length); + return this.AllocateCore(length, options); + } + + /// + /// Returns the validated allocation length in bytes. + /// + /// Type of the data stored in the buffer. + /// Size of the buffer to allocate. + /// The allocation length in bytes. + private long GetValidatedAllocationLengthInBytes(int length) + where T : struct + { + if (length < 0) + { + InvalidMemoryOperationException.ThrowNegativeAllocationException(length); + } + + ulong lengthInBytes = (ulong)length * (ulong)Unsafe.SizeOf(); + if (lengthInBytes > (ulong)this.SingleBufferAllocationLimitBytes) + { + InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes); + } + + return (long)lengthInBytes; + } + + /// + /// Reserves accumulative allocation bytes before creating the underlying buffer. + /// + /// The number of bytes to reserve. + private void ReserveAllocation(long lengthInBytes) + { + if (lengthInBytes <= 0) + { + return; + } + + long total = Interlocked.Add(ref this.accumulativeAllocatedBytes, lengthInBytes); + if (total > this.AccumulativeAllocationLimitBytes) + { + _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes); + InvalidMemoryOperationException.ThrowAccumulativeAllocationOverLimitException(lengthInBytes, total, this.AccumulativeAllocationLimitBytes); + } + } + + /// + /// Releases accumulative allocation bytes previously tracked by this allocator. + /// + /// The number of bytes to release. + internal void ReleaseAccumulatedBytes(long lengthInBytes) + { + if (lengthInBytes <= 0) + { + return; + } + + _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes); + } } diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs index d9ba62c1ef..0578fd64d2 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs @@ -8,8 +8,15 @@ namespace SixLabors.ImageSharp.Memory; /// public struct MemoryAllocatorOptions { + /// + /// The largest single-buffer limit, in Megabytes, that still fits bytes. + /// + private const int MaxSingleBufferAllocationLimitMegabytes = 2047; + private int? maximumPoolSizeMegabytes; private int? allocationLimitMegabytes; + private int? accumulativeAllocationLimitMegabytes; + private int? singleBufferAllocationLimitMegabytes; /// /// Gets or sets a value defining the maximum size of the 's internal memory pool @@ -17,7 +24,7 @@ public struct MemoryAllocatorOptions /// public int? MaximumPoolSizeMegabytes { - get => this.maximumPoolSizeMegabytes; + readonly get => this.maximumPoolSizeMegabytes; set { if (value.HasValue) @@ -35,15 +42,78 @@ public int? MaximumPoolSizeMegabytes /// public int? AllocationLimitMegabytes { - get => this.allocationLimitMegabytes; + readonly get => this.allocationLimitMegabytes; set { if (value.HasValue) { Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AllocationLimitMegabytes)); + if (this.AccumulativeAllocationLimitMegabytes.HasValue) + { + Guard.MustBeLessThanOrEqualTo( + value.Value, + this.AccumulativeAllocationLimitMegabytes.Value, + nameof(this.AllocationLimitMegabytes)); + } } this.allocationLimitMegabytes = value; } } + + /// + /// Gets or sets a value defining the maximum size, in Megabytes, of a single contiguous buffer + /// that the created can allocate. + /// means the default of 1 GB. + /// + /// + /// This limit applies to contiguous buffers, including image buffers requested through + /// . A single contiguous buffer can never exceed + /// bytes, so the largest accepted value is 2047. The applied limit is also + /// capped to because a single buffer can never be larger + /// than the total allocation limit. + /// + public int? SingleBufferAllocationLimitMegabytes + { + readonly get => this.singleBufferAllocationLimitMegabytes; + set + { + if (value.HasValue) + { + Guard.MustBeGreaterThan(value.Value, 0, nameof(this.SingleBufferAllocationLimitMegabytes)); + Guard.MustBeLessThanOrEqualTo( + value.Value, + MaxSingleBufferAllocationLimitMegabytes, + nameof(this.SingleBufferAllocationLimitMegabytes)); + } + + this.singleBufferAllocationLimitMegabytes = value; + } + } + + /// + /// Gets or sets a value defining the maximum accumulative size, in Megabytes, of all active allocations made + /// through the created instance. + /// (the default) imposes no limit on the accumulative total. + /// + public int? AccumulativeAllocationLimitMegabytes + { + readonly get => this.accumulativeAllocationLimitMegabytes; + set + { + if (value.HasValue) + { + Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AccumulativeAllocationLimitMegabytes)); + if (this.AllocationLimitMegabytes.HasValue) + { + Guard.MustBeGreaterThanOrEqualTo( + value.Value, + this.AllocationLimitMegabytes.Value, + nameof(this.AccumulativeAllocationLimitMegabytes)); + } + } + + this.accumulativeAllocationLimitMegabytes = value; + } + } } diff --git a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs index 675afe8b9f..5d183fa442 100644 --- a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory.Internals; namespace SixLabors.ImageSharp.Memory; @@ -12,23 +10,24 @@ namespace SixLabors.ImageSharp.Memory; ///
public sealed class SimpleGcMemoryAllocator : MemoryAllocator { + /// + /// Initializes a new instance of the class with default limits. + /// + public SimpleGcMemoryAllocator() + : this(default) + { + } + + /// + /// Initializes a new instance of the class with custom limits. + /// + /// The to apply. + public SimpleGcMemoryAllocator(MemoryAllocatorOptions options) => this.ApplyOptions(options); + /// protected internal override int GetBufferCapacityInBytes() => int.MaxValue; /// - public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) - { - if (length < 0) - { - InvalidMemoryOperationException.ThrowNegativeAllocationException(length); - } - - ulong lengthInBytes = (ulong)length * (ulong)Unsafe.SizeOf(); - if (lengthInBytes > (ulong)this.SingleBufferAllocationLimitBytes) - { - InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes); - } - - return new BasicArrayBuffer(new T[length]); - } + protected override AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) + => new BasicArrayBuffer(new T[length]); } diff --git a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs index 621073a3db..cfffc679a2 100644 --- a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory.Internals; @@ -71,32 +70,27 @@ internal UniformUnmanagedMemoryPoolMemoryAllocator( this.nonPoolAllocator = new UnmanagedMemoryAllocator(unmanagedBufferSizeInBytes); } - // This delegate allows overriding the method returning the available system memory, - // so we can test our workaround for https://github.com/dotnet/runtime/issues/65466 - internal static Func GetTotalAvailableMemoryBytes { get; set; } = () => GC.GetGCMemoryInfo().TotalAvailableMemoryBytes; + internal UniformUnmanagedMemoryPoolMemoryAllocator( + int sharedArrayPoolThresholdInBytes, + int poolBufferSizeInBytes, + long maxPoolSizeInBytes, + int unmanagedBufferSizeInBytes, + MemoryAllocatorOptions options) + : this(sharedArrayPoolThresholdInBytes, poolBufferSizeInBytes, maxPoolSizeInBytes, unmanagedBufferSizeInBytes) + => this.ApplyOptions(options); /// protected internal override int GetBufferCapacityInBytes() => this.poolBufferSizeInBytes; /// - public override IMemoryOwner Allocate( + protected override AllocationTrackedMemoryManager AllocateCore( int length, AllocationOptions options = AllocationOptions.None) { - if (length < 0) - { - InvalidMemoryOperationException.ThrowNegativeAllocationException(length); - } - - ulong lengthInBytes = (ulong)length * (ulong)Unsafe.SizeOf(); - if (lengthInBytes > (ulong)this.SingleBufferAllocationLimitBytes) - { - InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes); - } - - if (lengthInBytes <= (ulong)this.sharedArrayPoolThresholdInBytes) + int lengthInBytes = length * Unsafe.SizeOf(); + if (lengthInBytes <= this.sharedArrayPoolThresholdInBytes) { - var buffer = new SharedArrayPoolBuffer(length); + SharedArrayPoolBuffer buffer = new(length); if (options.Has(AllocationOptions.Clean)) { buffer.GetSpan().Clear(); @@ -105,17 +99,16 @@ public override IMemoryOwner Allocate( return buffer; } - if (lengthInBytes <= (ulong)this.poolBufferSizeInBytes) + if (lengthInBytes <= this.poolBufferSizeInBytes) { UnmanagedMemoryHandle mem = this.pool.Rent(); if (mem.IsValid) { - UnmanagedBuffer buffer = this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean)); - return buffer; + return this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean)); } } - return this.nonPoolAllocator.Allocate(length, options); + return UnmanagedMemoryAllocator.AllocateBuffer(length, options); } /// @@ -127,7 +120,7 @@ internal override MemoryGroup AllocateGroupCore( { if (totalLengthInBytes <= this.sharedArrayPoolThresholdInBytes) { - var buffer = new SharedArrayPoolBuffer((int)totalLengthInElements); + SharedArrayPoolBuffer buffer = new((int)totalLengthInElements); return MemoryGroup.CreateContiguous(buffer, options.Has(AllocationOptions.Clean)); } @@ -155,20 +148,14 @@ internal override MemoryGroup AllocateGroupCore( private static long GetDefaultMaxPoolSizeBytes() { - // On 64 bit set the pool size to a portion of the total available memory. - // https://github.com/dotnet/runtime/issues/55126#issuecomment-876779327 if (Environment.Is64BitProcess) { - long total = GetTotalAvailableMemoryBytes(); - - // Workaround for https://github.com/dotnet/runtime/issues/65466 - if (total > 0) - { - return (long)((ulong)total / 8); - } + // On 64 bit set the pool size to a portion of the total available memory. + GCMemoryInfo info = GC.GetGCMemoryInfo(); + return info.TotalAvailableMemoryBytes / 8; } - // Stick to a conservative value of 128 Megabytes on other platforms and 32 bit .NET 5.0: + // Stick to a conservative value of 128 Megabytes on 32 bit. return 128 * OneMegabyte; } } diff --git a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs index da202aa596..eb52da7c03 100644 --- a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs @@ -18,9 +18,16 @@ internal class UnmanagedMemoryAllocator : MemoryAllocator protected internal override int GetBufferCapacityInBytes() => this.bufferCapacityInBytes; - public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + protected override AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) + where T : struct + => AllocateBuffer(length, options); + + // The pooled allocator uses this internal entry point when it needs a raw unmanaged owner without + // nesting another allocator-level reservation cycle around the fallback allocation. + internal static UnmanagedBuffer AllocateBuffer(int length, AllocationOptions options = AllocationOptions.None) + where T : struct { - var buffer = UnmanagedBuffer.Allocate(length); + UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(length); if (options.Has(AllocationOptions.Clean)) { buffer.GetSpan().Clear(); diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 2eb05ea935..290d978d0b 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -25,27 +25,65 @@ public static IMemoryGroup GetMemoryGroup(this Buffer2D buffer) return buffer.FastMemoryGroup.View; } + /// + /// Performs a deep clone of the buffer covering the specified . + /// + /// The element type. + /// The source buffer. + /// The configuration. + /// The rectangle to clone. + /// The . + internal static Buffer2D CloneRegion(this Buffer2D source, Configuration configuration, Rectangle rectangle) + where T : unmanaged + { + Buffer2D buffer = configuration.MemoryAllocator.Allocate2D( + rectangle.Width, + rectangle.Height, + configuration.PreferContiguousImageBuffers); + + // Optimization for when the size of the area is the same as the buffer size. + Buffer2DRegion sourceRegion = source.GetRegion(rectangle); + if (sourceRegion.IsFullBufferArea) + { + sourceRegion.Buffer.CopyTo(buffer); + } + else + { + for (int y = 0; y < rectangle.Height; y++) + { + sourceRegion.DangerousGetRowSpan(y).CopyTo(buffer.DangerousGetRowSpan(y)); + } + } + + return buffer; + } + /// /// TODO: Does not work with multi-buffer groups, should be specific to Resize. - /// Copy columns of inplace, - /// from positions starting at to positions at . + /// Copy columns of in-place, + /// from positions starting at to positions at . /// + /// The element type. + /// The . + /// The source column index. + /// The destination column index. + /// The number of columns to copy. internal static unsafe void DangerousCopyColumns( this Buffer2D buffer, int sourceIndex, - int destIndex, + int destinationIndex, int columnCount) where T : struct { DebugGuard.NotNull(buffer, nameof(buffer)); DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex)); - DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, 0, nameof(sourceIndex)); - CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destIndex, columnCount); + DebugGuard.MustBeGreaterThanOrEqualTo(destinationIndex, 0, nameof(sourceIndex)); + CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destinationIndex, columnCount); int elementSize = Unsafe.SizeOf(); - int width = buffer.Width * elementSize; + int rowByteStride = buffer.RowStride * elementSize; int sOffset = sourceIndex * elementSize; - int dOffset = destIndex * elementSize; + int dOffset = destinationIndex * elementSize; long count = columnCount * elementSize; Span span = MemoryMarshal.AsBytes(buffer.DangerousGetSingleMemory().Span); @@ -60,65 +98,45 @@ internal static unsafe void DangerousCopyColumns( Buffer.MemoryCopy(sPtr, dPtr, count, count); - basePtr += width; + basePtr += rowByteStride; } } } /// - /// Returns a representing the full area of the buffer. - /// - /// The element type - /// The - /// The - internal static Rectangle FullRectangle(this Buffer2D buffer) - where T : struct - { - return new Rectangle(0, 0, buffer.Width, buffer.Height); - } - - /// - /// Return a to the subregion represented by 'rectangle' + /// Return a to the subregion represented by . /// /// The element type /// The /// The rectangle subregion /// The - internal static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle) - where T : unmanaged => - new Buffer2DRegion(buffer, rectangle); - - internal static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height) + public static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle) where T : unmanaged => - new Buffer2DRegion(buffer, new Rectangle(x, y, width, height)); + new(buffer, rectangle); /// - /// Return a to the whole area of 'buffer' + /// Return a to the specified area of . /// - /// The element type - /// The - /// The - internal static Buffer2DRegion GetRegion(this Buffer2D buffer) + /// The element type. + /// The . + /// The X coordinate of the region. + /// The Y coordinate of the region. + /// The region width. + /// The region height. + /// The . + public static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height) where T : unmanaged => - new Buffer2DRegion(buffer); + new(buffer, new Rectangle(x, y, width, height)); /// - /// Returns the size of the buffer. + /// Return a to the whole area of . /// /// The element type /// The - /// The of the buffer - internal static Size Size(this Buffer2D buffer) - where T : struct => - new(buffer.Width, buffer.Height); - - /// - /// Gets the bounds of the buffer. - /// - /// The - internal static Rectangle Bounds(this Buffer2D buffer) - where T : struct => - new(0, 0, buffer.Width, buffer.Height); + /// The + public static Buffer2DRegion GetRegion(this Buffer2D buffer) + where T : unmanaged => + new(buffer); [Conditional("DEBUG")] private static void CheckColumnRegionsDoNotOverlap( diff --git a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs index 033b0a25a6..c78c4ce9ee 100644 --- a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs +++ b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs @@ -15,17 +15,17 @@ public readonly struct Buffer2DRegion /// Initializes a new instance of the struct. ///
/// The . - /// The defining a rectangular area within the buffer. + /// The defining a rectangular area within the buffer. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Buffer2DRegion(Buffer2D buffer, Rectangle rectangle) + public Buffer2DRegion(Buffer2D buffer, Rectangle bounds) { - DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); - DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); - DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, buffer.Width, nameof(rectangle)); - DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, buffer.Height, nameof(rectangle)); + DebugGuard.MustBeGreaterThanOrEqualTo(bounds.X, 0, nameof(bounds)); + DebugGuard.MustBeGreaterThanOrEqualTo(bounds.Y, 0, nameof(bounds)); + DebugGuard.MustBeLessThanOrEqualTo(bounds.Width, buffer.Width, nameof(bounds)); + DebugGuard.MustBeLessThanOrEqualTo(bounds.Height, buffer.Height, nameof(bounds)); this.Buffer = buffer; - this.Rectangle = rectangle; + this.Bounds = bounds; } /// @@ -34,15 +34,10 @@ public Buffer2DRegion(Buffer2D buffer, Rectangle rectangle) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion(Buffer2D buffer) - : this(buffer, buffer.FullRectangle()) + : this(buffer, buffer.Bounds) { } - /// - /// Gets the rectangle specifying the boundaries of the area in . - /// - public Rectangle Rectangle { get; } - /// /// Gets the being pointed by this instance. /// @@ -51,27 +46,32 @@ public Buffer2DRegion(Buffer2D buffer) /// /// Gets the width /// - public int Width => this.Rectangle.Width; + public int Width => this.Bounds.Width; /// /// Gets the height /// - public int Height => this.Rectangle.Height; + public int Height => this.Bounds.Height; /// - /// Gets the pixel stride which is equal to the width of . + /// Gets the number of elements between row starts in . /// - public int Stride => this.Buffer.Width; + public int Stride => this.Buffer.RowStride; /// /// Gets the size of the area. /// - internal Size Size => this.Rectangle.Size; + public Size Size => this.Bounds.Size; + + /// + /// Gets the rectangle specifying the boundaries of the area in . + /// + public Rectangle Bounds { get; } /// /// Gets a value indicating whether the area refers to the entire /// - internal bool IsFullBufferArea => this.Size == this.Buffer.Size(); + internal bool IsFullBufferArea => this.Size == this.Buffer.Size; /// /// Gets or sets a value at the given index. @@ -79,7 +79,7 @@ public Buffer2DRegion(Buffer2D buffer) /// The position inside a row /// The row index /// The reference to the value - internal ref T this[int x, int y] => ref this.Buffer[x + this.Rectangle.X, y + this.Rectangle.Y]; + internal ref T this[int x, int y] => ref this.Buffer[x + this.Bounds.X, y + this.Bounds.Y]; /// /// Gets a span to row 'y' inside this area. @@ -89,9 +89,9 @@ public Buffer2DRegion(Buffer2D buffer) [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span DangerousGetRowSpan(int y) { - int yy = this.Rectangle.Y + y; - int xx = this.Rectangle.X; - int width = this.Rectangle.Width; + int yy = this.Bounds.Y + y; + int xx = this.Bounds.X; + int width = this.Bounds.Width; return this.Buffer.DangerousGetRowSpan(yy).Slice(xx, width); } @@ -107,23 +107,23 @@ public Span DangerousGetRowSpan(int y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion GetSubRegion(int x, int y, int width, int height) { - var rectangle = new Rectangle(x, y, width, height); + Rectangle rectangle = new(x, y, width, height); return this.GetSubRegion(rectangle); } /// /// Returns a subregion as . (Similar to .) /// - /// The specifying the boundaries of the subregion + /// The specifying the boundaries of the subregion /// The subregion [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion GetSubRegion(Rectangle rectangle) { - DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle)); - DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Bounds.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Bounds.Height, nameof(rectangle)); - int x = this.Rectangle.X + rectangle.X; - int y = this.Rectangle.Y + rectangle.Y; + int x = this.Bounds.X + rectangle.X; + int y = this.Bounds.Y + rectangle.Y; rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height); return new Buffer2DRegion(this.Buffer, rectangle); } @@ -135,8 +135,8 @@ public Buffer2DRegion GetSubRegion(Rectangle rectangle) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref T GetReferenceToOrigin() { - int y = this.Rectangle.Y; - int x = this.Rectangle.X; + int y = this.Bounds.Y; + int x = this.Bounds.X; return ref this.Buffer.DangerousGetRowSpan(y)[x]; } @@ -146,13 +146,13 @@ internal ref T GetReferenceToOrigin() internal void Clear() { // Optimization for when the size of the area is the same as the buffer size. - if (this.IsFullBufferArea) + if (this.IsFullBufferArea && this.Buffer.RowStride == this.Buffer.Width) { - this.Buffer.FastMemoryGroup.Clear(); + this.Buffer.Clear(default); return; } - for (int y = 0; y < this.Rectangle.Height; y++) + for (int y = 0; y < this.Bounds.Height; y++) { Span row = this.DangerousGetRowSpan(y); row.Clear(); @@ -166,13 +166,13 @@ internal void Clear() internal void Fill(T value) { // Optimization for when the size of the area is the same as the buffer size. - if (this.IsFullBufferArea) + if (this.IsFullBufferArea && this.Buffer.RowStride == this.Buffer.Width) { - this.Buffer.FastMemoryGroup.Fill(value); + this.Buffer.Clear(value); return; } - for (int y = 0; y < this.Rectangle.Height; y++) + for (int y = 0; y < this.Bounds.Height; y++) { Span row = this.DangerousGetRowSpan(y); row.Fill(value); diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 39c6e62e15..cfd7664f01 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Memory; @@ -20,21 +21,53 @@ public sealed class Buffer2D : IDisposable /// The number of elements in a row. /// The number of rows. internal Buffer2D(MemoryGroup memoryGroup, int width, int height) + : this(memoryGroup, width, height, width) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to wrap. + /// The number of elements in a row. + /// The number of rows. + /// The number of elements between row starts. + internal Buffer2D(MemoryGroup memoryGroup, int width, int height, int rowStride) + { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.MustBeGreaterThanOrEqualTo(rowStride, width, nameof(rowStride)); + this.FastMemoryGroup = memoryGroup; - this.Width = width; - this.Height = height; + this.Size = new Size(width, height); + this.RowStride = rowStride; } /// /// Gets the width. /// - public int Width { get; private set; } + public int Width => this.Size.Width; /// /// Gets the height. /// - public int Height { get; private set; } + public int Height => this.Size.Height; + + /// + /// Gets the size of the buffer. + /// + public Size Size { get; private set; } + + /// + /// Gets the bounds of the buffer. + /// + /// The + public Rectangle Bounds => new(0, 0, this.Width, this.Height); + + /// + /// Gets the number of elements between row starts in the backing memory. + /// + public int RowStride { get; private set; } /// /// Gets the backing . @@ -75,6 +108,168 @@ internal Buffer2D(MemoryGroup memoryGroup, int width, int height) } } + /// + /// Wraps an existing memory area as a with tightly packed rows. + /// + /// + /// This method does not transfer ownership of to the returned . + /// The caller is responsible for ensuring that the memory remains valid for the entire lifetime of the returned buffer. + /// If originates from an (for example from ), + /// do not dispose that owner while the returned buffer is still in use. + /// + /// The source memory. + /// The number of elements in each row. + /// The number of rows. + /// The wrapped instance. + /// Thrown when or is not positive. + /// Thrown when is shorter than width * height. +#pragma warning disable CA1000 // Do not declare static members on generic types + public static Buffer2D WrapMemory(Memory memory, int width, int height) +#pragma warning restore CA1000 // Do not declare static members on generic types + => WrapMemory(memory, width, height, width); + + /// + /// Wraps an existing memory area as a using the specified row stride. + /// + /// + /// This method does not transfer ownership of to the returned . + /// The caller is responsible for ensuring that the memory remains valid for the entire lifetime of the returned buffer. + /// If originates from an (for example from ), + /// do not dispose that owner while the returned buffer is still in use. + /// The minimum required length is ((height - 1) * stride) + width elements. + /// + /// The source memory. + /// The number of elements in each row. + /// The number of rows. + /// The number of elements between row starts in the source memory. + /// The wrapped instance. + /// + /// Thrown when or is not positive, + /// or when is less than . + /// + /// Thrown when is shorter than the required buffer size. +#pragma warning disable CA1000 // Do not declare static members on generic types + public static Buffer2D WrapMemory(Memory memory, int width, int height, int stride) +#pragma warning restore CA1000 // Do not declare static members on generic types + { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.MustBeGreaterThanOrEqualTo(stride, width, nameof(stride)); + + long requiredLength = checked(((long)(height - 1) * stride) + width); + Guard.IsTrue(memory.Length >= requiredLength, nameof(memory), "The length of the input memory is less than the specified buffer size"); + + MemoryGroup memorySource = MemoryGroup.Wrap(memory); + return new Buffer2D(memorySource, width, height, stride); + } + + /// + /// Gets the representation of the values as a single contiguous + /// when the backing group is a single tightly packed segment. + /// + /// The referencing the buffer. + /// + /// when the buffer can be copied as one contiguous block + /// without per-row handling; otherwise . + /// + public bool DangerousTryGetSingleMemory(out Memory memory) + { + if (this.MemoryGroup.Count > 1 || this.RowStride != this.Width) + { + memory = default; + return false; + } + + int logicalLength = checked((int)((long)this.Width * this.Height)); + memory = this.MemoryGroup[0][..logicalLength]; + return true; + } + + /// + /// Copies this buffer into using the source logical row layout. + /// + /// + /// When dimensions are equal, destination stride is respected. + /// When dimensions differ, source stride is used to copy the source logical layout into destination memory. + /// + /// The destination buffer. + internal void CopyTo(Buffer2D destination) + { + Guard.NotNull(destination, nameof(destination)); + + bool sameDimensions = this.Width == destination.Width && this.Height == destination.Height; + int destinationStride = sameDimensions ? destination.RowStride : this.RowStride; + + // Different dimensions use source logical layout. This supports SwapOrCopyContent, + // where metadata is swapped after data copy. + this.FastMemoryGroup.CopyTo( + this.RowStride, + destination.FastMemoryGroup, + destinationStride, + this.Width, + this.Height); + } + + /// + /// Copies this buffer into using the source row stride as destination layout. + /// + /// The destination span. + internal void CopyTo(Span destination) + { + long requiredLength = checked(((long)(this.Height - 1) * this.RowStride) + this.Width); + Guard.MustBeGreaterThanOrEqualTo(destination.Length, requiredLength, nameof(destination)); + + this.FastMemoryGroup.CopyTo( + this.RowStride, + destination, + this.RowStride, + this.Width, + this.Height); + } + + /// + /// Copies tightly packed row-major data from into this buffer. + /// + /// The source data. + internal void CopyFrom(ReadOnlySpan source) => this.CopyFrom(source, this.Width); + + /// + /// Copies row-major data from into this buffer using + /// elements between source row starts. + /// + /// The source data. + /// The number of elements between source row starts. + internal void CopyFrom(ReadOnlySpan source, int sourceStride) + { + Guard.MustBeGreaterThanOrEqualTo(sourceStride, this.Width, nameof(sourceStride)); + + long requiredLength = checked(((long)(this.Height - 1) * sourceStride) + this.Width); + Guard.MustBeGreaterThanOrEqualTo(source.Length, requiredLength, nameof(source)); + + // Copy row by row so padded source rows map correctly into the destination logical rows. + int sourceOffset = 0; + for (int y = 0; y < this.Height; y++) + { + source.Slice(sourceOffset, this.Width).CopyTo(this.DangerousGetRowSpan(y)); + sourceOffset += sourceStride; + } + } + + /// + /// Clears this buffer when is default; otherwise fills it with . + /// + /// The fill value. + internal void Clear(T value) + { + if (value.Equals(default)) + { + this.FastMemoryGroup.Clear(); + return; + } + + this.FastMemoryGroup.Fill(value); + } + /// /// Disposes the instance /// @@ -102,7 +297,13 @@ public Span DangerousGetRowSpan(int y) this.ThrowYOutOfRangeException(y); } - return this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width); + if (this.RowStride == this.Width) + { + return this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width); + } + + int rowStart = checked(y * this.RowStride); + return this.FastMemoryGroup[0].Span.Slice(rowStart, this.Width); } internal bool DangerousTryGetPaddedRowSpan(int y, int padding, out Span paddedSpan) @@ -111,8 +312,10 @@ internal bool DangerousTryGetPaddedRowSpan(int y, int padding, out Span padde DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); int stride = this.Width + padding; - - Span slice = this.FastMemoryGroup.GetRemainingSliceOfBuffer(y * (long)this.Width); + long rowStart = y * (long)this.RowStride; + Span slice = this.RowStride == this.Width + ? this.FastMemoryGroup.GetRemainingSliceOfBuffer(rowStart) + : this.FastMemoryGroup[0].Span[checked((int)rowStart)..]; if (slice.Length < stride) { @@ -127,7 +330,10 @@ internal bool DangerousTryGetPaddedRowSpan(int y, int padding, out Span padde [MethodImpl(InliningOptions.ShortMethod)] internal ref T GetElementUnsafe(int x, int y) { - Span span = this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width); + Span span = this.RowStride == this.Width + ? this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width) + : this.FastMemoryGroup[0].Span.Slice(checked(y * this.RowStride), this.Width); + return ref span[x]; } @@ -141,6 +347,13 @@ internal Memory GetSafeRowMemory(int y) { DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + + if (this.RowStride != this.Width) + { + int rowStart = checked(y * this.RowStride); + return this.FastMemoryGroup[0].Slice(rowStart, this.Width); + } + return this.FastMemoryGroup.View.GetBoundedMemorySlice(y * (long)this.Width, this.Width); } @@ -185,21 +398,30 @@ internal static bool SwapOrCopyContent(Buffer2D destination, Buffer2D sour } else { - if (destination.FastMemoryGroup.TotalLength != source.FastMemoryGroup.TotalLength) + long sourceLayoutLength = GetRequiredLength(source.Width, source.Height, source.RowStride); + long destinationLayoutLength = GetRequiredLength(destination.Width, destination.Height, destination.RowStride); + + bool destinationCanRepresentSource = destination.FastMemoryGroup.TotalLength >= sourceLayoutLength; + bool sourceCanRepresentDestination = source.FastMemoryGroup.TotalLength >= destinationLayoutLength; + if (!destinationCanRepresentSource || !sourceCanRepresentDestination) { throw new InvalidMemoryOperationException( "Trying to copy/swap incompatible buffers. This is most likely caused by applying an unsupported processor to wrapped-memory images."); } - source.FastMemoryGroup.CopyTo(destination.MemoryGroup); + source.CopyTo(destination); } - (destination.Width, source.Width) = (source.Width, destination.Width); - (destination.Height, source.Height) = (source.Height, destination.Height); + (destination.Size, source.Size) = (source.Size, destination.Size); + (destination.RowStride, source.RowStride) = (source.RowStride, destination.RowStride); return swapped; } [MethodImpl(InliningOptions.ColdPath)] private void ThrowYOutOfRangeException(int y) => throw new ArgumentOutOfRangeException($"DangerousGetRowSpan({y}). Y was out of range. Height={this.Height}"); + + [MethodImpl(InliningOptions.ShortMethod)] + private static long GetRequiredLength(int width, int height, int stride) + => checked(((long)(height - 1) * stride) + width); } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs index 7e9719ea75..03f26aab04 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs @@ -15,12 +15,12 @@ public interface IMemoryGroup : IReadOnlyList> /// Gets the number of elements per contiguous sub-buffer preceding the last buffer. /// The last buffer is allowed to be smaller. /// - int BufferLength { get; } + public int BufferLength { get; } /// /// Gets the aggregate number of elements in the group. /// - long TotalLength { get; } + public long TotalLength { get; } /// /// Gets a value indicating whether the group has been invalidated. @@ -29,7 +29,7 @@ public interface IMemoryGroup : IReadOnlyList> /// Invalidation usually occurs when an image processor capable to alter the image dimensions replaces /// the image buffers internally. /// - bool IsValid { get; } + public bool IsValid { get; } /// /// Returns a value-type implementing an allocation-free enumerator of the memory groups in the current @@ -39,5 +39,5 @@ public interface IMemoryGroup : IReadOnlyList> /// implementation, which is still available when casting to one of the underlying interfaces. /// /// A new instance mapping the current values in use. - new MemoryGroupEnumerator GetEnumerator(); + public new MemoryGroupEnumerator GetEnumerator(); } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs index b4b1ffc6f4..b399d3d700 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs @@ -36,8 +36,13 @@ internal static void Clear(this IMemoryGroup group) /// /// Returns a slice that is expected to be within the bounds of a single buffer. - /// Otherwise is thrown. /// + /// The type of element. + /// The group. + /// The start index of the slice. + /// The length of the slice. + /// Slice is out of bounds. + /// The slice. internal static Memory GetBoundedMemorySlice(this IMemoryGroup group, long start, int length) where T : struct { @@ -66,128 +71,159 @@ internal static Memory GetBoundedMemorySlice(this IMemoryGroup group, l return memory.Slice(bufferStart, length); } - internal static void CopyTo(this IMemoryGroup source, Span target) + /// + /// Copies a 2D logical region from into + /// using the provided source and target strides. + /// + /// The element type. + /// The source memory group. + /// Elements between source row starts. + /// The destination span. + /// Elements between destination row starts. + /// The logical row width to copy. + /// The number of rows to copy. + internal static void CopyTo( + this IMemoryGroup source, + int sourceStride, + Span target, + int targetStride, + int width, + int height) where T : struct { Guard.NotNull(source, nameof(source)); - Guard.MustBeGreaterThanOrEqualTo(target.Length, source.TotalLength, nameof(target)); + Guard.MustBeGreaterThanOrEqualTo(width, 0, nameof(width)); + Guard.MustBeGreaterThanOrEqualTo(height, 0, nameof(height)); + Guard.MustBeGreaterThanOrEqualTo(sourceStride, width, nameof(sourceStride)); + Guard.MustBeGreaterThanOrEqualTo(targetStride, width, nameof(targetStride)); - var cur = new MemoryGroupCursor(source); - long position = 0; - while (position < source.TotalLength) - { - int fwd = Math.Min(cur.LookAhead(), target.Length); - cur.GetSpan(fwd).CopyTo(target); + long sourceRequired = height == 0 ? 0 : checked(((long)(height - 1) * sourceStride) + width); + long targetRequired = height == 0 ? 0 : checked(((long)(height - 1) * targetStride) + width); + Guard.MustBeGreaterThanOrEqualTo(source.TotalLength, sourceRequired, nameof(source)); + Guard.MustBeGreaterThanOrEqualTo(target.Length, targetRequired, nameof(target)); - cur.Forward(fwd); - target = target[fwd..]; - position += fwd; + if (width == 0 || height == 0) + { + return; } - } - - internal static void CopyTo(this Span source, IMemoryGroup target) - where T : struct - => CopyTo((ReadOnlySpan)source, target); - internal static void CopyTo(this ReadOnlySpan source, IMemoryGroup target) - where T : struct - { - Guard.NotNull(target, nameof(target)); - Guard.MustBeGreaterThanOrEqualTo(target.TotalLength, source.Length, nameof(target)); + MemoryGroupCursor sourceCursor = new(source); + int sourceSkip = sourceStride - width; - var cur = new MemoryGroupCursor(target); - - while (!source.IsEmpty) + for (int y = 0; y < height; y++) { - int fwd = Math.Min(cur.LookAhead(), source.Length); - source[..fwd].CopyTo(cur.GetSpan(fwd)); - cur.Forward(fwd); - source = source[fwd..]; + int rowStart = checked(y * targetStride); + Span destinationRow = target.Slice(rowStart, width); + CopyFromCursorToSpan(ref sourceCursor, destinationRow); + + // Trailing padding after the last row is optional, so only skip between rows. + if (y < height - 1) + { + ForwardCursor(ref sourceCursor, sourceSkip); + } } } - internal static void CopyTo(this IMemoryGroup? source, IMemoryGroup? target) + /// + /// Copies a 2D logical region from into + /// using the provided source and target strides. + /// + /// The element type. + /// The source memory group. + /// Elements between source row starts. + /// The destination memory group. + /// Elements between destination row starts. + /// The logical row width to copy. + /// The number of rows to copy. + internal static void CopyTo( + this IMemoryGroup source, + int sourceStride, + IMemoryGroup target, + int targetStride, + int width, + int height) where T : struct { Guard.NotNull(source, nameof(source)); Guard.NotNull(target, nameof(target)); Guard.IsTrue(source.IsValid, nameof(source), "Source group must be valid."); Guard.IsTrue(target.IsValid, nameof(target), "Target group must be valid."); - Guard.MustBeLessThanOrEqualTo(source.TotalLength, target.TotalLength, "Destination buffer too short!"); + Guard.MustBeGreaterThanOrEqualTo(width, 0, nameof(width)); + Guard.MustBeGreaterThanOrEqualTo(height, 0, nameof(height)); + Guard.MustBeGreaterThanOrEqualTo(sourceStride, width, nameof(sourceStride)); + Guard.MustBeGreaterThanOrEqualTo(targetStride, width, nameof(targetStride)); - if (source.IsEmpty()) + long sourceRequired = height == 0 ? 0 : checked(((long)(height - 1) * sourceStride) + width); + long targetRequired = height == 0 ? 0 : checked(((long)(height - 1) * targetStride) + width); + Guard.MustBeGreaterThanOrEqualTo(source.TotalLength, sourceRequired, nameof(source)); + Guard.MustBeGreaterThanOrEqualTo(target.TotalLength, targetRequired, nameof(target)); + + if (width == 0 || height == 0) { return; } - long position = 0; - var srcCur = new MemoryGroupCursor(source); - var trgCur = new MemoryGroupCursor(target); + MemoryGroupCursor sourceCursor = new(source); + MemoryGroupCursor targetCursor = new(target); + int sourceSkip = sourceStride - width; + int targetSkip = targetStride - width; - while (position < source.TotalLength) + for (int y = 0; y < height; y++) { - int fwd = Math.Min(srcCur.LookAhead(), trgCur.LookAhead()); - Span srcSpan = srcCur.GetSpan(fwd); - Span trgSpan = trgCur.GetSpan(fwd); - srcSpan.CopyTo(trgSpan); - - srcCur.Forward(fwd); - trgCur.Forward(fwd); - position += fwd; + CopyFromCursorToCursor(ref sourceCursor, ref targetCursor, width); + + // Trailing padding after the last row is optional, so only skip between rows. + if (y < height - 1) + { + ForwardCursor(ref sourceCursor, sourceSkip); + ForwardCursor(ref targetCursor, targetSkip); + } } } - internal static void TransformTo( - this IMemoryGroup source, - IMemoryGroup target, - TransformItemsDelegate transform) - where TSource : struct - where TTarget : struct + private static void CopyFromCursorToCursor( + ref MemoryGroupCursor source, + ref MemoryGroupCursor target, + int count) + where T : struct { - Guard.NotNull(source, nameof(source)); - Guard.NotNull(target, nameof(target)); - Guard.NotNull(transform, nameof(transform)); - Guard.IsTrue(source.IsValid, nameof(source), "Source group must be valid."); - Guard.IsTrue(target.IsValid, nameof(target), "Target group must be valid."); - Guard.MustBeLessThanOrEqualTo(source.TotalLength, target.TotalLength, "Destination buffer too short!"); - - if (source.IsEmpty()) + int remaining = count; + while (remaining > 0) { - return; + int fwd = Math.Min(remaining, Math.Min(source.LookAhead(), target.LookAhead())); + source.GetSpan(fwd).CopyTo(target.GetSpan(fwd)); + source.Forward(fwd); + target.Forward(fwd); + remaining -= fwd; } + } - long position = 0; - var srcCur = new MemoryGroupCursor(source); - var trgCur = new MemoryGroupCursor(target); - - while (position < source.TotalLength) + private static void CopyFromCursorToSpan(ref MemoryGroupCursor source, Span target) + where T : struct + { + int remaining = target.Length; + while (remaining > 0) { - int fwd = Math.Min(srcCur.LookAhead(), trgCur.LookAhead()); - Span srcSpan = srcCur.GetSpan(fwd); - Span trgSpan = trgCur.GetSpan(fwd); - transform(srcSpan, trgSpan); - - srcCur.Forward(fwd); - trgCur.Forward(fwd); - position += fwd; + int copied = target.Length - remaining; + int fwd = Math.Min(remaining, source.LookAhead()); + source.GetSpan(fwd).CopyTo(target[copied..]); + source.Forward(fwd); + remaining -= fwd; } } - internal static void TransformInplace( - this IMemoryGroup memoryGroup, - TransformItemsInplaceDelegate transform) + private static void ForwardCursor(ref MemoryGroupCursor cursor, int steps) where T : struct { - foreach (Memory memory in memoryGroup) + int remaining = steps; + while (remaining > 0) { - transform(memory.Span); + int fwd = Math.Min(remaining, cursor.LookAhead()); + cursor.Forward(fwd); + remaining -= fwd; } } - internal static bool IsEmpty(this IMemoryGroup group) - where T : struct - => group.Count == 0; - private struct MemoryGroupCursor where T : struct { diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs index 950e2a019e..75e93ce7f8 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs @@ -31,23 +31,23 @@ public override int Count /// [MethodImpl(InliningOptions.ShortMethod)] - public override MemoryGroupEnumerator GetEnumerator() - { - return new MemoryGroupEnumerator(this); - } + public override MemoryGroupEnumerator GetEnumerator() => new(this); /// IEnumerator> IEnumerable>.GetEnumerator() - { + /* The runtime sees the Array class as if it implemented the * type-generic collection interfaces explicitly, so here we * can just cast the source array to IList> (or to * an equivalent type), and invoke the generic GetEnumerator * method directly from that interface reference. This saves * having to create our own iterator block here. */ - return ((IList>)this.source).GetEnumerator(); - } + => ((IList>)this.source).GetEnumerator(); - public override void Dispose() => this.View.Invalidate(); + public override void Dispose() + { + this.View.Invalidate(); + this.ReleaseAllocationTracking(); + } } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs index 9da0139e6e..c4b22f1560 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs @@ -60,21 +60,73 @@ public override Memory this[int index] } } + internal override void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) + { + if (this.groupLifetimeGuard != null) + { + // Pool-owned multi-buffer groups recover leaked handles through the group guard finalizer. + this.groupLifetimeGuard.AttachAllocationTracking(allocator, lengthInBytes); + return; + } + + IMemoryOwner[]? memoryOwners = this.memoryOwners; + if (memoryOwners?.Length == 1 && memoryOwners[0] is AllocationTrackedMemoryManager trackedOwner) + { + // Single-buffer groups should release tracking with the buffer owner when that owner has + // a more precise lifetime, such as an existing pooled-resource finalizer. + trackedOwner.AttachAllocationTracking(allocator, lengthInBytes); + return; + } + + if (memoryOwners?.Length > 1) + { + foreach (IMemoryOwner memoryOwner in memoryOwners) + { + if (memoryOwner is not AllocationTrackedMemoryManager) + { + // Splitting is only valid when every segment can own its reservation. A single + // untracked segment makes the whole group ineligible, and this preflight has + // not attached anything yet, so the entire group can fall back immediately. + base.AttachAllocationTracking(allocator, lengthInBytes); + return; + } + } + + // Non-pool multi-buffer groups have no group-level finalizer, so each segment carries + // its own share of the reservation through the segment owner or its lifetime guard. + long remainingLengthInBytes = lengthInBytes; + int lastOwnerIndex = memoryOwners.Length - 1; + for (int i = 0; i < lastOwnerIndex; i++) + { + trackedOwner = (AllocationTrackedMemoryManager)memoryOwners[i]; + long ownerLengthInBytes = (long)trackedOwner.Memory.Length * Unsafe.SizeOf(); + trackedOwner.AttachAllocationTracking(allocator, ownerLengthInBytes); + remainingLengthInBytes -= ownerLengthInBytes; + } + + trackedOwner = (AllocationTrackedMemoryManager)memoryOwners[lastOwnerIndex]; + trackedOwner.AttachAllocationTracking(allocator, remainingLengthInBytes); + return; + } + + base.AttachAllocationTracking(allocator, lengthInBytes); + } + private static IMemoryOwner[] CreateBuffers( UnmanagedMemoryHandle[] pooledBuffers, int bufferLength, int sizeOfLastBuffer, AllocationOptions options) { - var result = new IMemoryOwner[pooledBuffers.Length]; + IMemoryOwner[] result = new IMemoryOwner[pooledBuffers.Length]; for (int i = 0; i < pooledBuffers.Length - 1; i++) { - var currentBuffer = ObservedBuffer.Create(pooledBuffers[i], bufferLength, options); + ObservedBuffer currentBuffer = ObservedBuffer.Create(pooledBuffers[i], bufferLength, options); result[i] = currentBuffer; } - var lastBuffer = ObservedBuffer.Create(pooledBuffers[pooledBuffers.Length - 1], sizeOfLastBuffer, options); - result[result.Length - 1] = lastBuffer; + ObservedBuffer lastBuffer = ObservedBuffer.Create(pooledBuffers[^1], sizeOfLastBuffer, options); + result[^1] = lastBuffer; return result; } @@ -155,6 +207,7 @@ public override void Dispose() } } + this.ReleaseAllocationTracking(); this.memoryOwners = null; this.IsValid = false; this.groupLifetimeGuard = null; @@ -193,7 +246,7 @@ public static ObservedBuffer Create( int lengthInElements, AllocationOptions options) { - var buffer = new ObservedBuffer(handle, lengthInElements); + ObservedBuffer buffer = new(handle, lengthInElements); if (options.Has(AllocationOptions.Clean)) { buffer.GetSpan().Clear(); diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index 03c29a7231..e0b9bca5e2 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -21,6 +21,7 @@ internal abstract partial class MemoryGroup : IMemoryGroup, IDisposable { private static readonly int ElementSize = Unsafe.SizeOf(); + private AllocationTrackingState allocationTracking; private MemoryGroupSpanCache memoryGroupSpanCache; private MemoryGroup(int bufferLength, long totalLength) @@ -52,16 +53,36 @@ private MemoryGroup(int bufferLength, long totalLength) /// public abstract MemoryGroupEnumerator GetEnumerator(); + /// + /// Attaches allocation tracking by specifying the allocator and the length, in bytes, to be tracked. + /// + /// The memory allocator to use for tracking allocations. + /// The length, in bytes, of the memory region to track. Must be greater than or equal to zero. + /// + /// Intended for one-time initialization after the group has been created; callers should avoid changing + /// tracking state concurrently with disposal. + /// + internal virtual void AttachAllocationTracking(MemoryAllocator allocator, long lengthInBytes) => + this.allocationTracking.Attach(allocator, lengthInBytes); + + /// + /// Releases any resources or tracking information associated with allocation tracking for this instance. + /// + /// + /// This method is intended to be called when allocation tracking is no longer needed. It is safe + /// to call multiple times; subsequent calls after the first have no effect, even when called concurrently. + /// + internal void ReleaseAllocationTracking() => this.allocationTracking.Release(); + /// IEnumerator> IEnumerable>.GetEnumerator() - { + /* This method is implemented in each derived class. * Implementing the method here as non-abstract and throwing, * then reimplementing it explicitly in each derived class, is * a workaround for the lack of support for abstract explicit * interface method implementations in C#. */ - throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()"); - } + => throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()"); /// IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); @@ -81,8 +102,8 @@ public static MemoryGroup Allocate( int bufferAlignmentInElements, AllocationOptions options = AllocationOptions.None) { - int bufferCapacityInBytes = allocator.GetBufferCapacityInBytes(); Guard.NotNull(allocator, nameof(allocator)); + int bufferCapacityInBytes = allocator.GetBufferCapacityInBytes(); if (totalLengthInElements < 0) { @@ -97,8 +118,8 @@ public static MemoryGroup Allocate( if (totalLengthInElements == 0) { - var buffers0 = new IMemoryOwner[1] { allocator.Allocate(0, options) }; - return new Owned(buffers0, 0, 0, true); + IMemoryOwner[] emptyBuffer = [allocator.AllocateGroupBuffer(0, options)]; + return new Owned(emptyBuffer, 0, 0, true); } int numberOfAlignedSegments = blockCapacityInElements / bufferAlignmentInElements; @@ -120,15 +141,15 @@ public static MemoryGroup Allocate( bufferCount++; } - var buffers = new IMemoryOwner[bufferCount]; + IMemoryOwner[] buffers = new IMemoryOwner[bufferCount]; for (int i = 0; i < buffers.Length - 1; i++) { - buffers[i] = allocator.Allocate(bufferLength, options); + buffers[i] = allocator.AllocateGroupBuffer(bufferLength, options); } if (bufferCount > 0) { - buffers[^1] = allocator.Allocate(sizeOfLastBuffer, options); + buffers[^1] = allocator.AllocateGroupBuffer(sizeOfLastBuffer, options); } return new Owned(buffers, bufferLength, totalLengthInElements, true); @@ -142,7 +163,7 @@ public static MemoryGroup CreateContiguous(IMemoryOwner buffer, bool clear } int length = buffer.Memory.Length; - var buffers = new IMemoryOwner[1] { buffer }; + IMemoryOwner[] buffers = [buffer]; return new Owned(buffers, length, length, true); } diff --git a/src/ImageSharp/Memory/InvalidMemoryOperationException.cs b/src/ImageSharp/Memory/InvalidMemoryOperationException.cs index 81210f13db..724af35e1d 100644 --- a/src/ImageSharp/Memory/InvalidMemoryOperationException.cs +++ b/src/ImageSharp/Memory/InvalidMemoryOperationException.cs @@ -39,4 +39,9 @@ internal static void ThrowInvalidAlignmentException(long alignment) => [DoesNotReturn] internal static void ThrowAllocationOverLimitException(ulong length, long limit) => throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of length={length} that exceeded the limit {limit}."); + + [DoesNotReturn] + internal static void ThrowAccumulativeAllocationOverLimitException(long requestedLength, long totalLength, long limit) => + throw new InvalidMemoryOperationException( + $"Attempted to allocate a buffer of length={requestedLength} that would increase the accumulative allocation size to {totalLength}, exceeding the limit {limit}."); } diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index ff306e1e45..57ebcab768 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -29,6 +29,9 @@ public static Buffer2D Allocate2D( AllocationOptions options = AllocationOptions.None) where T : struct { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + long groupLength = (long)width * height; MemoryGroup memoryGroup; if (preferContiguosImageBuffers && groupLength < int.MaxValue) @@ -104,6 +107,9 @@ internal static Buffer2D Allocate2DOveraligned( AllocationOptions options = AllocationOptions.None) where T : struct { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + long groupLength = (long)width * height; MemoryGroup memoryGroup = memoryAllocator.AllocateGroup( groupLength, diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 90a88d735a..f27dc74411 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -79,7 +79,7 @@ public override bool Equals(object? obj) /// public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; - internal RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); + internal RowInterval Slice(int start) => new(this.Min + start, this.Max); - internal RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); + internal RowInterval Slice(int start, int length) => new(this.Min + start, this.Min + start + length); } diff --git a/src/ImageSharp/Memory/TransformItemsDelegate{TSource, TTarget}.cs b/src/ImageSharp/Memory/TransformItemsDelegate{TSource, TTarget}.cs deleted file mode 100644 index bc3d17f8f0..0000000000 --- a/src/ImageSharp/Memory/TransformItemsDelegate{TSource, TTarget}.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Memory; - -#pragma warning disable SA1649 // File name should match first type name -internal delegate void TransformItemsDelegate(ReadOnlySpan source, Span target); -#pragma warning restore SA1649 // File name should match first type name diff --git a/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs b/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs deleted file mode 100644 index d1ef51fb85..0000000000 --- a/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Memory; - -internal delegate void TransformItemsInplaceDelegate(Span data); diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 1c0330d5d0..554afd69aa 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Cicp; 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; @@ -15,7 +17,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 +37,9 @@ internal ImageFrameMetadata(ImageFrameMetadata other) { 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 +47,10 @@ internal ImageFrameMetadata(ImageFrameMetadata other) 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 +78,19 @@ internal ImageFrameMetadata(ImageFrameMetadata other) /// 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 +100,80 @@ internal ImageFrameMetadata(ImageFrameMetadata other) /// 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)) + { + TFormatFrameMetadata derivedMeta = TFormatFrameMetadata.FromFormatConnectingFrameMetadata(decodedMetadata.ToFormatConnectingFrameMetadata()); + this.SetFormatMetadata(key, derivedMeta); + return derivedMeta; + } + TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata(); - this.formatMetadata[key] = newMeta; + this.SetFormatMetadata(key, newMeta); return newMeta; } /// - /// Gets the metadata value associated with the specified key. + /// Sets the metadata value associated with the specified key. /// /// The type of format metadata. /// The type of format frame metadata. + /// The key of the value to set. + /// The value to set. + public void SetFormatMetadata(IImageFormat key, TFormatFrameMetadata value) + where TFormatMetadata : class + where TFormatFrameMetadata : class, IFormatFrameMetadata + => this.formatMetadata[key] = value; + + /// + /// Creates a new instance the metadata value associated with the specified key. + /// The instance is created from a clone generated via . + /// + /// 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 + where TFormatFrameMetadata : class, IFormatFrameMetadata + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); + + /// + /// Synchronizes the profiles with the current metadata. + /// + internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this); + + /// + /// This method is called after a process has been applied to the image frame. + /// + /// The type of pixel format. + /// The source image frame. + /// The destination image frame. + /// The transformation matrix applied to the frame. + internal void AfterFrameApply( + ImageFrame source, + ImageFrame destination, + Matrix4x4 matrix) + where TPixel : unmanaged, IPixel { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) + // Always updated using the full frame dimensions. + // Individual format frame metadata will update with sub region dimensions if appropriate. + this.ExifProfile?.SyncDimensions(destination.Width, destination.Height); + this.ExifProfile?.SyncSubject(destination.Width, destination.Height, matrix); + + foreach (KeyValuePair meta in this.formatMetadata) { - metadata = (TFormatFrameMetadata)meta; - return true; + meta.Value.AfterFrameApply(source, destination, matrix); } - - metadata = default; - return false; } } diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 6b62be08ff..918cf162a5 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Cicp; 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; @@ -33,7 +35,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; @@ -60,9 +62,9 @@ private ImageMetadata(ImageMetadata other) 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(); @@ -165,12 +167,15 @@ public double VerticalResolution 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; } /// - /// 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,43 +183,45 @@ public double VerticalResolution /// 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)) + { + TFormatMetadata derivedMeta = TFormatMetadata.FromFormatConnectingMetadata(decodedMetadata.ToFormatConnectingMetadata()); + this.formatMetadata[key] = derivedMeta; + return derivedMeta; + } + + // 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; - } + public TFormatMetadata CloneFormatMetadata(IImageFormat key) + where TFormatMetadata : class, IFormatMetadata + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); - metadata = default; - return false; - } + internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) + where TFormatMetadata : class, IFormatMetadata + => this.formatMetadata[key] = value; /// public ImageMetadata DeepClone() => new(this); @@ -222,5 +229,36 @@ public bool TryGetFormatMetadata(IImageFormat /// /// Synchronizes the profiles with the current metadata. /// - internal void SyncProfiles() => this.ExifProfile?.Sync(this); + internal void SynchronizeProfiles() => this.ExifProfile?.Sync(this); + + /// + /// This method is called after a process has been applied to the image. + /// + /// The type of pixel format. + /// The destination image. + /// The transformation matrix applied to the image. + internal void AfterImageApply(Image destination, Matrix4x4 matrix) + where TPixel : unmanaged, IPixel + { + this.ExifProfile?.SyncDimensions(destination.Width, destination.Height); + this.ExifProfile?.SyncSubject(destination.Width, destination.Height, matrix); + + foreach (KeyValuePair meta in this.formatMetadata) + { + meta.Value.AfterImageApply(destination, matrix); + } + } + + 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; + } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs index 725533ea5f..df6f7bdf83 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs @@ -7,21 +7,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; internal static class ExifConstants { - public static ReadOnlySpan LittleEndianByteOrderMarker => new byte[] - { + public static ReadOnlySpan LittleEndianByteOrderMarker => + [ (byte)'I', (byte)'I', 0x2A, - 0x00, - }; + 0x00 + ]; - public static ReadOnlySpan BigEndianByteOrderMarker => new byte[] - { + public static ReadOnlySpan BigEndianByteOrderMarker => + [ (byte)'M', (byte)'M', 0x00, 0x2A - }; + ]; // UTF-8 is better than ASCII, UTF-8 encodes the ASCII codes the same way public static Encoding DefaultEncoding => Encoding.UTF8; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 90a5d15b74..e0b549362d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; /// -/// Specifies exif data types. +/// Specifies Exif data types. /// public enum ExifDataType { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs index e9f46731c8..d2b88cbfff 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs @@ -16,13 +16,13 @@ internal static class ExifEncodedStringHelpers private const ulong UnicodeCode = 0x_45_44_4F_43_49_4E_55; private const ulong UndefinedCode = 0x_00_00_00_00_00_00_00_00; - private static ReadOnlySpan AsciiCodeBytes => new byte[] { 0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0 }; + private static ReadOnlySpan AsciiCodeBytes => [0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0]; - private static ReadOnlySpan JISCodeBytes => new byte[] { 0x4A, 0x49, 0x53, 0, 0, 0, 0, 0 }; + private static ReadOnlySpan JISCodeBytes => [0x4A, 0x49, 0x53, 0, 0, 0, 0, 0]; - private static ReadOnlySpan UnicodeCodeBytes => new byte[] { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0 }; + private static ReadOnlySpan UnicodeCodeBytes => [0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0]; - private static ReadOnlySpan UndefinedCodeBytes => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + private static ReadOnlySpan UndefinedCodeBytes => [0, 0, 0, 0, 0, 0, 0, 0]; // 20932 EUC-JP Japanese (JIS 0208-1990 and 0212-1990) // https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-6.0 @@ -50,22 +50,45 @@ private static Encoding JIS0208Encoding _ => UndefinedCodeBytes }; - public static Encoding GetEncoding(CharacterCode code) => code switch + public static Encoding GetEncoding(CharacterCode code, ByteOrder order) => code switch { CharacterCode.ASCII => Encoding.ASCII, CharacterCode.JIS => JIS0208Encoding, - CharacterCode.Unicode => Encoding.Unicode, + CharacterCode.Unicode => order is ByteOrder.BigEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, CharacterCode.Undefined => Encoding.UTF8, _ => Encoding.UTF8 }; - public static bool TryParse(ReadOnlySpan buffer, out EncodedString encodedString) + public static bool TryParse(ReadOnlySpan buffer, ByteOrder order, out EncodedString encodedString) { if (TryDetect(buffer, out CharacterCode code)) { - string text = GetEncoding(code).GetString(buffer[CharacterCodeBytesLength..]); - encodedString = new EncodedString(code, text); - return true; + ReadOnlySpan textBuffer = buffer[CharacterCodeBytesLength..]; + if (code == CharacterCode.Unicode && textBuffer.Length >= 2) + { + // Check BOM + if (textBuffer.StartsWith((ReadOnlySpan)[0xFF, 0xFE])) + { + // Little-endian BOM + string text = Encoding.Unicode.GetString(textBuffer[2..]); + encodedString = new EncodedString(code, text); + return true; + } + + if (textBuffer.StartsWith((ReadOnlySpan)[0xFE, 0xFF])) + { + // Big-endian BOM + string text = Encoding.BigEndianUnicode.GetString(textBuffer[2..]); + encodedString = new EncodedString(code, text); + return true; + } + } + + { + string text = GetEncoding(code, order).GetString(textBuffer); + encodedString = new EncodedString(code, text); + return true; + } } encodedString = default; @@ -73,14 +96,14 @@ public static bool TryParse(ReadOnlySpan buffer, out EncodedString encoded } public static uint GetDataLength(EncodedString encodedString) => - (uint)GetEncoding(encodedString.Code).GetByteCount(encodedString.Text) + CharacterCodeBytesLength; + (uint)GetEncoding(encodedString.Code, ByteOrder.LittleEndian).GetByteCount(encodedString.Text) + CharacterCodeBytesLength; public static int Write(EncodedString encodedString, Span destination) { GetCodeBytes(encodedString.Code).CopyTo(destination); string text = encodedString.Text; - int count = Write(GetEncoding(encodedString.Code), text, destination[CharacterCodeBytesLength..]); + int count = Write(GetEncoding(encodedString.Code, ByteOrder.LittleEndian), text, destination[CharacterCodeBytesLength..]); return CharacterCodeBytesLength + count; } @@ -92,8 +115,7 @@ private static bool TryDetect(ReadOnlySpan buffer, out CharacterCode code) { if (buffer.Length >= CharacterCodeBytesLength) { - ulong test = BinaryPrimitives.ReadUInt64LittleEndian(buffer); - switch (test) + switch (BinaryPrimitives.ReadUInt64LittleEndian(buffer)) { case AsciiCode: code = CharacterCode.ASCII; @@ -108,7 +130,8 @@ private static bool TryDetect(ReadOnlySpan buffer, out CharacterCode code) code = CharacterCode.Undefined; return true; default: - break; + code = default; + return false; } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index dd5792ae79..6cd16aa9c2 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -47,7 +49,7 @@ public ExifProfile(byte[]? data) { this.Parts = ExifParts.All; this.data = data; - this.InvalidTags = Array.Empty(); + this.InvalidTags = []; } /// @@ -160,17 +162,15 @@ public bool TryCreateThumbnail([NotNullWhen(true)] out Image? im return false; } - using (MemoryStream memStream = new(this.data, this.thumbnailOffset, this.thumbnailLength)) - { - image = Image.Load(memStream); - return true; - } + using MemoryStream memStream = new(this.data, this.thumbnailOffset, this.thumbnailLength); + image = Image.Load(memStream); + return true; } /// /// Returns the value with the specified tag. /// - /// The tag of the exif value. + /// The tag of the Exif value. /// The value with the specified tag. /// True when found, otherwise false /// The data type of the tag. @@ -214,26 +214,33 @@ public bool RemoveValue(ExifTag tag) /// /// Sets the value of the specified tag. /// - /// The tag of the exif value. + /// The tag of the Exif value. /// The value. /// The data type of the tag. public void SetValue(ExifTag tag, TValueType value) => this.SetValueInternal(tag, value); /// - /// Converts this instance to a byte array. + /// Converts the sections selected by to a byte array. /// /// The public byte[]? ToByteArray() { if (this.values is null) { - return this.data; + // The original bytes include every section. They can only be reused when no filtering + // is requested; otherwise lazy profiles must go through the same writer as initialized ones. + if (this.Parts == ExifParts.All) + { + return this.data; + } + + this.InitializeValues(); } if (this.values.Count == 0) { - return Array.Empty(); + return []; } ExifWriter writer = new(this.values, this.Parts); @@ -246,7 +253,7 @@ public void SetValue(ExifTag tag, TValueType value) /// /// Returns the value with the specified tag. /// - /// The tag of the exif value. + /// The tag of the Exif value. /// The value with the specified tag. internal IExifValue? GetValueInternal(ExifTag tag) { @@ -264,9 +271,9 @@ public void SetValue(ExifTag tag, TValueType value) /// /// Sets the value of the specified tag. /// - /// The tag of the exif value. + /// The tag of the Exif value. /// The value. - /// Newly created value is null. + /// The newly created value is null. internal void SetValueInternal(ExifTag tag, object? value) { foreach (IExifValue exifValue in this.Values) @@ -278,11 +285,7 @@ internal void SetValueInternal(ExifTag tag, object? value) } } - ExifValue? newExifValue = ExifValues.Create(tag); - if (newExifValue is null) - { - throw new NotSupportedException($"Newly created value for tag {tag} is null."); - } + ExifValue? newExifValue = ExifValues.Create(tag) ?? throw new NotSupportedException($"Newly created value for tag {tag} is null."); newExifValue.TrySetValue(value); this.values.Add(newExifValue); @@ -298,6 +301,84 @@ internal void Sync(ImageMetadata metadata) this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } + internal void SyncDimensions(int width, int height) + { + if (this.TryGetValue(ExifTag.PixelXDimension, out _)) + { + this.SetValue(ExifTag.PixelXDimension, width); + } + + if (this.TryGetValue(ExifTag.PixelYDimension, out _)) + { + this.SetValue(ExifTag.PixelYDimension, height); + } + } + + internal void SyncSubject(int width, int height, Matrix4x4 matrix) + { + if (matrix.IsIdentity) + { + return; + } + + if (this.TryGetValue(ExifTag.SubjectLocation, out IExifValue? location)) + { + if (location.Value?.Length == 2) + { + Vector2 point = TransformUtilities.ProjectiveTransform2D(location.Value[0], location.Value[1], matrix); + + // Ensure the point is within the image dimensions. + point = Vector2.Clamp(point, Vector2.Zero, new Vector2(width - 1, height - 1)); + + // Floor the point to the nearest pixel. + location.Value[0] = (ushort)Math.Floor(point.X); + location.Value[1] = (ushort)Math.Floor(point.Y); + + this.SetValue(ExifTag.SubjectLocation, location.Value); + } + else + { + this.RemoveValue(ExifTag.SubjectLocation); + } + } + + if (this.TryGetValue(ExifTag.SubjectArea, out IExifValue? area)) + { + if (area.Value?.Length == 4) + { + RectangleF rectangle = new(area.Value[0], area.Value[1], area.Value[2], area.Value[3]); + if (!TransformUtilities.TryGetTransformedRectangle(rectangle, matrix, out RectangleF bounds)) + { + return; + } + + // Ensure the bounds are within the image dimensions. + bounds = RectangleF.Intersect(bounds, new Rectangle(0, 0, width, height)); + + area.Value[0] = (ushort)MathF.Floor(bounds.X); + area.Value[1] = (ushort)MathF.Floor(bounds.Y); + area.Value[2] = (ushort)MathF.Ceiling(bounds.Width); + area.Value[3] = (ushort)MathF.Ceiling(bounds.Height); + this.SetValue(ExifTag.SubjectArea, area.Value); + } + else + { + this.RemoveValue(ExifTag.SubjectArea); + } + } + } + + /// + /// 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)) @@ -324,7 +405,7 @@ private void InitializeValues() if (this.data is null) { - this.values = new List(); + this.values = []; return; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 953ef74afb..4e2315cd88 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using SixLabors.ImageSharp.Memory; @@ -33,7 +34,7 @@ public ExifReader(byte[] exifData, MemoryAllocator? allocator) /// public List ReadValues() { - List values = new(); + List values = []; // II == 0x4949 this.IsBigEndian = this.ReadUInt16() != 0x4949; @@ -63,7 +64,7 @@ private void GetThumbnail(uint offset) return; } - List values = new(); + List values = []; this.ReadValues(values, offset); for (int i = 0; i < values.Count; i++) @@ -89,8 +90,8 @@ internal abstract class BaseExifReader private readonly MemoryAllocator? allocator; private readonly Stream data; private List? invalidTags; - private List? subIfds; + private bool isBigEndian; protected BaseExifReader(Stream stream, MemoryAllocator? allocator) { @@ -103,7 +104,7 @@ protected BaseExifReader(Stream stream, MemoryAllocator? allocator) /// /// Gets the invalid tags. /// - public IReadOnlyList InvalidTags => this.invalidTags ?? (IReadOnlyList)Array.Empty(); + public IReadOnlyList InvalidTags => this.invalidTags ?? (IReadOnlyList)[]; /// /// Gets or sets the thumbnail length in the byte stream. @@ -115,9 +116,19 @@ protected BaseExifReader(Stream stream, MemoryAllocator? allocator) /// public uint ThumbnailOffset { get; protected set; } - public bool IsBigEndian { get; protected set; } + public bool IsBigEndian + { + get => this.isBigEndian; + protected set + { + this.isBigEndian = value; + this.ByteOrder = value ? ByteOrder.BigEndian : ByteOrder.LittleEndian; + } + } - public List<(ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif)> BigValues { get; } = new(); + protected ByteOrder ByteOrder { get; private set; } + + public List<(ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif)> BigValues { get; } = []; protected void ReadBigValues(List values) { @@ -187,11 +198,21 @@ protected void ReadValues(List values, uint offset) protected void ReadSubIfd(List values) { - if (this.subIfds is not null) + if (this.subIfds != null) { - foreach (ulong subIfdOffset in this.subIfds) + const int maxSubIfds = 8; + const int maxNestingLevel = 8; + Span buf = stackalloc ulong[maxSubIfds]; + for (int i = 0; i < maxNestingLevel && this.subIfds.Count > 0; i++) { - this.ReadValues(values, (uint)subIfdOffset); + int sz = Math.Min(this.subIfds.Count, maxSubIfds); + CollectionsMarshal.AsSpan(this.subIfds)[..sz].CopyTo(buf); + + this.subIfds.Clear(); + foreach (ulong subIfdOffset in buf[..sz]) + { + this.ReadValues(values, (uint)subIfdOffset); + } } } } @@ -203,6 +224,13 @@ protected void ReadValues64(List values, ulong offset) this.Seek(offset); ulong count = this.ReadUInt64(); + // Each entry occupies 20 bytes and the directory ends with an 8-byte next-IFD offset. + long remainingDirectoryBytes = this.data.Length - this.data.Position; + if (remainingDirectoryBytes < 8 || count > (ulong)((remainingDirectoryBytes - 8) / 20)) + { + throw new InvalidImageContentException("The BigTIFF directory entry count exceeds the available data."); + } + Span offsetBuffer = stackalloc byte[8]; for (ulong i = 0; i < count; i++) { @@ -447,6 +475,7 @@ private void ReadValue64(List values, Span offsetBuffer) ExifTagValue.TileByteCounts => new ExifLong8Array(ExifTagValue.TileByteCounts), _ => ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents), }; + if (exifValue is null) { this.AddInvalidTag(new UnkownExifTag(tag)); @@ -472,17 +501,25 @@ private void ReadValue64(List values, Span offsetBuffer) } } - private void Add(IList values, IExifValue exif, object? value) + private void Add(IList values, ExifValue exif, object? value) { - if (!exif.TrySetValue(value)) + if (exif is ExifEncodedString encodedString) + { + if (!encodedString.TrySetValue(value, this.ByteOrder)) + { + return; + } + } + else if (!exif.TrySetValue(value)) { return; } foreach (IExifValue val in values) { - // Sometimes duplicates appear, can compare val.Tag == exif.Tag - if (val == exif) + // To skip duplicates must be used Equals method, + // == operator not defined for ExifValue and IExifValue + if (exif.Equals(val)) { Debug.WriteLine($"Duplicate Exif tag: tag={exif.Tag}, dataType={exif.DataType}"); return; @@ -504,10 +541,10 @@ private void Add(IList values, IExifValue exif, object? value) } private void AddInvalidTag(ExifTag tag) - => (this.invalidTags ??= new List()).Add(tag); + => (this.invalidTags ??= []).Add(tag); private void AddSubIfd(object? val) - => (this.subIfds ??= new List()).Add(Convert.ToUInt64(val, CultureInfo.InvariantCulture)); + => (this.subIfds ??= []).Add(Convert.ToUInt64(val, CultureInfo.InvariantCulture)); private void Seek(ulong pos) => this.data.Seek((long)pos, SeekOrigin.Begin); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 1d2dca8700..659df01a65 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -55,7 +55,7 @@ public byte[] GetData() if (length == 0) { - return Array.Empty(); + return []; } // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total @@ -197,7 +197,7 @@ private static int WriteInt32(int value, Span destination, int offset) private List GetPartValues(ExifParts part) { - List result = new(); + List result = []; if (!EnumUtils.HasFlag(this.allowedParts, part)) { @@ -241,7 +241,7 @@ private static bool HasValue(IExifValue exifValue) return true; } - private static uint GetLength(IList values) + private static uint GetLength(List values) { if (values.Count == 0) { @@ -332,7 +332,7 @@ private int WriteData(uint startIndex, List values, Span desti private int WriteHeaders(List values, Span destination, int offset) { - this.dataOffsets = new List(); + this.dataOffsets = []; int newOffset = WriteUInt16((ushort)values.Count, destination, offset); @@ -403,6 +403,13 @@ private static int WriteValue(ExifDataType dataType, object value, Span de return WriteUInt32((uint)longNumber, destination, offset); } + // ExifLong8Array retains ulong storage but reports Long when every value fits + // in 32 bits, allowing BigTIFF offsets to be serialized by classic EXIF writers. + if (value is ulong long8Value) + { + return WriteUInt32((uint)long8Value, destination, offset); + } + return WriteUInt32((uint)value, destination, offset); case ExifDataType.Long8: return WriteUInt64((ulong)value, destination, offset); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs index 9ee2cf2f45..ff74ecd19d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs @@ -9,15 +9,15 @@ public abstract partial class ExifTag /// /// Gets the FaxProfile exif tag. /// - public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile); + public static ExifTag FaxProfile { get; } = new(ExifTagValue.FaxProfile); /// /// Gets the ModeNumber exif tag. /// - public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber); + public static ExifTag ModeNumber { get; } = new(ExifTagValue.ModeNumber); /// /// Gets the GPSAltitudeRef exif tag. /// - public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef); + public static ExifTag GPSAltitudeRef { get; } = new(ExifTagValue.GPSAltitudeRef); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs index 00a9056d34..64d8e14371 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs @@ -9,40 +9,40 @@ public abstract partial class ExifTag /// /// Gets the ClipPath exif tag. /// - public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath); + public static ExifTag ClipPath => new(ExifTagValue.ClipPath); /// /// Gets the VersionYear exif tag. /// - public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear); + public static ExifTag VersionYear => new(ExifTagValue.VersionYear); /// /// Gets the XMP exif tag. /// - public static ExifTag XMP => new ExifTag(ExifTagValue.XMP); + public static ExifTag XMP => new(ExifTagValue.XMP); /// /// Gets the IPTC exif tag. /// - public static ExifTag IPTC => new ExifTag(ExifTagValue.IPTC); + public static ExifTag IPTC => new(ExifTagValue.IPTC); /// /// Gets the IccProfile exif tag. /// - public static ExifTag IccProfile => new ExifTag(ExifTagValue.IccProfile); + public static ExifTag IccProfile => new(ExifTagValue.IccProfile); /// /// Gets the CFAPattern2 exif tag. /// - public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2); + public static ExifTag CFAPattern2 => new(ExifTagValue.CFAPattern2); /// /// Gets the TIFFEPStandardID exif tag. /// - public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID); + public static ExifTag TIFFEPStandardID => new(ExifTagValue.TIFFEPStandardID); /// /// Gets the GPSVersionID exif tag. /// - public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID); + public static ExifTag GPSVersionID => new(ExifTagValue.GPSVersionID); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs index 91d0c97b38..fded122613 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs @@ -9,20 +9,20 @@ public abstract partial class ExifTag /// /// Gets the PixelScale exif tag. /// - public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale); + public static ExifTag PixelScale { get; } = new(ExifTagValue.PixelScale); /// /// Gets the IntergraphMatrix exif tag. /// - public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix); + public static ExifTag IntergraphMatrix { get; } = new(ExifTagValue.IntergraphMatrix); /// /// Gets the ModelTiePoint exif tag. /// - public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint); + public static ExifTag ModelTiePoint { get; } = new(ExifTagValue.ModelTiePoint); /// /// Gets the ModelTransform exif tag. /// - public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform); + public static ExifTag ModelTransform { get; } = new(ExifTagValue.ModelTransform); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs index 4b53ba6360..4cac0d0cab 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.EncodedString.cs @@ -9,15 +9,15 @@ public abstract partial class ExifTag /// /// Gets the UserComment exif tag. /// - public static ExifTag UserComment { get; } = new ExifTag(ExifTagValue.UserComment); + public static ExifTag UserComment { get; } = new(ExifTagValue.UserComment); /// /// Gets the GPSProcessingMethod exif tag. /// - public static ExifTag GPSProcessingMethod { get; } = new ExifTag(ExifTagValue.GPSProcessingMethod); + public static ExifTag GPSProcessingMethod { get; } = new(ExifTagValue.GPSProcessingMethod); /// /// Gets the GPSAreaInformation exif tag. /// - public static ExifTag GPSAreaInformation { get; } = new ExifTag(ExifTagValue.GPSAreaInformation); + public static ExifTag GPSAreaInformation { get; } = new(ExifTagValue.GPSAreaInformation); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs index f6c7c8ea73..5f9dfb4cde 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs @@ -9,105 +9,105 @@ public abstract partial class ExifTag /// /// Gets the SubfileType exif tag. /// - public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType); + public static ExifTag SubfileType { get; } = new(ExifTagValue.SubfileType); /// /// Gets the SubIFDOffset exif tag. /// - public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset); + public static ExifTag SubIFDOffset { get; } = new(ExifTagValue.SubIFDOffset); /// /// Gets the GPSIFDOffset exif tag. /// - public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset); + public static ExifTag GPSIFDOffset { get; } = new(ExifTagValue.GPSIFDOffset); /// /// Gets the T4Options exif tag. /// - public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options); + public static ExifTag T4Options { get; } = new(ExifTagValue.T4Options); /// /// Gets the T6Options exif tag. /// - public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options); + public static ExifTag T6Options { get; } = new(ExifTagValue.T6Options); /// /// Gets the XClipPathUnits exif tag. /// - public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits); + public static ExifTag XClipPathUnits { get; } = new(ExifTagValue.XClipPathUnits); /// /// Gets the YClipPathUnits exif tag. /// - public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits); + public static ExifTag YClipPathUnits { get; } = new(ExifTagValue.YClipPathUnits); /// /// Gets the ProfileType exif tag. /// - public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType); + public static ExifTag ProfileType { get; } = new(ExifTagValue.ProfileType); /// /// Gets the CodingMethods exif tag. /// - public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods); + public static ExifTag CodingMethods { get; } = new(ExifTagValue.CodingMethods); /// /// Gets the T82ptions exif tag. /// - public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions); + public static ExifTag T82ptions { get; } = new(ExifTagValue.T82ptions); /// /// Gets the JPEGInterchangeFormat exif tag. /// - public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat); + public static ExifTag JPEGInterchangeFormat { get; } = new(ExifTagValue.JPEGInterchangeFormat); /// /// Gets the JPEGInterchangeFormatLength exif tag. /// - public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength); + public static ExifTag JPEGInterchangeFormatLength { get; } = new(ExifTagValue.JPEGInterchangeFormatLength); /// /// Gets the MDFileTag exif tag. /// - public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag); + public static ExifTag MDFileTag { get; } = new(ExifTagValue.MDFileTag); /// /// Gets the StandardOutputSensitivity exif tag. /// - public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity); + public static ExifTag StandardOutputSensitivity { get; } = new(ExifTagValue.StandardOutputSensitivity); /// /// Gets the RecommendedExposureIndex exif tag. /// - public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex); + public static ExifTag RecommendedExposureIndex { get; } = new(ExifTagValue.RecommendedExposureIndex); /// /// Gets the ISOSpeed exif tag. /// - public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed); + public static ExifTag ISOSpeed { get; } = new(ExifTagValue.ISOSpeed); /// /// Gets the ISOSpeedLatitudeyyy exif tag. /// - public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy); + public static ExifTag ISOSpeedLatitudeyyy { get; } = new(ExifTagValue.ISOSpeedLatitudeyyy); /// /// Gets the ISOSpeedLatitudezzz exif tag. /// - public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz); + public static ExifTag ISOSpeedLatitudezzz { get; } = new(ExifTagValue.ISOSpeedLatitudezzz); /// /// Gets the FaxRecvParams exif tag. /// - public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams); + public static ExifTag FaxRecvParams { get; } = new(ExifTagValue.FaxRecvParams); /// /// Gets the FaxRecvTime exif tag. /// - public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime); + public static ExifTag FaxRecvTime { get; } = new(ExifTagValue.FaxRecvTime); /// /// Gets the ImageNumber exif tag. /// - public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber); + public static ExifTag ImageNumber { get; } = new(ExifTagValue.ImageNumber); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index 4767ca852e..e6821651ac 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -9,55 +9,55 @@ public abstract partial class ExifTag /// /// Gets the FreeOffsets exif tag. /// - public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets); + public static ExifTag FreeOffsets { get; } = new(ExifTagValue.FreeOffsets); /// /// Gets the FreeByteCounts exif tag. /// - public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts); + public static ExifTag FreeByteCounts { get; } = new(ExifTagValue.FreeByteCounts); /// /// Gets the ColorResponseUnit exif tag. /// - public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit); + public static ExifTag ColorResponseUnit { get; } = new(ExifTagValue.ColorResponseUnit); /// /// Gets the SMinSampleValue exif tag. /// - public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue); + public static ExifTag SMinSampleValue { get; } = new(ExifTagValue.SMinSampleValue); /// /// Gets the SMaxSampleValue exif tag. /// - public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue); + public static ExifTag SMaxSampleValue { get; } = new(ExifTagValue.SMaxSampleValue); /// /// Gets the JPEGQTables exif tag. /// - public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables); + public static ExifTag JPEGQTables { get; } = new(ExifTagValue.JPEGQTables); /// /// Gets the JPEGDCTables exif tag. /// - public static ExifTag JPEGDCTables { get; } = new ExifTag(ExifTagValue.JPEGDCTables); + public static ExifTag JPEGDCTables { get; } = new(ExifTagValue.JPEGDCTables); /// /// Gets the JPEGACTables exif tag. /// - public static ExifTag JPEGACTables { get; } = new ExifTag(ExifTagValue.JPEGACTables); + public static ExifTag JPEGACTables { get; } = new(ExifTagValue.JPEGACTables); /// /// Gets the StripRowCounts exif tag. /// - public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts); + public static ExifTag StripRowCounts { get; } = new(ExifTagValue.StripRowCounts); /// /// Gets the IntergraphRegisters exif tag. /// - public static ExifTag IntergraphRegisters { get; } = new ExifTag(ExifTagValue.IntergraphRegisters); + public static ExifTag IntergraphRegisters { get; } = new(ExifTagValue.IntergraphRegisters); /// /// Gets the offset to child IFDs exif tag. /// - public static ExifTag SubIFDs { get; } = new ExifTag(ExifTagValue.SubIFDs); + public static ExifTag SubIFDs { get; } = new(ExifTagValue.SubIFDs); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs index 2018e4cea0..e023beeb7a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs @@ -9,45 +9,45 @@ public abstract partial class ExifTag /// /// Gets the ImageWidth exif tag. /// - public static ExifTag ImageWidth { get; } = new ExifTag(ExifTagValue.ImageWidth); + public static ExifTag ImageWidth { get; } = new(ExifTagValue.ImageWidth); /// /// Gets the ImageLength exif tag. /// - public static ExifTag ImageLength { get; } = new ExifTag(ExifTagValue.ImageLength); + public static ExifTag ImageLength { get; } = new(ExifTagValue.ImageLength); /// /// Gets the RowsPerStrip exif tag. /// - public static ExifTag RowsPerStrip { get; } = new ExifTag(ExifTagValue.RowsPerStrip); + public static ExifTag RowsPerStrip { get; } = new(ExifTagValue.RowsPerStrip); /// /// Gets the TileWidth exif tag. /// - public static ExifTag TileWidth { get; } = new ExifTag(ExifTagValue.TileWidth); + public static ExifTag TileWidth { get; } = new(ExifTagValue.TileWidth); /// /// Gets the TileLength exif tag. /// - public static ExifTag TileLength { get; } = new ExifTag(ExifTagValue.TileLength); + public static ExifTag TileLength { get; } = new(ExifTagValue.TileLength); /// /// Gets the BadFaxLines exif tag. /// - public static ExifTag BadFaxLines { get; } = new ExifTag(ExifTagValue.BadFaxLines); + public static ExifTag BadFaxLines { get; } = new(ExifTagValue.BadFaxLines); /// /// Gets the ConsecutiveBadFaxLines exif tag. /// - public static ExifTag ConsecutiveBadFaxLines { get; } = new ExifTag(ExifTagValue.ConsecutiveBadFaxLines); + public static ExifTag ConsecutiveBadFaxLines { get; } = new(ExifTagValue.ConsecutiveBadFaxLines); /// /// Gets the PixelXDimension exif tag. /// - public static ExifTag PixelXDimension { get; } = new ExifTag(ExifTagValue.PixelXDimension); + public static ExifTag PixelXDimension { get; } = new(ExifTagValue.PixelXDimension); /// /// Gets the PixelYDimension exif tag. /// - public static ExifTag PixelYDimension { get; } = new ExifTag(ExifTagValue.PixelYDimension); + public static ExifTag PixelYDimension { get; } = new(ExifTagValue.PixelYDimension); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs index cd89681413..6a7438fc6e 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs @@ -9,25 +9,25 @@ public abstract partial class ExifTag /// /// Gets the StripOffsets exif tag. /// - public static ExifTag StripOffsets { get; } = new ExifTag(ExifTagValue.StripOffsets); + public static ExifTag StripOffsets { get; } = new(ExifTagValue.StripOffsets); /// /// Gets the StripByteCounts exif tag. /// - public static ExifTag StripByteCounts { get; } = new ExifTag(ExifTagValue.StripByteCounts); + public static ExifTag StripByteCounts { get; } = new(ExifTagValue.StripByteCounts); /// /// Gets the TileByteCounts exif tag. /// - public static ExifTag TileByteCounts { get; } = new ExifTag(ExifTagValue.TileByteCounts); + public static ExifTag TileByteCounts { get; } = new(ExifTagValue.TileByteCounts); /// /// Gets the TileOffsets exif tag. /// - public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets); + public static ExifTag TileOffsets { get; } = new(ExifTagValue.TileOffsets); /// /// Gets the ImageLayer exif tag. /// - public static ExifTag ImageLayer { get; } = new ExifTag(ExifTagValue.ImageLayer); + public static ExifTag ImageLayer { get; } = new(ExifTagValue.ImageLayer); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs index e4fe13fe57..02526ac34d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs @@ -9,165 +9,165 @@ public abstract partial class ExifTag /// /// Gets the XPosition exif tag. /// - public static ExifTag XPosition { get; } = new ExifTag(ExifTagValue.XPosition); + public static ExifTag XPosition { get; } = new(ExifTagValue.XPosition); /// /// Gets the YPosition exif tag. /// - public static ExifTag YPosition { get; } = new ExifTag(ExifTagValue.YPosition); + public static ExifTag YPosition { get; } = new(ExifTagValue.YPosition); /// /// Gets the XResolution exif tag. /// - public static ExifTag XResolution { get; } = new ExifTag(ExifTagValue.XResolution); + public static ExifTag XResolution { get; } = new(ExifTagValue.XResolution); /// /// Gets the YResolution exif tag. /// - public static ExifTag YResolution { get; } = new ExifTag(ExifTagValue.YResolution); + public static ExifTag YResolution { get; } = new(ExifTagValue.YResolution); /// /// Gets the BatteryLevel exif tag. /// - public static ExifTag BatteryLevel { get; } = new ExifTag(ExifTagValue.BatteryLevel); + public static ExifTag BatteryLevel { get; } = new(ExifTagValue.BatteryLevel); /// /// Gets the ExposureTime exif tag. /// - public static ExifTag ExposureTime { get; } = new ExifTag(ExifTagValue.ExposureTime); + public static ExifTag ExposureTime { get; } = new(ExifTagValue.ExposureTime); /// /// Gets the FNumber exif tag. /// - public static ExifTag FNumber { get; } = new ExifTag(ExifTagValue.FNumber); + public static ExifTag FNumber { get; } = new(ExifTagValue.FNumber); /// /// Gets the MDScalePixel exif tag. /// - public static ExifTag MDScalePixel { get; } = new ExifTag(ExifTagValue.MDScalePixel); + public static ExifTag MDScalePixel { get; } = new(ExifTagValue.MDScalePixel); /// /// Gets the CompressedBitsPerPixel exif tag. /// - public static ExifTag CompressedBitsPerPixel { get; } = new ExifTag(ExifTagValue.CompressedBitsPerPixel); + public static ExifTag CompressedBitsPerPixel { get; } = new(ExifTagValue.CompressedBitsPerPixel); /// /// Gets the ApertureValue exif tag. /// - public static ExifTag ApertureValue { get; } = new ExifTag(ExifTagValue.ApertureValue); + public static ExifTag ApertureValue { get; } = new(ExifTagValue.ApertureValue); /// /// Gets the MaxApertureValue exif tag. /// - public static ExifTag MaxApertureValue { get; } = new ExifTag(ExifTagValue.MaxApertureValue); + public static ExifTag MaxApertureValue { get; } = new(ExifTagValue.MaxApertureValue); /// /// Gets the SubjectDistance exif tag. /// - public static ExifTag SubjectDistance { get; } = new ExifTag(ExifTagValue.SubjectDistance); + public static ExifTag SubjectDistance { get; } = new(ExifTagValue.SubjectDistance); /// /// Gets the FocalLength exif tag. /// - public static ExifTag FocalLength { get; } = new ExifTag(ExifTagValue.FocalLength); + public static ExifTag FocalLength { get; } = new(ExifTagValue.FocalLength); /// /// Gets the FlashEnergy2 exif tag. /// - public static ExifTag FlashEnergy2 { get; } = new ExifTag(ExifTagValue.FlashEnergy2); + public static ExifTag FlashEnergy2 { get; } = new(ExifTagValue.FlashEnergy2); /// /// Gets the FocalPlaneXResolution2 exif tag. /// - public static ExifTag FocalPlaneXResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution2); + public static ExifTag FocalPlaneXResolution2 { get; } = new(ExifTagValue.FocalPlaneXResolution2); /// /// Gets the FocalPlaneYResolution2 exif tag. /// - public static ExifTag FocalPlaneYResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution2); + public static ExifTag FocalPlaneYResolution2 { get; } = new(ExifTagValue.FocalPlaneYResolution2); /// /// Gets the ExposureIndex2 exif tag. /// - public static ExifTag ExposureIndex2 { get; } = new ExifTag(ExifTagValue.ExposureIndex2); + public static ExifTag ExposureIndex2 { get; } = new(ExifTagValue.ExposureIndex2); /// /// Gets the Humidity exif tag. /// - public static ExifTag Humidity { get; } = new ExifTag(ExifTagValue.Humidity); + public static ExifTag Humidity { get; } = new(ExifTagValue.Humidity); /// /// Gets the Pressure exif tag. /// - public static ExifTag Pressure { get; } = new ExifTag(ExifTagValue.Pressure); + public static ExifTag Pressure { get; } = new(ExifTagValue.Pressure); /// /// Gets the Acceleration exif tag. /// - public static ExifTag Acceleration { get; } = new ExifTag(ExifTagValue.Acceleration); + public static ExifTag Acceleration { get; } = new(ExifTagValue.Acceleration); /// /// Gets the FlashEnergy exif tag. /// - public static ExifTag FlashEnergy { get; } = new ExifTag(ExifTagValue.FlashEnergy); + public static ExifTag FlashEnergy { get; } = new(ExifTagValue.FlashEnergy); /// /// Gets the FocalPlaneXResolution exif tag. /// - public static ExifTag FocalPlaneXResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution); + public static ExifTag FocalPlaneXResolution { get; } = new(ExifTagValue.FocalPlaneXResolution); /// /// Gets the FocalPlaneYResolution exif tag. /// - public static ExifTag FocalPlaneYResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution); + public static ExifTag FocalPlaneYResolution { get; } = new(ExifTagValue.FocalPlaneYResolution); /// /// Gets the ExposureIndex exif tag. /// - public static ExifTag ExposureIndex { get; } = new ExifTag(ExifTagValue.ExposureIndex); + public static ExifTag ExposureIndex { get; } = new(ExifTagValue.ExposureIndex); /// /// Gets the DigitalZoomRatio exif tag. /// - public static ExifTag DigitalZoomRatio { get; } = new ExifTag(ExifTagValue.DigitalZoomRatio); + public static ExifTag DigitalZoomRatio { get; } = new(ExifTagValue.DigitalZoomRatio); /// /// Gets the GPSAltitude exif tag. /// - public static ExifTag GPSAltitude { get; } = new ExifTag(ExifTagValue.GPSAltitude); + public static ExifTag GPSAltitude { get; } = new(ExifTagValue.GPSAltitude); /// /// Gets the GPSDOP exif tag. /// - public static ExifTag GPSDOP { get; } = new ExifTag(ExifTagValue.GPSDOP); + public static ExifTag GPSDOP { get; } = new(ExifTagValue.GPSDOP); /// /// Gets the GPSSpeed exif tag. /// - public static ExifTag GPSSpeed { get; } = new ExifTag(ExifTagValue.GPSSpeed); + public static ExifTag GPSSpeed { get; } = new(ExifTagValue.GPSSpeed); /// /// Gets the GPSTrack exif tag. /// - public static ExifTag GPSTrack { get; } = new ExifTag(ExifTagValue.GPSTrack); + public static ExifTag GPSTrack { get; } = new(ExifTagValue.GPSTrack); /// /// Gets the GPSImgDirection exif tag. /// - public static ExifTag GPSImgDirection { get; } = new ExifTag(ExifTagValue.GPSImgDirection); + public static ExifTag GPSImgDirection { get; } = new(ExifTagValue.GPSImgDirection); /// /// Gets the GPSDestBearing exif tag. /// - public static ExifTag GPSDestBearing { get; } = new ExifTag(ExifTagValue.GPSDestBearing); + public static ExifTag GPSDestBearing { get; } = new(ExifTagValue.GPSDestBearing); /// /// Gets the GPSDestDistance exif tag. /// - public static ExifTag GPSDestDistance { get; } = new ExifTag(ExifTagValue.GPSDestDistance); + public static ExifTag GPSDestDistance { get; } = new(ExifTagValue.GPSDestDistance); /// /// Gets the GPSHPositioningError exif tag. /// - public static ExifTag GPSHPositioningError { get; } = new ExifTag(ExifTagValue.GPSHPositioningError); + public static ExifTag GPSHPositioningError { get; } = new(ExifTagValue.GPSHPositioningError); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs index 8b764c3f77..4ba140441d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs @@ -9,50 +9,50 @@ public abstract partial class ExifTag /// /// Gets the WhitePoint exif tag. /// - public static ExifTag WhitePoint { get; } = new ExifTag(ExifTagValue.WhitePoint); + public static ExifTag WhitePoint { get; } = new(ExifTagValue.WhitePoint); /// /// Gets the PrimaryChromaticities exif tag. /// - public static ExifTag PrimaryChromaticities { get; } = new ExifTag(ExifTagValue.PrimaryChromaticities); + public static ExifTag PrimaryChromaticities { get; } = new(ExifTagValue.PrimaryChromaticities); /// /// Gets the YCbCrCoefficients exif tag. /// - public static ExifTag YCbCrCoefficients { get; } = new ExifTag(ExifTagValue.YCbCrCoefficients); + public static ExifTag YCbCrCoefficients { get; } = new(ExifTagValue.YCbCrCoefficients); /// /// Gets the ReferenceBlackWhite exif tag. /// - public static ExifTag ReferenceBlackWhite { get; } = new ExifTag(ExifTagValue.ReferenceBlackWhite); + public static ExifTag ReferenceBlackWhite { get; } = new(ExifTagValue.ReferenceBlackWhite); /// /// Gets the GPSLatitude exif tag. /// - public static ExifTag GPSLatitude { get; } = new ExifTag(ExifTagValue.GPSLatitude); + public static ExifTag GPSLatitude { get; } = new(ExifTagValue.GPSLatitude); /// /// Gets the GPSLongitude exif tag. /// - public static ExifTag GPSLongitude { get; } = new ExifTag(ExifTagValue.GPSLongitude); + public static ExifTag GPSLongitude { get; } = new(ExifTagValue.GPSLongitude); /// /// Gets the GPSTimestamp exif tag. /// - public static ExifTag GPSTimestamp { get; } = new ExifTag(ExifTagValue.GPSTimestamp); + public static ExifTag GPSTimestamp { get; } = new(ExifTagValue.GPSTimestamp); /// /// Gets the GPSDestLatitude exif tag. /// - public static ExifTag GPSDestLatitude { get; } = new ExifTag(ExifTagValue.GPSDestLatitude); + public static ExifTag GPSDestLatitude { get; } = new(ExifTagValue.GPSDestLatitude); /// /// Gets the GPSDestLongitude exif tag. /// - public static ExifTag GPSDestLongitude { get; } = new ExifTag(ExifTagValue.GPSDestLongitude); + public static ExifTag GPSDestLongitude { get; } = new(ExifTagValue.GPSDestLongitude); /// /// Gets the LensSpecification exif tag. /// - public static ExifTag LensSpecification { get; } = new ExifTag(ExifTagValue.LensSpecification); + public static ExifTag LensSpecification { get; } = new(ExifTagValue.LensSpecification); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs index aeeda58bb0..52972811e6 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs @@ -9,235 +9,235 @@ public abstract partial class ExifTag /// /// Gets the OldSubfileType exif tag. /// - public static ExifTag OldSubfileType { get; } = new ExifTag(ExifTagValue.OldSubfileType); + public static ExifTag OldSubfileType { get; } = new(ExifTagValue.OldSubfileType); /// /// Gets the Compression exif tag. /// - public static ExifTag Compression { get; } = new ExifTag(ExifTagValue.Compression); + public static ExifTag Compression { get; } = new(ExifTagValue.Compression); /// /// Gets the PhotometricInterpretation exif tag. /// - public static ExifTag PhotometricInterpretation { get; } = new ExifTag(ExifTagValue.PhotometricInterpretation); + public static ExifTag PhotometricInterpretation { get; } = new(ExifTagValue.PhotometricInterpretation); /// /// Gets the Thresholding exif tag. /// - public static ExifTag Thresholding { get; } = new ExifTag(ExifTagValue.Thresholding); + public static ExifTag Thresholding { get; } = new(ExifTagValue.Thresholding); /// /// Gets the CellWidth exif tag. /// - public static ExifTag CellWidth { get; } = new ExifTag(ExifTagValue.CellWidth); + public static ExifTag CellWidth { get; } = new(ExifTagValue.CellWidth); /// /// Gets the CellLength exif tag. /// - public static ExifTag CellLength { get; } = new ExifTag(ExifTagValue.CellLength); + public static ExifTag CellLength { get; } = new(ExifTagValue.CellLength); /// /// Gets the FillOrder exif tag. /// - public static ExifTag FillOrder { get; } = new ExifTag(ExifTagValue.FillOrder); + public static ExifTag FillOrder { get; } = new(ExifTagValue.FillOrder); /// /// Gets the Orientation exif tag. /// - public static ExifTag Orientation { get; } = new ExifTag(ExifTagValue.Orientation); + public static ExifTag Orientation { get; } = new(ExifTagValue.Orientation); /// /// Gets the SamplesPerPixel exif tag. /// - public static ExifTag SamplesPerPixel { get; } = new ExifTag(ExifTagValue.SamplesPerPixel); + public static ExifTag SamplesPerPixel { get; } = new(ExifTagValue.SamplesPerPixel); /// /// Gets the PlanarConfiguration exif tag. /// - public static ExifTag PlanarConfiguration { get; } = new ExifTag(ExifTagValue.PlanarConfiguration); + public static ExifTag PlanarConfiguration { get; } = new(ExifTagValue.PlanarConfiguration); /// /// Gets the Predictor exif tag. /// - public static ExifTag Predictor { get; } = new ExifTag(ExifTagValue.Predictor); + public static ExifTag Predictor { get; } = new(ExifTagValue.Predictor); /// /// Gets the GrayResponseUnit exif tag. /// - public static ExifTag GrayResponseUnit { get; } = new ExifTag(ExifTagValue.GrayResponseUnit); + public static ExifTag GrayResponseUnit { get; } = new(ExifTagValue.GrayResponseUnit); /// /// Gets the ResolutionUnit exif tag. /// - public static ExifTag ResolutionUnit { get; } = new ExifTag(ExifTagValue.ResolutionUnit); + public static ExifTag ResolutionUnit { get; } = new(ExifTagValue.ResolutionUnit); /// /// Gets the CleanFaxData exif tag. /// - public static ExifTag CleanFaxData { get; } = new ExifTag(ExifTagValue.CleanFaxData); + public static ExifTag CleanFaxData { get; } = new(ExifTagValue.CleanFaxData); /// /// Gets the InkSet exif tag. /// - public static ExifTag InkSet { get; } = new ExifTag(ExifTagValue.InkSet); + public static ExifTag InkSet { get; } = new(ExifTagValue.InkSet); /// /// Gets the NumberOfInks exif tag. /// - public static ExifTag NumberOfInks { get; } = new ExifTag(ExifTagValue.NumberOfInks); + public static ExifTag NumberOfInks { get; } = new(ExifTagValue.NumberOfInks); /// /// Gets the DotRange exif tag. /// - public static ExifTag DotRange { get; } = new ExifTag(ExifTagValue.DotRange); + public static ExifTag DotRange { get; } = new(ExifTagValue.DotRange); /// /// Gets the Indexed exif tag. /// - public static ExifTag Indexed { get; } = new ExifTag(ExifTagValue.Indexed); + public static ExifTag Indexed { get; } = new(ExifTagValue.Indexed); /// /// Gets the OPIProxy exif tag. /// - public static ExifTag OPIProxy { get; } = new ExifTag(ExifTagValue.OPIProxy); + public static ExifTag OPIProxy { get; } = new(ExifTagValue.OPIProxy); /// /// Gets the JPEGProc exif tag. /// - public static ExifTag JPEGProc { get; } = new ExifTag(ExifTagValue.JPEGProc); + public static ExifTag JPEGProc { get; } = new(ExifTagValue.JPEGProc); /// /// Gets the JPEGRestartInterval exif tag. /// - public static ExifTag JPEGRestartInterval { get; } = new ExifTag(ExifTagValue.JPEGRestartInterval); + public static ExifTag JPEGRestartInterval { get; } = new(ExifTagValue.JPEGRestartInterval); /// /// Gets the YCbCrPositioning exif tag. /// - public static ExifTag YCbCrPositioning { get; } = new ExifTag(ExifTagValue.YCbCrPositioning); + public static ExifTag YCbCrPositioning { get; } = new(ExifTagValue.YCbCrPositioning); /// /// Gets the Rating exif tag. /// - public static ExifTag Rating { get; } = new ExifTag(ExifTagValue.Rating); + public static ExifTag Rating { get; } = new(ExifTagValue.Rating); /// /// Gets the RatingPercent exif tag. /// - public static ExifTag RatingPercent { get; } = new ExifTag(ExifTagValue.RatingPercent); + public static ExifTag RatingPercent { get; } = new(ExifTagValue.RatingPercent); /// /// Gets the ExposureProgram exif tag. /// - public static ExifTag ExposureProgram { get; } = new ExifTag(ExifTagValue.ExposureProgram); + public static ExifTag ExposureProgram { get; } = new(ExifTagValue.ExposureProgram); /// /// Gets the Interlace exif tag. /// - public static ExifTag Interlace { get; } = new ExifTag(ExifTagValue.Interlace); + public static ExifTag Interlace { get; } = new(ExifTagValue.Interlace); /// /// Gets the SelfTimerMode exif tag. /// - public static ExifTag SelfTimerMode { get; } = new ExifTag(ExifTagValue.SelfTimerMode); + public static ExifTag SelfTimerMode { get; } = new(ExifTagValue.SelfTimerMode); /// /// Gets the SensitivityType exif tag. /// - public static ExifTag SensitivityType { get; } = new ExifTag(ExifTagValue.SensitivityType); + public static ExifTag SensitivityType { get; } = new(ExifTagValue.SensitivityType); /// /// Gets the MeteringMode exif tag. /// - public static ExifTag MeteringMode { get; } = new ExifTag(ExifTagValue.MeteringMode); + public static ExifTag MeteringMode { get; } = new(ExifTagValue.MeteringMode); /// /// Gets the LightSource exif tag. /// - public static ExifTag LightSource { get; } = new ExifTag(ExifTagValue.LightSource); + public static ExifTag LightSource { get; } = new(ExifTagValue.LightSource); /// /// Gets the FocalPlaneResolutionUnit2 exif tag. /// - public static ExifTag FocalPlaneResolutionUnit2 { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit2); + public static ExifTag FocalPlaneResolutionUnit2 { get; } = new(ExifTagValue.FocalPlaneResolutionUnit2); /// /// Gets the SensingMethod2 exif tag. /// - public static ExifTag SensingMethod2 { get; } = new ExifTag(ExifTagValue.SensingMethod2); + public static ExifTag SensingMethod2 { get; } = new(ExifTagValue.SensingMethod2); /// /// Gets the Flash exif tag. /// - public static ExifTag Flash { get; } = new ExifTag(ExifTagValue.Flash); + public static ExifTag Flash { get; } = new(ExifTagValue.Flash); /// /// Gets the ColorSpace exif tag. /// - public static ExifTag ColorSpace { get; } = new ExifTag(ExifTagValue.ColorSpace); + public static ExifTag ColorSpace { get; } = new(ExifTagValue.ColorSpace); /// /// Gets the FocalPlaneResolutionUnit exif tag. /// - public static ExifTag FocalPlaneResolutionUnit { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit); + public static ExifTag FocalPlaneResolutionUnit { get; } = new(ExifTagValue.FocalPlaneResolutionUnit); /// /// Gets the SensingMethod exif tag. /// - public static ExifTag SensingMethod { get; } = new ExifTag(ExifTagValue.SensingMethod); + public static ExifTag SensingMethod { get; } = new(ExifTagValue.SensingMethod); /// /// Gets the CustomRendered exif tag. /// - public static ExifTag CustomRendered { get; } = new ExifTag(ExifTagValue.CustomRendered); + public static ExifTag CustomRendered { get; } = new(ExifTagValue.CustomRendered); /// /// Gets the ExposureMode exif tag. /// - public static ExifTag ExposureMode { get; } = new ExifTag(ExifTagValue.ExposureMode); + public static ExifTag ExposureMode { get; } = new(ExifTagValue.ExposureMode); /// /// Gets the WhiteBalance exif tag. /// - public static ExifTag WhiteBalance { get; } = new ExifTag(ExifTagValue.WhiteBalance); + public static ExifTag WhiteBalance { get; } = new(ExifTagValue.WhiteBalance); /// /// Gets the FocalLengthIn35mmFilm exif tag. /// - public static ExifTag FocalLengthIn35mmFilm { get; } = new ExifTag(ExifTagValue.FocalLengthIn35mmFilm); + public static ExifTag FocalLengthIn35mmFilm { get; } = new(ExifTagValue.FocalLengthIn35mmFilm); /// /// Gets the SceneCaptureType exif tag. /// - public static ExifTag SceneCaptureType { get; } = new ExifTag(ExifTagValue.SceneCaptureType); + public static ExifTag SceneCaptureType { get; } = new(ExifTagValue.SceneCaptureType); /// /// Gets the GainControl exif tag. /// - public static ExifTag GainControl { get; } = new ExifTag(ExifTagValue.GainControl); + public static ExifTag GainControl { get; } = new(ExifTagValue.GainControl); /// /// Gets the Contrast exif tag. /// - public static ExifTag Contrast { get; } = new ExifTag(ExifTagValue.Contrast); + public static ExifTag Contrast { get; } = new(ExifTagValue.Contrast); /// /// Gets the Saturation exif tag. /// - public static ExifTag Saturation { get; } = new ExifTag(ExifTagValue.Saturation); + public static ExifTag Saturation { get; } = new(ExifTagValue.Saturation); /// /// Gets the Sharpness exif tag. /// - public static ExifTag Sharpness { get; } = new ExifTag(ExifTagValue.Sharpness); + public static ExifTag Sharpness { get; } = new(ExifTagValue.Sharpness); /// /// Gets the SubjectDistanceRange exif tag. /// - public static ExifTag SubjectDistanceRange { get; } = new ExifTag(ExifTagValue.SubjectDistanceRange); + public static ExifTag SubjectDistanceRange { get; } = new(ExifTagValue.SubjectDistanceRange); /// /// Gets the GPSDifferential exif tag. /// - public static ExifTag GPSDifferential { get; } = new ExifTag(ExifTagValue.GPSDifferential); + public static ExifTag GPSDifferential { get; } = new(ExifTagValue.GPSDifferential); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs index dc708c50f1..55e65517f1 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs @@ -9,100 +9,100 @@ public abstract partial class ExifTag /// /// Gets the BitsPerSample exif tag. /// - public static ExifTag BitsPerSample { get; } = new ExifTag(ExifTagValue.BitsPerSample); + public static ExifTag BitsPerSample { get; } = new(ExifTagValue.BitsPerSample); /// /// Gets the MinSampleValue exif tag. /// - public static ExifTag MinSampleValue { get; } = new ExifTag(ExifTagValue.MinSampleValue); + public static ExifTag MinSampleValue { get; } = new(ExifTagValue.MinSampleValue); /// /// Gets the MaxSampleValue exif tag. /// - public static ExifTag MaxSampleValue { get; } = new ExifTag(ExifTagValue.MaxSampleValue); + public static ExifTag MaxSampleValue { get; } = new(ExifTagValue.MaxSampleValue); /// /// Gets the GrayResponseCurve exif tag. /// - public static ExifTag GrayResponseCurve { get; } = new ExifTag(ExifTagValue.GrayResponseCurve); + public static ExifTag GrayResponseCurve { get; } = new(ExifTagValue.GrayResponseCurve); /// /// Gets the ColorMap exif tag. /// - public static ExifTag ColorMap { get; } = new ExifTag(ExifTagValue.ColorMap); + public static ExifTag ColorMap { get; } = new(ExifTagValue.ColorMap); /// /// Gets the ExtraSamples exif tag. /// - public static ExifTag ExtraSamples { get; } = new ExifTag(ExifTagValue.ExtraSamples); + public static ExifTag ExtraSamples { get; } = new(ExifTagValue.ExtraSamples); /// /// Gets the PageNumber exif tag. /// - public static ExifTag PageNumber { get; } = new ExifTag(ExifTagValue.PageNumber); + public static ExifTag PageNumber { get; } = new(ExifTagValue.PageNumber); /// /// Gets the TransferFunction exif tag. /// - public static ExifTag TransferFunction { get; } = new ExifTag(ExifTagValue.TransferFunction); + public static ExifTag TransferFunction { get; } = new(ExifTagValue.TransferFunction); /// /// Gets the HalftoneHints exif tag. /// - public static ExifTag HalftoneHints { get; } = new ExifTag(ExifTagValue.HalftoneHints); + public static ExifTag HalftoneHints { get; } = new(ExifTagValue.HalftoneHints); /// /// Gets the SampleFormat exif tag. /// - public static ExifTag SampleFormat { get; } = new ExifTag(ExifTagValue.SampleFormat); + public static ExifTag SampleFormat { get; } = new(ExifTagValue.SampleFormat); /// /// Gets the TransferRange exif tag. /// - public static ExifTag TransferRange { get; } = new ExifTag(ExifTagValue.TransferRange); + public static ExifTag TransferRange { get; } = new(ExifTagValue.TransferRange); /// /// Gets the DefaultImageColor exif tag. /// - public static ExifTag DefaultImageColor { get; } = new ExifTag(ExifTagValue.DefaultImageColor); + public static ExifTag DefaultImageColor { get; } = new(ExifTagValue.DefaultImageColor); /// /// Gets the JPEGLosslessPredictors exif tag. /// - public static ExifTag JPEGLosslessPredictors { get; } = new ExifTag(ExifTagValue.JPEGLosslessPredictors); + public static ExifTag JPEGLosslessPredictors { get; } = new(ExifTagValue.JPEGLosslessPredictors); /// /// Gets the JPEGPointTransforms exif tag. /// - public static ExifTag JPEGPointTransforms { get; } = new ExifTag(ExifTagValue.JPEGPointTransforms); + public static ExifTag JPEGPointTransforms { get; } = new(ExifTagValue.JPEGPointTransforms); /// /// Gets the YCbCrSubsampling exif tag. /// - public static ExifTag YCbCrSubsampling { get; } = new ExifTag(ExifTagValue.YCbCrSubsampling); + public static ExifTag YCbCrSubsampling { get; } = new(ExifTagValue.YCbCrSubsampling); /// /// Gets the CFARepeatPatternDim exif tag. /// - public static ExifTag CFARepeatPatternDim { get; } = new ExifTag(ExifTagValue.CFARepeatPatternDim); + public static ExifTag CFARepeatPatternDim { get; } = new(ExifTagValue.CFARepeatPatternDim); /// /// Gets the IntergraphPacketData exif tag. /// - public static ExifTag IntergraphPacketData { get; } = new ExifTag(ExifTagValue.IntergraphPacketData); + public static ExifTag IntergraphPacketData { get; } = new(ExifTagValue.IntergraphPacketData); /// /// Gets the ISOSpeedRatings exif tag. /// - public static ExifTag ISOSpeedRatings { get; } = new ExifTag(ExifTagValue.ISOSpeedRatings); + public static ExifTag ISOSpeedRatings { get; } = new(ExifTagValue.ISOSpeedRatings); /// /// Gets the SubjectArea exif tag. /// - public static ExifTag SubjectArea { get; } = new ExifTag(ExifTagValue.SubjectArea); + public static ExifTag SubjectArea { get; } = new(ExifTagValue.SubjectArea); /// /// Gets the SubjectLocation exif tag. /// - public static ExifTag SubjectLocation { get; } = new ExifTag(ExifTagValue.SubjectLocation); + public static ExifTag SubjectLocation { get; } = new(ExifTagValue.SubjectLocation); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs index d645b5176a..8fbbba2177 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs @@ -9,30 +9,30 @@ public abstract partial class ExifTag /// /// Gets the ShutterSpeedValue exif tag. /// - public static ExifTag ShutterSpeedValue { get; } = new ExifTag(ExifTagValue.ShutterSpeedValue); + public static ExifTag ShutterSpeedValue { get; } = new(ExifTagValue.ShutterSpeedValue); /// /// Gets the BrightnessValue exif tag. /// - public static ExifTag BrightnessValue { get; } = new ExifTag(ExifTagValue.BrightnessValue); + public static ExifTag BrightnessValue { get; } = new(ExifTagValue.BrightnessValue); /// /// Gets the ExposureBiasValue exif tag. /// - public static ExifTag ExposureBiasValue { get; } = new ExifTag(ExifTagValue.ExposureBiasValue); + public static ExifTag ExposureBiasValue { get; } = new(ExifTagValue.ExposureBiasValue); /// /// Gets the AmbientTemperature exif tag. /// - public static ExifTag AmbientTemperature { get; } = new ExifTag(ExifTagValue.AmbientTemperature); + public static ExifTag AmbientTemperature { get; } = new(ExifTagValue.AmbientTemperature); /// /// Gets the WaterDepth exif tag. /// - public static ExifTag WaterDepth { get; } = new ExifTag(ExifTagValue.WaterDepth); + public static ExifTag WaterDepth { get; } = new(ExifTagValue.WaterDepth); /// /// Gets the CameraElevationAngle exif tag. /// - public static ExifTag CameraElevationAngle { get; } = new ExifTag(ExifTagValue.CameraElevationAngle); + public static ExifTag CameraElevationAngle { get; } = new(ExifTagValue.CameraElevationAngle); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs index ef1e8afea4..a27108f8a0 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs @@ -9,5 +9,5 @@ public abstract partial class ExifTag /// /// Gets the Decode exif tag. /// - public static ExifTag Decode { get; } = new ExifTag(ExifTagValue.Decode); + public static ExifTag Decode { get; } = new(ExifTagValue.Decode); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedShortArray.cs index d6a9205143..0868426987 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedShortArray.cs @@ -9,5 +9,5 @@ public abstract partial class ExifTag /// /// Gets the TimeZoneOffset exif tag. /// - public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); + public static ExifTag TimeZoneOffset { get; } = new(ExifTagValue.TimeZoneOffset); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs index 498dad3ebb..688403a65a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs @@ -9,270 +9,270 @@ public abstract partial class ExifTag /// /// Gets the ImageDescription exif tag. /// - public static ExifTag ImageDescription { get; } = new ExifTag(ExifTagValue.ImageDescription); + public static ExifTag ImageDescription { get; } = new(ExifTagValue.ImageDescription); /// /// Gets the Make exif tag. /// - public static ExifTag Make { get; } = new ExifTag(ExifTagValue.Make); + public static ExifTag Make { get; } = new(ExifTagValue.Make); /// /// Gets the Model exif tag. /// - public static ExifTag Model { get; } = new ExifTag(ExifTagValue.Model); + public static ExifTag Model { get; } = new(ExifTagValue.Model); /// /// Gets the Software exif tag. /// - public static ExifTag Software { get; } = new ExifTag(ExifTagValue.Software); + public static ExifTag Software { get; } = new(ExifTagValue.Software); /// /// Gets the DateTime exif tag. /// - public static ExifTag DateTime { get; } = new ExifTag(ExifTagValue.DateTime); + public static ExifTag DateTime { get; } = new(ExifTagValue.DateTime); /// /// Gets the Artist exif tag. /// - public static ExifTag Artist { get; } = new ExifTag(ExifTagValue.Artist); + public static ExifTag Artist { get; } = new(ExifTagValue.Artist); /// /// Gets the HostComputer exif tag. /// - public static ExifTag HostComputer { get; } = new ExifTag(ExifTagValue.HostComputer); + public static ExifTag HostComputer { get; } = new(ExifTagValue.HostComputer); /// /// Gets the Copyright exif tag. /// - public static ExifTag Copyright { get; } = new ExifTag(ExifTagValue.Copyright); + public static ExifTag Copyright { get; } = new(ExifTagValue.Copyright); /// /// Gets the DocumentName exif tag. /// - public static ExifTag DocumentName { get; } = new ExifTag(ExifTagValue.DocumentName); + public static ExifTag DocumentName { get; } = new(ExifTagValue.DocumentName); /// /// Gets the PageName exif tag. /// - public static ExifTag PageName { get; } = new ExifTag(ExifTagValue.PageName); + public static ExifTag PageName { get; } = new(ExifTagValue.PageName); /// /// Gets the InkNames exif tag. /// - public static ExifTag InkNames { get; } = new ExifTag(ExifTagValue.InkNames); + public static ExifTag InkNames { get; } = new(ExifTagValue.InkNames); /// /// Gets the TargetPrinter exif tag. /// - public static ExifTag TargetPrinter { get; } = new ExifTag(ExifTagValue.TargetPrinter); + public static ExifTag TargetPrinter { get; } = new(ExifTagValue.TargetPrinter); /// /// Gets the ImageID exif tag. /// - public static ExifTag ImageID { get; } = new ExifTag(ExifTagValue.ImageID); + public static ExifTag ImageID { get; } = new(ExifTagValue.ImageID); /// /// Gets the MDLabName exif tag. /// - public static ExifTag MDLabName { get; } = new ExifTag(ExifTagValue.MDLabName); + public static ExifTag MDLabName { get; } = new(ExifTagValue.MDLabName); /// /// Gets the MDSampleInfo exif tag. /// - public static ExifTag MDSampleInfo { get; } = new ExifTag(ExifTagValue.MDSampleInfo); + public static ExifTag MDSampleInfo { get; } = new(ExifTagValue.MDSampleInfo); /// /// Gets the MDPrepDate exif tag. /// - public static ExifTag MDPrepDate { get; } = new ExifTag(ExifTagValue.MDPrepDate); + public static ExifTag MDPrepDate { get; } = new(ExifTagValue.MDPrepDate); /// /// Gets the MDPrepTime exif tag. /// - public static ExifTag MDPrepTime { get; } = new ExifTag(ExifTagValue.MDPrepTime); + public static ExifTag MDPrepTime { get; } = new(ExifTagValue.MDPrepTime); /// /// Gets the MDFileUnits exif tag. /// - public static ExifTag MDFileUnits { get; } = new ExifTag(ExifTagValue.MDFileUnits); + public static ExifTag MDFileUnits { get; } = new(ExifTagValue.MDFileUnits); /// /// Gets the SEMInfo exif tag. /// - public static ExifTag SEMInfo { get; } = new ExifTag(ExifTagValue.SEMInfo); + public static ExifTag SEMInfo { get; } = new(ExifTagValue.SEMInfo); /// /// Gets the SpectralSensitivity exif tag. /// - public static ExifTag SpectralSensitivity { get; } = new ExifTag(ExifTagValue.SpectralSensitivity); + public static ExifTag SpectralSensitivity { get; } = new(ExifTagValue.SpectralSensitivity); /// /// Gets the DateTimeOriginal exif tag. /// - public static ExifTag DateTimeOriginal { get; } = new ExifTag(ExifTagValue.DateTimeOriginal); + public static ExifTag DateTimeOriginal { get; } = new(ExifTagValue.DateTimeOriginal); /// /// Gets the DateTimeDigitized exif tag. /// - public static ExifTag DateTimeDigitized { get; } = new ExifTag(ExifTagValue.DateTimeDigitized); + public static ExifTag DateTimeDigitized { get; } = new(ExifTagValue.DateTimeDigitized); /// /// Gets the SubsecTime exif tag. /// - public static ExifTag SubsecTime { get; } = new ExifTag(ExifTagValue.SubsecTime); + public static ExifTag SubsecTime { get; } = new(ExifTagValue.SubsecTime); /// /// Gets the SubsecTimeOriginal exif tag. /// - public static ExifTag SubsecTimeOriginal { get; } = new ExifTag(ExifTagValue.SubsecTimeOriginal); + public static ExifTag SubsecTimeOriginal { get; } = new(ExifTagValue.SubsecTimeOriginal); /// /// Gets the SubsecTimeDigitized exif tag. /// - public static ExifTag SubsecTimeDigitized { get; } = new ExifTag(ExifTagValue.SubsecTimeDigitized); + public static ExifTag SubsecTimeDigitized { get; } = new(ExifTagValue.SubsecTimeDigitized); /// /// Gets the RelatedSoundFile exif tag. /// - public static ExifTag RelatedSoundFile { get; } = new ExifTag(ExifTagValue.RelatedSoundFile); + public static ExifTag RelatedSoundFile { get; } = new(ExifTagValue.RelatedSoundFile); /// /// Gets the FaxSubaddress exif tag. /// - public static ExifTag FaxSubaddress { get; } = new ExifTag(ExifTagValue.FaxSubaddress); + public static ExifTag FaxSubaddress { get; } = new(ExifTagValue.FaxSubaddress); /// /// Gets the OffsetTime exif tag. /// - public static ExifTag OffsetTime { get; } = new ExifTag(ExifTagValue.OffsetTime); + public static ExifTag OffsetTime { get; } = new(ExifTagValue.OffsetTime); /// /// Gets the OffsetTimeOriginal exif tag. /// - public static ExifTag OffsetTimeOriginal { get; } = new ExifTag(ExifTagValue.OffsetTimeOriginal); + public static ExifTag OffsetTimeOriginal { get; } = new(ExifTagValue.OffsetTimeOriginal); /// /// Gets the OffsetTimeDigitized exif tag. /// - public static ExifTag OffsetTimeDigitized { get; } = new ExifTag(ExifTagValue.OffsetTimeDigitized); + public static ExifTag OffsetTimeDigitized { get; } = new(ExifTagValue.OffsetTimeDigitized); /// /// Gets the SecurityClassification exif tag. /// - public static ExifTag SecurityClassification { get; } = new ExifTag(ExifTagValue.SecurityClassification); + public static ExifTag SecurityClassification { get; } = new(ExifTagValue.SecurityClassification); /// /// Gets the ImageHistory exif tag. /// - public static ExifTag ImageHistory { get; } = new ExifTag(ExifTagValue.ImageHistory); + public static ExifTag ImageHistory { get; } = new(ExifTagValue.ImageHistory); /// /// Gets the ImageUniqueID exif tag. /// - public static ExifTag ImageUniqueID { get; } = new ExifTag(ExifTagValue.ImageUniqueID); + public static ExifTag ImageUniqueID { get; } = new(ExifTagValue.ImageUniqueID); /// /// Gets the OwnerName exif tag. /// - public static ExifTag OwnerName { get; } = new ExifTag(ExifTagValue.OwnerName); + public static ExifTag OwnerName { get; } = new(ExifTagValue.OwnerName); /// /// Gets the SerialNumber exif tag. /// - public static ExifTag SerialNumber { get; } = new ExifTag(ExifTagValue.SerialNumber); + public static ExifTag SerialNumber { get; } = new(ExifTagValue.SerialNumber); /// /// Gets the LensMake exif tag. /// - public static ExifTag LensMake { get; } = new ExifTag(ExifTagValue.LensMake); + public static ExifTag LensMake { get; } = new(ExifTagValue.LensMake); /// /// Gets the LensModel exif tag. /// - public static ExifTag LensModel { get; } = new ExifTag(ExifTagValue.LensModel); + public static ExifTag LensModel { get; } = new(ExifTagValue.LensModel); /// /// Gets the LensSerialNumber exif tag. /// - public static ExifTag LensSerialNumber { get; } = new ExifTag(ExifTagValue.LensSerialNumber); + public static ExifTag LensSerialNumber { get; } = new(ExifTagValue.LensSerialNumber); /// /// Gets the GDALMetadata exif tag. /// - public static ExifTag GDALMetadata { get; } = new ExifTag(ExifTagValue.GDALMetadata); + public static ExifTag GDALMetadata { get; } = new(ExifTagValue.GDALMetadata); /// /// Gets the GDALNoData exif tag. /// - public static ExifTag GDALNoData { get; } = new ExifTag(ExifTagValue.GDALNoData); + public static ExifTag GDALNoData { get; } = new(ExifTagValue.GDALNoData); /// /// Gets the GPSLatitudeRef exif tag. /// - public static ExifTag GPSLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSLatitudeRef); + public static ExifTag GPSLatitudeRef { get; } = new(ExifTagValue.GPSLatitudeRef); /// /// Gets the GPSLongitudeRef exif tag. /// - public static ExifTag GPSLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSLongitudeRef); + public static ExifTag GPSLongitudeRef { get; } = new(ExifTagValue.GPSLongitudeRef); /// /// Gets the GPSSatellites exif tag. /// - public static ExifTag GPSSatellites { get; } = new ExifTag(ExifTagValue.GPSSatellites); + public static ExifTag GPSSatellites { get; } = new(ExifTagValue.GPSSatellites); /// /// Gets the GPSStatus exif tag. /// - public static ExifTag GPSStatus { get; } = new ExifTag(ExifTagValue.GPSStatus); + public static ExifTag GPSStatus { get; } = new(ExifTagValue.GPSStatus); /// /// Gets the GPSMeasureMode exif tag. /// - public static ExifTag GPSMeasureMode { get; } = new ExifTag(ExifTagValue.GPSMeasureMode); + public static ExifTag GPSMeasureMode { get; } = new(ExifTagValue.GPSMeasureMode); /// /// Gets the GPSSpeedRef exif tag. /// - public static ExifTag GPSSpeedRef { get; } = new ExifTag(ExifTagValue.GPSSpeedRef); + public static ExifTag GPSSpeedRef { get; } = new(ExifTagValue.GPSSpeedRef); /// /// Gets the GPSTrackRef exif tag. /// - public static ExifTag GPSTrackRef { get; } = new ExifTag(ExifTagValue.GPSTrackRef); + public static ExifTag GPSTrackRef { get; } = new(ExifTagValue.GPSTrackRef); /// /// Gets the GPSImgDirectionRef exif tag. /// - public static ExifTag GPSImgDirectionRef { get; } = new ExifTag(ExifTagValue.GPSImgDirectionRef); + public static ExifTag GPSImgDirectionRef { get; } = new(ExifTagValue.GPSImgDirectionRef); /// /// Gets the GPSMapDatum exif tag. /// - public static ExifTag GPSMapDatum { get; } = new ExifTag(ExifTagValue.GPSMapDatum); + public static ExifTag GPSMapDatum { get; } = new(ExifTagValue.GPSMapDatum); /// /// Gets the GPSDestLatitudeRef exif tag. /// - public static ExifTag GPSDestLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLatitudeRef); + public static ExifTag GPSDestLatitudeRef { get; } = new(ExifTagValue.GPSDestLatitudeRef); /// /// Gets the GPSDestLongitudeRef exif tag. /// - public static ExifTag GPSDestLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLongitudeRef); + public static ExifTag GPSDestLongitudeRef { get; } = new(ExifTagValue.GPSDestLongitudeRef); /// /// Gets the GPSDestBearingRef exif tag. /// - public static ExifTag GPSDestBearingRef { get; } = new ExifTag(ExifTagValue.GPSDestBearingRef); + public static ExifTag GPSDestBearingRef { get; } = new(ExifTagValue.GPSDestBearingRef); /// /// Gets the GPSDestDistanceRef exif tag. /// - public static ExifTag GPSDestDistanceRef { get; } = new ExifTag(ExifTagValue.GPSDestDistanceRef); + public static ExifTag GPSDestDistanceRef { get; } = new(ExifTagValue.GPSDestDistanceRef); /// /// Gets the GPSDateStamp exif tag. /// - public static ExifTag GPSDateStamp { get; } = new ExifTag(ExifTagValue.GPSDateStamp); + public static ExifTag GPSDateStamp { get; } = new(ExifTagValue.GPSDateStamp); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs index 65acc4bd75..e13b3b30e4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs @@ -9,25 +9,25 @@ public abstract partial class ExifTag /// /// Gets the title tag used by Windows (encoded in UCS2). /// - public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); + public static ExifTag XPTitle => new(ExifTagValue.XPTitle); /// /// Gets the comment tag used by Windows (encoded in UCS2). /// - public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); + public static ExifTag XPComment => new(ExifTagValue.XPComment); /// /// Gets the author tag used by Windows (encoded in UCS2). /// - public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); + public static ExifTag XPAuthor => new(ExifTagValue.XPAuthor); /// /// Gets the keywords tag used by Windows (encoded in UCS2). /// - public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); + public static ExifTag XPKeywords => new(ExifTagValue.XPKeywords); /// /// Gets the subject tag used by Windows (encoded in UCS2). /// - public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); + public static ExifTag XPSubject => new(ExifTagValue.XPSubject); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs index 417673b4ae..e822c2a111 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs @@ -9,70 +9,70 @@ public abstract partial class ExifTag /// /// Gets the JPEGTables exif tag. /// - public static ExifTag JPEGTables { get; } = new ExifTag(ExifTagValue.JPEGTables); + public static ExifTag JPEGTables { get; } = new(ExifTagValue.JPEGTables); /// /// Gets the OECF exif tag. /// - public static ExifTag OECF { get; } = new ExifTag(ExifTagValue.OECF); + public static ExifTag OECF { get; } = new(ExifTagValue.OECF); /// /// Gets the ExifVersion exif tag. /// - public static ExifTag ExifVersion { get; } = new ExifTag(ExifTagValue.ExifVersion); + public static ExifTag ExifVersion { get; } = new(ExifTagValue.ExifVersion); /// /// Gets the ComponentsConfiguration exif tag. /// - public static ExifTag ComponentsConfiguration { get; } = new ExifTag(ExifTagValue.ComponentsConfiguration); + public static ExifTag ComponentsConfiguration { get; } = new(ExifTagValue.ComponentsConfiguration); /// /// Gets the MakerNote exif tag. /// - public static ExifTag MakerNote { get; } = new ExifTag(ExifTagValue.MakerNote); + public static ExifTag MakerNote { get; } = new(ExifTagValue.MakerNote); /// /// Gets the FlashpixVersion exif tag. /// - public static ExifTag FlashpixVersion { get; } = new ExifTag(ExifTagValue.FlashpixVersion); + public static ExifTag FlashpixVersion { get; } = new(ExifTagValue.FlashpixVersion); /// /// Gets the SpatialFrequencyResponse exif tag. /// - public static ExifTag SpatialFrequencyResponse { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse); + public static ExifTag SpatialFrequencyResponse { get; } = new(ExifTagValue.SpatialFrequencyResponse); /// /// Gets the SpatialFrequencyResponse2 exif tag. /// - public static ExifTag SpatialFrequencyResponse2 { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse2); + public static ExifTag SpatialFrequencyResponse2 { get; } = new(ExifTagValue.SpatialFrequencyResponse2); /// /// Gets the Noise exif tag. /// - public static ExifTag Noise { get; } = new ExifTag(ExifTagValue.Noise); + public static ExifTag Noise { get; } = new(ExifTagValue.Noise); /// /// Gets the CFAPattern exif tag. /// - public static ExifTag CFAPattern { get; } = new ExifTag(ExifTagValue.CFAPattern); + public static ExifTag CFAPattern { get; } = new(ExifTagValue.CFAPattern); /// /// Gets the DeviceSettingDescription exif tag. /// - public static ExifTag DeviceSettingDescription { get; } = new ExifTag(ExifTagValue.DeviceSettingDescription); + public static ExifTag DeviceSettingDescription { get; } = new(ExifTagValue.DeviceSettingDescription); /// /// Gets the ImageSourceData exif tag. /// - public static ExifTag ImageSourceData { get; } = new ExifTag(ExifTagValue.ImageSourceData); + public static ExifTag ImageSourceData { get; } = new(ExifTagValue.ImageSourceData); /// /// Gets the FileSource exif tag. /// - public static ExifTag FileSource { get; } = new ExifTag(ExifTagValue.FileSource); + public static ExifTag FileSource { get; } = new(ExifTagValue.FileSource); /// /// Gets the ImageDescription exif tag. /// - public static ExifTag SceneType { get; } = new ExifTag(ExifTagValue.SceneType); + public static ExifTag SceneType { get; } = new(ExifTagValue.SceneType); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs index ea0b8060d5..ec30f21cd4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; /// -/// Class that represents an exif tag from the Exif standard 2.31. +/// Class that represents an Exif tag from the Exif standard 2.31. /// public abstract partial class ExifTag : IEquatable { @@ -16,21 +16,21 @@ public abstract partial class ExifTag : IEquatable /// Converts the specified to a . /// /// The to convert. - public static explicit operator ushort(ExifTag tag) => tag?.value ?? (ushort)ExifTagValue.Unknown; + public static explicit operator ushort(ExifTag? tag) => tag?.value ?? (ushort)ExifTagValue.Unknown; /// /// Determines whether the specified instances are considered equal. /// /// The first to compare. /// The second to compare. - public static bool operator ==(ExifTag left, ExifTag right) => Equals(left, right); + public static bool operator ==(ExifTag? left, ExifTag? right) => left?.Equals(right) == true; /// /// Determines whether the specified instances are not considered equal. /// /// The first to compare. /// The second to compare. - public static bool operator !=(ExifTag left, ExifTag right) => !Equals(left, right); + public static bool operator !=(ExifTag? left, ExifTag? right) => !(left == right); /// public override bool Equals(object? obj) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs index 56e8a3ffd1..07dbc51e7d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs @@ -23,14 +23,6 @@ internal enum ExifTagValue ///
GPSIFDOffset = 0x8825, - /// - /// Indicates the identification of the Interoperability rule. - /// See https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/interoperability/interoperabilityindex.html - /// - [ExifTagDescription("R98", "Indicates a file conforming to R98 file specification of Recommended Exif Interoperability Rules (ExifR98) or to DCF basic file stipulated by Design Rule for Camera File System.")] - [ExifTagDescription("THM", "Indicates a file conforming to DCF thumbnail file stipulated by Design rule for Camera File System.")] - InteroperabilityIndex = 0x0001, - /// /// A general indication of the kind of data contained in this subfile. /// See Section 8: Baseline Fields. diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs index 64b8d23137..630709beca 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs @@ -43,7 +43,7 @@ public override bool TrySetValue(object? value) if (type == typeof(TValueType)) { - this.Value = new TValueType[] { (TValueType)value }; + this.Value = [(TValueType)value]; return true; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs index eb69c43897..a261ca657c 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs @@ -30,7 +30,7 @@ public override bool TrySetValue(object? value) switch (value) { case int intValue: - if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + if (intValue is >= byte.MinValue and <= byte.MaxValue) { this.Value = (byte)intValue; return true; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs index 6811fc6f9c..c0bbb5bee2 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs @@ -30,9 +30,9 @@ public override bool TrySetValue(object? value) if (value is int intValue) { - if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + if (intValue is >= byte.MinValue and <= byte.MaxValue) { - this.Value = new byte[] { (byte)intValue }; + this.Value = [(byte)intValue]; } return true; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs index cce7cf3e89..14b097f816 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs @@ -24,7 +24,7 @@ private ExifEncodedString(ExifEncodedString value) protected override string StringValue => this.Value.Text; - public override bool TrySetValue(object? value) + public bool TrySetValue(object? value, ByteOrder order) { if (base.TrySetValue(value)) { @@ -38,7 +38,7 @@ public override bool TrySetValue(object? value) } else if (value is byte[] buffer) { - if (ExifEncodedStringHelpers.TryParse(buffer, out EncodedString encodedString)) + if (ExifEncodedStringHelpers.TryParse(buffer, order, out EncodedString encodedString)) { this.Value = encodedString; return true; @@ -48,5 +48,8 @@ public override bool TrySetValue(object? value) return false; } + public override bool TrySetValue(object? value) + => this.TrySetValue(value, ByteOrder.LittleEndian); + public override IExifValue DeepClone() => new ExifEncodedString(this); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs index b7756e62bd..3266e538ab 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -47,46 +47,40 @@ public override bool TrySetValue(object? value) return this.SetSingle((ulong)Numerics.Clamp(val, 0, int.MaxValue)); case uint val: - return this.SetSingle((ulong)val); + return this.SetSingle(val); case short val: return this.SetSingle((ulong)Numerics.Clamp(val, 0, short.MaxValue)); case ushort val: - return this.SetSingle((ulong)val); + return this.SetSingle(val); case long val: return this.SetSingle((ulong)Numerics.Clamp(val, 0, long.MaxValue)); case long[] array: - { if (value.GetType() == typeof(ulong[])) { return this.SetArray((ulong[])value); } return this.SetArray(array); - } case int[] array: - { if (value.GetType() == typeof(uint[])) { return this.SetArray((uint[])value); } return this.SetArray(array); - } case short[] array: - { if (value.GetType() == typeof(ushort[])) { return this.SetArray((ushort[])value); } return this.SetArray(array); - } } return false; @@ -96,13 +90,13 @@ public override bool TrySetValue(object? value) private bool SetSingle(ulong value) { - this.Value = new[] { value }; + this.Value = [value]; return true; } private bool SetArray(long[] values) { - var numbers = new ulong[values.Length]; + ulong[] numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = (ulong)(values[i] < 0 ? 0 : values[i]); @@ -120,7 +114,7 @@ private bool SetArray(ulong[] values) private bool SetArray(int[] values) { - var numbers = new ulong[values.Length]; + ulong[] numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = (ulong)Numerics.Clamp(values[i], 0, int.MaxValue); @@ -132,10 +126,10 @@ private bool SetArray(int[] values) private bool SetArray(uint[] values) { - var numbers = new ulong[values.Length]; + ulong[] numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { - numbers[i] = (ulong)values[i]; + numbers[i] = values[i]; } this.Value = numbers; @@ -144,7 +138,7 @@ private bool SetArray(uint[] values) private bool SetArray(short[] values) { - var numbers = new ulong[values.Length]; + ulong[] numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = (ulong)Numerics.Clamp(values[i], 0, short.MaxValue); @@ -156,10 +150,10 @@ private bool SetArray(short[] values) private bool SetArray(ushort[] values) { - var numbers = new ulong[values.Length]; + ulong[] numbers = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { - numbers[i] = (ulong)values[i]; + numbers[i] = values[i]; } this.Value = numbers; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 1162c25ea9..3c8e4fc333 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -52,7 +52,6 @@ public override bool TrySetValue(object? value) case ushort val: return this.SetSingle(val); case int[] array: - { // workaround for inconsistent covariance of value-typed arrays if (value.GetType() == typeof(uint[])) { @@ -60,17 +59,14 @@ public override bool TrySetValue(object? value) } return this.SetArray(array); - } case short[] array: - { if (value.GetType() == typeof(ushort[])) { return this.SetArray((ushort[])value); } return this.SetArray(array); - } } return false; @@ -80,13 +76,13 @@ public override bool TrySetValue(object? value) private bool SetSingle(Number value) { - this.Value = new[] { value }; + this.Value = [value]; return true; } private bool SetArray(int[] values) { - var numbers = new Number[values.Length]; + Number[] numbers = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = values[i]; @@ -98,7 +94,7 @@ private bool SetArray(int[] values) private bool SetArray(uint[] values) { - var numbers = new Number[values.Length]; + Number[] numbers = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = values[i]; @@ -110,7 +106,7 @@ private bool SetArray(uint[] values) private bool SetArray(short[] values) { - var numbers = new Number[values.Length]; + Number[] numbers = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = values[i]; @@ -122,7 +118,7 @@ private bool SetArray(short[] values) private bool SetArray(ushort[] values) { - var numbers = new Number[values.Length]; + Number[] numbers = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { numbers[i] = values[i]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs index ac6453edc7..e8b2006df1 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs @@ -38,7 +38,7 @@ public override bool TrySetValue(object? value) { if (signed.Numerator >= 0 && signed.Denominator >= 0) { - this.Value = new[] { new Rational((uint)signed.Numerator, (uint)signed.Denominator) }; + this.Value = [new Rational((uint)signed.Numerator, (uint)signed.Denominator)]; } return true; @@ -56,7 +56,7 @@ private bool TrySetSignedArray(SignedRational[] signed) return false; } - var unsigned = new Rational[signed.Length]; + Rational[] unsigned = new Rational[signed.Length]; for (int i = 0; i < signed.Length; i++) { SignedRational s = signed[i]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs index d35b21422b..7dfd7aed1d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs @@ -36,7 +36,7 @@ public override bool TrySetValue(object? value) switch (value) { case int intValue: - if (intValue >= ushort.MinValue && intValue <= ushort.MaxValue) + if (intValue is >= ushort.MinValue and <= ushort.MaxValue) { this.Value = (ushort)intValue; return true; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs index a205e77dee..18ab5a162e 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs @@ -41,9 +41,9 @@ public override bool TrySetValue(object? value) if (value is int signedInt) { - if (signedInt >= ushort.MinValue && signedInt <= ushort.MaxValue) + if (signedInt is >= ushort.MinValue and <= ushort.MaxValue) { - this.Value = new ushort[] { (ushort)signedInt }; + this.Value = [(ushort)signedInt]; } return true; @@ -53,7 +53,7 @@ public override bool TrySetValue(object? value) { if (signedShort >= ushort.MinValue) { - this.Value = new ushort[] { (ushort)signedShort }; + this.Value = [(ushort)signedShort]; } return true; @@ -66,12 +66,12 @@ public override bool TrySetValue(object? value) private bool TrySetSignedIntArray(int[] signed) { - if (Array.FindIndex(signed, x => x < ushort.MinValue || x > ushort.MaxValue) > -1) + if (Array.FindIndex(signed, x => x is < ushort.MinValue or > ushort.MaxValue) > -1) { return false; } - var unsigned = new ushort[signed.Length]; + ushort[] unsigned = new ushort[signed.Length]; for (int i = 0; i < signed.Length; i++) { int s = signed[i]; @@ -89,7 +89,7 @@ private bool TrySetSignedShortArray(short[] signed) return false; } - var unsigned = new ushort[signed.Length]; + ushort[] unsigned = new ushort[signed.Length]; for (int i = 0; i < signed.Length; i++) { short s = signed[i]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs index 95a239b70d..206f8bd41b 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs @@ -31,7 +31,7 @@ public override bool TrySetValue(object? value) switch (value) { case int intValue: - if (intValue >= sbyte.MinValue && intValue <= sbyte.MaxValue) + if (intValue is >= sbyte.MinValue and <= sbyte.MaxValue) { this.Value = (sbyte)intValue; return true; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs index 6726febef0..5b008ea4a7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs @@ -31,7 +31,7 @@ public override bool TrySetValue(object? value) switch (value) { case int intValue: - if (intValue >= short.MinValue && intValue <= short.MaxValue) + if (intValue is >= short.MinValue and <= short.MaxValue) { this.Value = (short)intValue; return true; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs index 206417f667..d3993ac0dc 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs @@ -36,9 +36,9 @@ public override bool TrySetValue(object? value) if (value is int intValue) { - if (intValue >= short.MinValue && intValue <= short.MaxValue) + if (intValue is >= short.MinValue and <= short.MaxValue) { - this.Value = new short[] { (short)intValue }; + this.Value = [(short)intValue]; } return true; @@ -51,12 +51,12 @@ public override bool TrySetValue(object? value) private bool TrySetSignedArray(int[] intArray) { - if (Array.FindIndex(intArray, x => x < short.MinValue || x > short.MaxValue) > -1) + if (Array.FindIndex(intArray, x => x is < short.MinValue or > short.MaxValue) > -1) { return false; } - var value = new short[intArray.Length]; + short[] value = new short[intArray.Length]; for (int i = 0; i < intArray.Length; i++) { int s = intArray[i]; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs index eacb41cfb3..41b947e20c 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; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index 93f67d46ad..1c054fcba9 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -3,7 +3,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; -internal static partial class ExifValues +internal static class ExifValues { public static ExifValue? Create(ExifTagValue tag) => (ExifValue?)CreateValue(tag); @@ -12,573 +12,281 @@ internal static partial class ExifValues public static ExifValue? Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); public static ExifValue? Create(ExifTagValue tag, ExifDataType dataType, bool isArray) - { - switch (dataType) + => dataType switch { - case ExifDataType.Byte: - return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); - case ExifDataType.DoubleFloat: - return isArray ? new ExifDoubleArray(tag) : new ExifDouble(tag); - case ExifDataType.SingleFloat: - return isArray ? new ExifFloatArray(tag) : new ExifFloat(tag); - case ExifDataType.Long: - return isArray ? new ExifLongArray(tag) : new ExifLong(tag); - case ExifDataType.Long8: - return isArray ? new ExifLong8Array(tag) : new ExifLong8(tag); - case ExifDataType.Rational: - return isArray ? new ExifRationalArray(tag) : new ExifRational(tag); - case ExifDataType.Short: - return isArray ? new ExifShortArray(tag) : new ExifShort(tag); - case ExifDataType.SignedByte: - return isArray ? new ExifSignedByteArray(tag) : new ExifSignedByte(tag); - case ExifDataType.SignedLong: - return isArray ? new ExifSignedLongArray(tag) : new ExifSignedLong(tag); - case ExifDataType.SignedLong8: - return isArray ? new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag); - case ExifDataType.SignedRational: - return isArray ? new ExifSignedRationalArray(tag) : new ExifSignedRational(tag); - case ExifDataType.SignedShort: - return isArray ? new ExifSignedShortArray(tag) : new ExifSignedShort(tag); - case ExifDataType.Ascii: - return new ExifString(tag); - case ExifDataType.Undefined: - return isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); - default: - return null; - } - } + ExifDataType.Byte => isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType), + ExifDataType.DoubleFloat => isArray ? new ExifDoubleArray(tag) : new ExifDouble(tag), + ExifDataType.SingleFloat => isArray ? new ExifFloatArray(tag) : new ExifFloat(tag), + ExifDataType.Long => isArray ? new ExifLongArray(tag) : new ExifLong(tag), + ExifDataType.Long8 => isArray ? new ExifLong8Array(tag) : new ExifLong8(tag), + ExifDataType.Rational => isArray ? new ExifRationalArray(tag) : new ExifRational(tag), + ExifDataType.Short => isArray ? new ExifShortArray(tag) : new ExifShort(tag), + ExifDataType.SignedByte => isArray ? new ExifSignedByteArray(tag) : new ExifSignedByte(tag), + ExifDataType.SignedLong => isArray ? new ExifSignedLongArray(tag) : new ExifSignedLong(tag), + ExifDataType.SignedLong8 => isArray ? new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag), + ExifDataType.SignedRational => isArray ? new ExifSignedRationalArray(tag) : new ExifSignedRational(tag), + ExifDataType.SignedShort => isArray ? new ExifSignedShortArray(tag) : new ExifSignedShort(tag), + ExifDataType.Ascii => new ExifString(tag), + ExifDataType.Undefined => isArray ? new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType), + _ => null, + }; private static object? CreateValue(ExifTagValue tag) - { - switch (tag) + => tag switch { - case ExifTagValue.FaxProfile: - return new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte); - case ExifTagValue.ModeNumber: - return new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte); - case ExifTagValue.GPSAltitudeRef: - return new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte); - - case ExifTagValue.ClipPath: - return new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte); - case ExifTagValue.VersionYear: - return new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte); - case ExifTagValue.XMP: - return new ExifByteArray(ExifTag.XMP, ExifDataType.Byte); - case ExifTagValue.CFAPattern2: - return new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte); - case ExifTagValue.TIFFEPStandardID: - return new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte); - case ExifTagValue.GPSVersionID: - return new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte); - - case ExifTagValue.PixelScale: - return new ExifDoubleArray(ExifTag.PixelScale); - case ExifTagValue.IntergraphMatrix: - return new ExifDoubleArray(ExifTag.IntergraphMatrix); - case ExifTagValue.ModelTiePoint: - return new ExifDoubleArray(ExifTag.ModelTiePoint); - case ExifTagValue.ModelTransform: - return new ExifDoubleArray(ExifTag.ModelTransform); - - case ExifTagValue.SubfileType: - return new ExifLong(ExifTag.SubfileType); - case ExifTagValue.SubIFDOffset: - return new ExifLong(ExifTag.SubIFDOffset); - case ExifTagValue.GPSIFDOffset: - return new ExifLong(ExifTag.GPSIFDOffset); - case ExifTagValue.T4Options: - return new ExifLong(ExifTag.T4Options); - case ExifTagValue.T6Options: - return new ExifLong(ExifTag.T6Options); - case ExifTagValue.XClipPathUnits: - return new ExifLong(ExifTag.XClipPathUnits); - case ExifTagValue.YClipPathUnits: - return new ExifLong(ExifTag.YClipPathUnits); - case ExifTagValue.ProfileType: - return new ExifLong(ExifTag.ProfileType); - case ExifTagValue.CodingMethods: - return new ExifLong(ExifTag.CodingMethods); - case ExifTagValue.T82ptions: - return new ExifLong(ExifTag.T82ptions); - case ExifTagValue.JPEGInterchangeFormat: - return new ExifLong(ExifTag.JPEGInterchangeFormat); - case ExifTagValue.JPEGInterchangeFormatLength: - return new ExifLong(ExifTag.JPEGInterchangeFormatLength); - case ExifTagValue.MDFileTag: - return new ExifLong(ExifTag.MDFileTag); - case ExifTagValue.StandardOutputSensitivity: - return new ExifLong(ExifTag.StandardOutputSensitivity); - case ExifTagValue.RecommendedExposureIndex: - return new ExifLong(ExifTag.RecommendedExposureIndex); - case ExifTagValue.ISOSpeed: - return new ExifLong(ExifTag.ISOSpeed); - case ExifTagValue.ISOSpeedLatitudeyyy: - return new ExifLong(ExifTag.ISOSpeedLatitudeyyy); - case ExifTagValue.ISOSpeedLatitudezzz: - return new ExifLong(ExifTag.ISOSpeedLatitudezzz); - case ExifTagValue.FaxRecvParams: - return new ExifLong(ExifTag.FaxRecvParams); - case ExifTagValue.FaxRecvTime: - return new ExifLong(ExifTag.FaxRecvTime); - case ExifTagValue.ImageNumber: - return new ExifLong(ExifTag.ImageNumber); - - case ExifTagValue.FreeOffsets: - return new ExifLongArray(ExifTag.FreeOffsets); - case ExifTagValue.FreeByteCounts: - return new ExifLongArray(ExifTag.FreeByteCounts); - case ExifTagValue.ColorResponseUnit: - return new ExifLongArray(ExifTag.ColorResponseUnit); - case ExifTagValue.SMinSampleValue: - return new ExifLongArray(ExifTag.SMinSampleValue); - case ExifTagValue.SMaxSampleValue: - return new ExifLongArray(ExifTag.SMaxSampleValue); - case ExifTagValue.JPEGQTables: - return new ExifLongArray(ExifTag.JPEGQTables); - case ExifTagValue.JPEGDCTables: - return new ExifLongArray(ExifTag.JPEGDCTables); - case ExifTagValue.JPEGACTables: - return new ExifLongArray(ExifTag.JPEGACTables); - case ExifTagValue.StripRowCounts: - return new ExifLongArray(ExifTag.StripRowCounts); - case ExifTagValue.IntergraphRegisters: - return new ExifLongArray(ExifTag.IntergraphRegisters); - case ExifTagValue.SubIFDs: - return new ExifLongArray(ExifTag.SubIFDs); - - case ExifTagValue.ImageWidth: - return new ExifNumber(ExifTag.ImageWidth); - case ExifTagValue.ImageLength: - return new ExifNumber(ExifTag.ImageLength); - case ExifTagValue.RowsPerStrip: - return new ExifNumber(ExifTag.RowsPerStrip); - case ExifTagValue.TileWidth: - return new ExifNumber(ExifTag.TileWidth); - case ExifTagValue.TileLength: - return new ExifNumber(ExifTag.TileLength); - case ExifTagValue.BadFaxLines: - return new ExifNumber(ExifTag.BadFaxLines); - case ExifTagValue.ConsecutiveBadFaxLines: - return new ExifNumber(ExifTag.ConsecutiveBadFaxLines); - case ExifTagValue.PixelXDimension: - return new ExifNumber(ExifTag.PixelXDimension); - case ExifTagValue.PixelYDimension: - return new ExifNumber(ExifTag.PixelYDimension); - - case ExifTagValue.StripByteCounts: - return new ExifNumberArray(ExifTag.StripByteCounts); - case ExifTagValue.StripOffsets: - return new ExifNumberArray(ExifTag.StripOffsets); - case ExifTagValue.TileByteCounts: - return new ExifNumberArray(ExifTag.TileByteCounts); - case ExifTagValue.TileOffsets: - return new ExifNumberArray(ExifTag.TileOffsets); - case ExifTagValue.ImageLayer: - return new ExifNumberArray(ExifTag.ImageLayer); - - case ExifTagValue.XPosition: - return new ExifRational(ExifTag.XPosition); - case ExifTagValue.YPosition: - return new ExifRational(ExifTag.YPosition); - case ExifTagValue.XResolution: - return new ExifRational(ExifTag.XResolution); - case ExifTagValue.YResolution: - return new ExifRational(ExifTag.YResolution); - case ExifTagValue.BatteryLevel: - return new ExifRational(ExifTag.BatteryLevel); - case ExifTagValue.ExposureTime: - return new ExifRational(ExifTag.ExposureTime); - case ExifTagValue.FNumber: - return new ExifRational(ExifTag.FNumber); - case ExifTagValue.MDScalePixel: - return new ExifRational(ExifTag.MDScalePixel); - case ExifTagValue.CompressedBitsPerPixel: - return new ExifRational(ExifTag.CompressedBitsPerPixel); - case ExifTagValue.ApertureValue: - return new ExifRational(ExifTag.ApertureValue); - case ExifTagValue.MaxApertureValue: - return new ExifRational(ExifTag.MaxApertureValue); - case ExifTagValue.SubjectDistance: - return new ExifRational(ExifTag.SubjectDistance); - case ExifTagValue.FocalLength: - return new ExifRational(ExifTag.FocalLength); - case ExifTagValue.FlashEnergy2: - return new ExifRational(ExifTag.FlashEnergy2); - case ExifTagValue.FocalPlaneXResolution2: - return new ExifRational(ExifTag.FocalPlaneXResolution2); - case ExifTagValue.FocalPlaneYResolution2: - return new ExifRational(ExifTag.FocalPlaneYResolution2); - case ExifTagValue.ExposureIndex2: - return new ExifRational(ExifTag.ExposureIndex2); - case ExifTagValue.Humidity: - return new ExifRational(ExifTag.Humidity); - case ExifTagValue.Pressure: - return new ExifRational(ExifTag.Pressure); - case ExifTagValue.Acceleration: - return new ExifRational(ExifTag.Acceleration); - case ExifTagValue.FlashEnergy: - return new ExifRational(ExifTag.FlashEnergy); - case ExifTagValue.FocalPlaneXResolution: - return new ExifRational(ExifTag.FocalPlaneXResolution); - case ExifTagValue.FocalPlaneYResolution: - return new ExifRational(ExifTag.FocalPlaneYResolution); - case ExifTagValue.ExposureIndex: - return new ExifRational(ExifTag.ExposureIndex); - case ExifTagValue.DigitalZoomRatio: - return new ExifRational(ExifTag.DigitalZoomRatio); - case ExifTagValue.GPSAltitude: - return new ExifRational(ExifTag.GPSAltitude); - case ExifTagValue.GPSDOP: - return new ExifRational(ExifTag.GPSDOP); - case ExifTagValue.GPSSpeed: - return new ExifRational(ExifTag.GPSSpeed); - case ExifTagValue.GPSTrack: - return new ExifRational(ExifTag.GPSTrack); - case ExifTagValue.GPSImgDirection: - return new ExifRational(ExifTag.GPSImgDirection); - case ExifTagValue.GPSDestBearing: - return new ExifRational(ExifTag.GPSDestBearing); - case ExifTagValue.GPSDestDistance: - return new ExifRational(ExifTag.GPSDestDistance); - case ExifTagValue.GPSHPositioningError: - return new ExifRational(ExifTag.GPSHPositioningError); - - case ExifTagValue.WhitePoint: - return new ExifRationalArray(ExifTag.WhitePoint); - case ExifTagValue.PrimaryChromaticities: - return new ExifRationalArray(ExifTag.PrimaryChromaticities); - case ExifTagValue.YCbCrCoefficients: - return new ExifRationalArray(ExifTag.YCbCrCoefficients); - case ExifTagValue.ReferenceBlackWhite: - return new ExifRationalArray(ExifTag.ReferenceBlackWhite); - case ExifTagValue.GPSLatitude: - return new ExifRationalArray(ExifTag.GPSLatitude); - case ExifTagValue.GPSLongitude: - return new ExifRationalArray(ExifTag.GPSLongitude); - case ExifTagValue.GPSTimestamp: - return new ExifRationalArray(ExifTag.GPSTimestamp); - case ExifTagValue.GPSDestLatitude: - return new ExifRationalArray(ExifTag.GPSDestLatitude); - case ExifTagValue.GPSDestLongitude: - return new ExifRationalArray(ExifTag.GPSDestLongitude); - case ExifTagValue.LensSpecification: - return new ExifRationalArray(ExifTag.LensSpecification); - - case ExifTagValue.OldSubfileType: - return new ExifShort(ExifTag.OldSubfileType); - case ExifTagValue.Compression: - return new ExifShort(ExifTag.Compression); - case ExifTagValue.PhotometricInterpretation: - return new ExifShort(ExifTag.PhotometricInterpretation); - case ExifTagValue.Thresholding: - return new ExifShort(ExifTag.Thresholding); - case ExifTagValue.CellWidth: - return new ExifShort(ExifTag.CellWidth); - case ExifTagValue.CellLength: - return new ExifShort(ExifTag.CellLength); - case ExifTagValue.FillOrder: - return new ExifShort(ExifTag.FillOrder); - case ExifTagValue.Orientation: - return new ExifShort(ExifTag.Orientation); - case ExifTagValue.SamplesPerPixel: - return new ExifShort(ExifTag.SamplesPerPixel); - case ExifTagValue.PlanarConfiguration: - return new ExifShort(ExifTag.PlanarConfiguration); - case ExifTagValue.Predictor: - return new ExifShort(ExifTag.Predictor); - case ExifTagValue.GrayResponseUnit: - return new ExifShort(ExifTag.GrayResponseUnit); - case ExifTagValue.ResolutionUnit: - return new ExifShort(ExifTag.ResolutionUnit); - case ExifTagValue.CleanFaxData: - return new ExifShort(ExifTag.CleanFaxData); - case ExifTagValue.InkSet: - return new ExifShort(ExifTag.InkSet); - case ExifTagValue.NumberOfInks: - return new ExifShort(ExifTag.NumberOfInks); - case ExifTagValue.DotRange: - return new ExifShort(ExifTag.DotRange); - case ExifTagValue.Indexed: - return new ExifShort(ExifTag.Indexed); - case ExifTagValue.OPIProxy: - return new ExifShort(ExifTag.OPIProxy); - case ExifTagValue.JPEGProc: - return new ExifShort(ExifTag.JPEGProc); - case ExifTagValue.JPEGRestartInterval: - return new ExifShort(ExifTag.JPEGRestartInterval); - case ExifTagValue.YCbCrPositioning: - return new ExifShort(ExifTag.YCbCrPositioning); - case ExifTagValue.Rating: - return new ExifShort(ExifTag.Rating); - case ExifTagValue.RatingPercent: - return new ExifShort(ExifTag.RatingPercent); - case ExifTagValue.ExposureProgram: - return new ExifShort(ExifTag.ExposureProgram); - case ExifTagValue.Interlace: - return new ExifShort(ExifTag.Interlace); - case ExifTagValue.SelfTimerMode: - return new ExifShort(ExifTag.SelfTimerMode); - case ExifTagValue.SensitivityType: - return new ExifShort(ExifTag.SensitivityType); - case ExifTagValue.MeteringMode: - return new ExifShort(ExifTag.MeteringMode); - case ExifTagValue.LightSource: - return new ExifShort(ExifTag.LightSource); - case ExifTagValue.FocalPlaneResolutionUnit2: - return new ExifShort(ExifTag.FocalPlaneResolutionUnit2); - case ExifTagValue.SensingMethod2: - return new ExifShort(ExifTag.SensingMethod2); - case ExifTagValue.Flash: - return new ExifShort(ExifTag.Flash); - case ExifTagValue.ColorSpace: - return new ExifShort(ExifTag.ColorSpace); - case ExifTagValue.FocalPlaneResolutionUnit: - return new ExifShort(ExifTag.FocalPlaneResolutionUnit); - case ExifTagValue.SensingMethod: - return new ExifShort(ExifTag.SensingMethod); - case ExifTagValue.CustomRendered: - return new ExifShort(ExifTag.CustomRendered); - case ExifTagValue.ExposureMode: - return new ExifShort(ExifTag.ExposureMode); - case ExifTagValue.WhiteBalance: - return new ExifShort(ExifTag.WhiteBalance); - case ExifTagValue.FocalLengthIn35mmFilm: - return new ExifShort(ExifTag.FocalLengthIn35mmFilm); - case ExifTagValue.SceneCaptureType: - return new ExifShort(ExifTag.SceneCaptureType); - case ExifTagValue.GainControl: - return new ExifShort(ExifTag.GainControl); - case ExifTagValue.Contrast: - return new ExifShort(ExifTag.Contrast); - case ExifTagValue.Saturation: - return new ExifShort(ExifTag.Saturation); - case ExifTagValue.Sharpness: - return new ExifShort(ExifTag.Sharpness); - case ExifTagValue.SubjectDistanceRange: - return new ExifShort(ExifTag.SubjectDistanceRange); - case ExifTagValue.GPSDifferential: - return new ExifShort(ExifTag.GPSDifferential); - - case ExifTagValue.BitsPerSample: - return new ExifShortArray(ExifTag.BitsPerSample); - case ExifTagValue.MinSampleValue: - return new ExifShortArray(ExifTag.MinSampleValue); - case ExifTagValue.MaxSampleValue: - return new ExifShortArray(ExifTag.MaxSampleValue); - case ExifTagValue.GrayResponseCurve: - return new ExifShortArray(ExifTag.GrayResponseCurve); - case ExifTagValue.ColorMap: - return new ExifShortArray(ExifTag.ColorMap); - case ExifTagValue.ExtraSamples: - return new ExifShortArray(ExifTag.ExtraSamples); - case ExifTagValue.PageNumber: - return new ExifShortArray(ExifTag.PageNumber); - case ExifTagValue.TransferFunction: - return new ExifShortArray(ExifTag.TransferFunction); - case ExifTagValue.HalftoneHints: - return new ExifShortArray(ExifTag.HalftoneHints); - case ExifTagValue.SampleFormat: - return new ExifShortArray(ExifTag.SampleFormat); - case ExifTagValue.TransferRange: - return new ExifShortArray(ExifTag.TransferRange); - case ExifTagValue.DefaultImageColor: - return new ExifShortArray(ExifTag.DefaultImageColor); - case ExifTagValue.JPEGLosslessPredictors: - return new ExifShortArray(ExifTag.JPEGLosslessPredictors); - case ExifTagValue.JPEGPointTransforms: - return new ExifShortArray(ExifTag.JPEGPointTransforms); - case ExifTagValue.YCbCrSubsampling: - return new ExifShortArray(ExifTag.YCbCrSubsampling); - case ExifTagValue.CFARepeatPatternDim: - return new ExifShortArray(ExifTag.CFARepeatPatternDim); - case ExifTagValue.IntergraphPacketData: - return new ExifShortArray(ExifTag.IntergraphPacketData); - case ExifTagValue.ISOSpeedRatings: - return new ExifShortArray(ExifTag.ISOSpeedRatings); - case ExifTagValue.SubjectArea: - return new ExifShortArray(ExifTag.SubjectArea); - case ExifTagValue.SubjectLocation: - return new ExifShortArray(ExifTag.SubjectLocation); - - case ExifTagValue.ShutterSpeedValue: - return new ExifSignedRational(ExifTag.ShutterSpeedValue); - case ExifTagValue.BrightnessValue: - return new ExifSignedRational(ExifTag.BrightnessValue); - case ExifTagValue.ExposureBiasValue: - return new ExifSignedRational(ExifTag.ExposureBiasValue); - case ExifTagValue.AmbientTemperature: - return new ExifSignedRational(ExifTag.AmbientTemperature); - case ExifTagValue.WaterDepth: - return new ExifSignedRational(ExifTag.WaterDepth); - case ExifTagValue.CameraElevationAngle: - return new ExifSignedRational(ExifTag.CameraElevationAngle); - - case ExifTagValue.Decode: - return new ExifSignedRationalArray(ExifTag.Decode); - - case ExifTagValue.TimeZoneOffset: - return new ExifSignedShortArray(ExifTag.TimeZoneOffset); - - case ExifTagValue.ImageDescription: - return new ExifString(ExifTag.ImageDescription); - case ExifTagValue.Make: - return new ExifString(ExifTag.Make); - case ExifTagValue.Model: - return new ExifString(ExifTag.Model); - case ExifTagValue.Software: - return new ExifString(ExifTag.Software); - case ExifTagValue.DateTime: - return new ExifString(ExifTag.DateTime); - case ExifTagValue.Artist: - return new ExifString(ExifTag.Artist); - case ExifTagValue.HostComputer: - return new ExifString(ExifTag.HostComputer); - case ExifTagValue.Copyright: - return new ExifString(ExifTag.Copyright); - case ExifTagValue.DocumentName: - return new ExifString(ExifTag.DocumentName); - case ExifTagValue.PageName: - return new ExifString(ExifTag.PageName); - case ExifTagValue.InkNames: - return new ExifString(ExifTag.InkNames); - case ExifTagValue.TargetPrinter: - return new ExifString(ExifTag.TargetPrinter); - case ExifTagValue.ImageID: - return new ExifString(ExifTag.ImageID); - case ExifTagValue.MDLabName: - return new ExifString(ExifTag.MDLabName); - case ExifTagValue.MDSampleInfo: - return new ExifString(ExifTag.MDSampleInfo); - case ExifTagValue.MDPrepDate: - return new ExifString(ExifTag.MDPrepDate); - case ExifTagValue.MDPrepTime: - return new ExifString(ExifTag.MDPrepTime); - case ExifTagValue.MDFileUnits: - return new ExifString(ExifTag.MDFileUnits); - case ExifTagValue.SEMInfo: - return new ExifString(ExifTag.SEMInfo); - case ExifTagValue.SpectralSensitivity: - return new ExifString(ExifTag.SpectralSensitivity); - case ExifTagValue.DateTimeOriginal: - return new ExifString(ExifTag.DateTimeOriginal); - case ExifTagValue.DateTimeDigitized: - return new ExifString(ExifTag.DateTimeDigitized); - case ExifTagValue.SubsecTime: - return new ExifString(ExifTag.SubsecTime); - case ExifTagValue.SubsecTimeOriginal: - return new ExifString(ExifTag.SubsecTimeOriginal); - case ExifTagValue.SubsecTimeDigitized: - return new ExifString(ExifTag.SubsecTimeDigitized); - case ExifTagValue.RelatedSoundFile: - return new ExifString(ExifTag.RelatedSoundFile); - case ExifTagValue.FaxSubaddress: - return new ExifString(ExifTag.FaxSubaddress); - case ExifTagValue.OffsetTime: - return new ExifString(ExifTag.OffsetTime); - case ExifTagValue.OffsetTimeOriginal: - return new ExifString(ExifTag.OffsetTimeOriginal); - case ExifTagValue.OffsetTimeDigitized: - return new ExifString(ExifTag.OffsetTimeDigitized); - case ExifTagValue.SecurityClassification: - return new ExifString(ExifTag.SecurityClassification); - case ExifTagValue.ImageHistory: - return new ExifString(ExifTag.ImageHistory); - case ExifTagValue.ImageUniqueID: - return new ExifString(ExifTag.ImageUniqueID); - case ExifTagValue.OwnerName: - return new ExifString(ExifTag.OwnerName); - case ExifTagValue.SerialNumber: - return new ExifString(ExifTag.SerialNumber); - case ExifTagValue.LensMake: - return new ExifString(ExifTag.LensMake); - case ExifTagValue.LensModel: - return new ExifString(ExifTag.LensModel); - case ExifTagValue.LensSerialNumber: - return new ExifString(ExifTag.LensSerialNumber); - case ExifTagValue.GDALMetadata: - return new ExifString(ExifTag.GDALMetadata); - case ExifTagValue.GDALNoData: - return new ExifString(ExifTag.GDALNoData); - case ExifTagValue.GPSLatitudeRef: - return new ExifString(ExifTag.GPSLatitudeRef); - case ExifTagValue.GPSLongitudeRef: - return new ExifString(ExifTag.GPSLongitudeRef); - case ExifTagValue.GPSSatellites: - return new ExifString(ExifTag.GPSSatellites); - case ExifTagValue.GPSStatus: - return new ExifString(ExifTag.GPSStatus); - case ExifTagValue.GPSMeasureMode: - return new ExifString(ExifTag.GPSMeasureMode); - case ExifTagValue.GPSSpeedRef: - return new ExifString(ExifTag.GPSSpeedRef); - case ExifTagValue.GPSTrackRef: - return new ExifString(ExifTag.GPSTrackRef); - case ExifTagValue.GPSImgDirectionRef: - return new ExifString(ExifTag.GPSImgDirectionRef); - case ExifTagValue.GPSMapDatum: - return new ExifString(ExifTag.GPSMapDatum); - case ExifTagValue.GPSDestLatitudeRef: - return new ExifString(ExifTag.GPSDestLatitudeRef); - case ExifTagValue.GPSDestLongitudeRef: - return new ExifString(ExifTag.GPSDestLongitudeRef); - case ExifTagValue.GPSDestBearingRef: - return new ExifString(ExifTag.GPSDestBearingRef); - case ExifTagValue.GPSDestDistanceRef: - return new ExifString(ExifTag.GPSDestDistanceRef); - case ExifTagValue.GPSDateStamp: - return new ExifString(ExifTag.GPSDateStamp); - - case ExifTagValue.FileSource: - return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined); - case ExifTagValue.SceneType: - return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined); - - case ExifTagValue.JPEGTables: - return new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined); - case ExifTagValue.OECF: - return new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined); - case ExifTagValue.ExifVersion: - return new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined); - case ExifTagValue.ComponentsConfiguration: - return new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined); - case ExifTagValue.MakerNote: - return new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined); - case ExifTagValue.FlashpixVersion: - return new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined); - case ExifTagValue.SpatialFrequencyResponse: - return new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined); - case ExifTagValue.SpatialFrequencyResponse2: - return new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined); - case ExifTagValue.Noise: - return new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined); - case ExifTagValue.CFAPattern: - return new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined); - case ExifTagValue.DeviceSettingDescription: - return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined); - case ExifTagValue.ImageSourceData: - return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined); - - case ExifTagValue.XPTitle: - return new ExifUcs2String(ExifTag.XPTitle); - case ExifTagValue.XPComment: - return new ExifUcs2String(ExifTag.XPComment); - case ExifTagValue.XPAuthor: - return new ExifUcs2String(ExifTag.XPAuthor); - case ExifTagValue.XPKeywords: - return new ExifUcs2String(ExifTag.XPKeywords); - case ExifTagValue.XPSubject: - return new ExifUcs2String(ExifTag.XPSubject); - - case ExifTagValue.UserComment: - return new ExifEncodedString(ExifTag.UserComment); - case ExifTagValue.GPSProcessingMethod: - return new ExifEncodedString(ExifTag.GPSProcessingMethod); - case ExifTagValue.GPSAreaInformation: - return new ExifEncodedString(ExifTag.GPSAreaInformation); - - default: - return null; - } - } + ExifTagValue.FaxProfile => new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte), + ExifTagValue.ModeNumber => new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte), + ExifTagValue.GPSAltitudeRef => new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte), + ExifTagValue.ClipPath => new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte), + ExifTagValue.VersionYear => new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte), + ExifTagValue.XMP => new ExifByteArray(ExifTag.XMP, ExifDataType.Byte), + ExifTagValue.CFAPattern2 => new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte), + ExifTagValue.TIFFEPStandardID => new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte), + ExifTagValue.GPSVersionID => new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte), + ExifTagValue.PixelScale => new ExifDoubleArray(ExifTag.PixelScale), + ExifTagValue.IntergraphMatrix => new ExifDoubleArray(ExifTag.IntergraphMatrix), + ExifTagValue.ModelTiePoint => new ExifDoubleArray(ExifTag.ModelTiePoint), + ExifTagValue.ModelTransform => new ExifDoubleArray(ExifTag.ModelTransform), + ExifTagValue.SubfileType => new ExifLong(ExifTag.SubfileType), + ExifTagValue.SubIFDOffset => new ExifLong(ExifTag.SubIFDOffset), + ExifTagValue.GPSIFDOffset => new ExifLong(ExifTag.GPSIFDOffset), + ExifTagValue.T4Options => new ExifLong(ExifTag.T4Options), + ExifTagValue.T6Options => new ExifLong(ExifTag.T6Options), + ExifTagValue.XClipPathUnits => new ExifLong(ExifTag.XClipPathUnits), + ExifTagValue.YClipPathUnits => new ExifLong(ExifTag.YClipPathUnits), + ExifTagValue.ProfileType => new ExifLong(ExifTag.ProfileType), + ExifTagValue.CodingMethods => new ExifLong(ExifTag.CodingMethods), + ExifTagValue.T82ptions => new ExifLong(ExifTag.T82ptions), + ExifTagValue.JPEGInterchangeFormat => new ExifLong(ExifTag.JPEGInterchangeFormat), + ExifTagValue.JPEGInterchangeFormatLength => new ExifLong(ExifTag.JPEGInterchangeFormatLength), + ExifTagValue.MDFileTag => new ExifLong(ExifTag.MDFileTag), + ExifTagValue.StandardOutputSensitivity => new ExifLong(ExifTag.StandardOutputSensitivity), + ExifTagValue.RecommendedExposureIndex => new ExifLong(ExifTag.RecommendedExposureIndex), + ExifTagValue.ISOSpeed => new ExifLong(ExifTag.ISOSpeed), + ExifTagValue.ISOSpeedLatitudeyyy => new ExifLong(ExifTag.ISOSpeedLatitudeyyy), + ExifTagValue.ISOSpeedLatitudezzz => new ExifLong(ExifTag.ISOSpeedLatitudezzz), + ExifTagValue.FaxRecvParams => new ExifLong(ExifTag.FaxRecvParams), + ExifTagValue.FaxRecvTime => new ExifLong(ExifTag.FaxRecvTime), + ExifTagValue.ImageNumber => new ExifLong(ExifTag.ImageNumber), + ExifTagValue.FreeOffsets => new ExifLongArray(ExifTag.FreeOffsets), + ExifTagValue.FreeByteCounts => new ExifLongArray(ExifTag.FreeByteCounts), + ExifTagValue.ColorResponseUnit => new ExifLongArray(ExifTag.ColorResponseUnit), + ExifTagValue.SMinSampleValue => new ExifLongArray(ExifTag.SMinSampleValue), + ExifTagValue.SMaxSampleValue => new ExifLongArray(ExifTag.SMaxSampleValue), + ExifTagValue.JPEGQTables => new ExifLongArray(ExifTag.JPEGQTables), + ExifTagValue.JPEGDCTables => new ExifLongArray(ExifTag.JPEGDCTables), + ExifTagValue.JPEGACTables => new ExifLongArray(ExifTag.JPEGACTables), + ExifTagValue.StripRowCounts => new ExifLongArray(ExifTag.StripRowCounts), + ExifTagValue.IntergraphRegisters => new ExifLongArray(ExifTag.IntergraphRegisters), + ExifTagValue.SubIFDs => new ExifLongArray(ExifTag.SubIFDs), + ExifTagValue.ImageWidth => new ExifNumber(ExifTag.ImageWidth), + ExifTagValue.ImageLength => new ExifNumber(ExifTag.ImageLength), + ExifTagValue.RowsPerStrip => new ExifNumber(ExifTag.RowsPerStrip), + ExifTagValue.TileWidth => new ExifNumber(ExifTag.TileWidth), + ExifTagValue.TileLength => new ExifNumber(ExifTag.TileLength), + ExifTagValue.BadFaxLines => new ExifNumber(ExifTag.BadFaxLines), + ExifTagValue.ConsecutiveBadFaxLines => new ExifNumber(ExifTag.ConsecutiveBadFaxLines), + ExifTagValue.PixelXDimension => new ExifNumber(ExifTag.PixelXDimension), + ExifTagValue.PixelYDimension => new ExifNumber(ExifTag.PixelYDimension), + ExifTagValue.StripByteCounts => new ExifNumberArray(ExifTag.StripByteCounts), + ExifTagValue.StripOffsets => new ExifNumberArray(ExifTag.StripOffsets), + ExifTagValue.TileByteCounts => new ExifNumberArray(ExifTag.TileByteCounts), + ExifTagValue.TileOffsets => new ExifNumberArray(ExifTag.TileOffsets), + ExifTagValue.ImageLayer => new ExifNumberArray(ExifTag.ImageLayer), + ExifTagValue.XPosition => new ExifRational(ExifTag.XPosition), + ExifTagValue.YPosition => new ExifRational(ExifTag.YPosition), + ExifTagValue.XResolution => new ExifRational(ExifTag.XResolution), + ExifTagValue.YResolution => new ExifRational(ExifTag.YResolution), + ExifTagValue.BatteryLevel => new ExifRational(ExifTag.BatteryLevel), + ExifTagValue.ExposureTime => new ExifRational(ExifTag.ExposureTime), + ExifTagValue.FNumber => new ExifRational(ExifTag.FNumber), + ExifTagValue.MDScalePixel => new ExifRational(ExifTag.MDScalePixel), + ExifTagValue.CompressedBitsPerPixel => new ExifRational(ExifTag.CompressedBitsPerPixel), + ExifTagValue.ApertureValue => new ExifRational(ExifTag.ApertureValue), + ExifTagValue.MaxApertureValue => new ExifRational(ExifTag.MaxApertureValue), + ExifTagValue.SubjectDistance => new ExifRational(ExifTag.SubjectDistance), + ExifTagValue.FocalLength => new ExifRational(ExifTag.FocalLength), + ExifTagValue.FlashEnergy2 => new ExifRational(ExifTag.FlashEnergy2), + ExifTagValue.FocalPlaneXResolution2 => new ExifRational(ExifTag.FocalPlaneXResolution2), + ExifTagValue.FocalPlaneYResolution2 => new ExifRational(ExifTag.FocalPlaneYResolution2), + ExifTagValue.ExposureIndex2 => new ExifRational(ExifTag.ExposureIndex2), + ExifTagValue.Humidity => new ExifRational(ExifTag.Humidity), + ExifTagValue.Pressure => new ExifRational(ExifTag.Pressure), + ExifTagValue.Acceleration => new ExifRational(ExifTag.Acceleration), + ExifTagValue.FlashEnergy => new ExifRational(ExifTag.FlashEnergy), + ExifTagValue.FocalPlaneXResolution => new ExifRational(ExifTag.FocalPlaneXResolution), + ExifTagValue.FocalPlaneYResolution => new ExifRational(ExifTag.FocalPlaneYResolution), + ExifTagValue.ExposureIndex => new ExifRational(ExifTag.ExposureIndex), + ExifTagValue.DigitalZoomRatio => new ExifRational(ExifTag.DigitalZoomRatio), + ExifTagValue.GPSAltitude => new ExifRational(ExifTag.GPSAltitude), + ExifTagValue.GPSDOP => new ExifRational(ExifTag.GPSDOP), + ExifTagValue.GPSSpeed => new ExifRational(ExifTag.GPSSpeed), + ExifTagValue.GPSTrack => new ExifRational(ExifTag.GPSTrack), + ExifTagValue.GPSImgDirection => new ExifRational(ExifTag.GPSImgDirection), + ExifTagValue.GPSDestBearing => new ExifRational(ExifTag.GPSDestBearing), + ExifTagValue.GPSDestDistance => new ExifRational(ExifTag.GPSDestDistance), + ExifTagValue.GPSHPositioningError => new ExifRational(ExifTag.GPSHPositioningError), + ExifTagValue.WhitePoint => new ExifRationalArray(ExifTag.WhitePoint), + ExifTagValue.PrimaryChromaticities => new ExifRationalArray(ExifTag.PrimaryChromaticities), + ExifTagValue.YCbCrCoefficients => new ExifRationalArray(ExifTag.YCbCrCoefficients), + ExifTagValue.ReferenceBlackWhite => new ExifRationalArray(ExifTag.ReferenceBlackWhite), + ExifTagValue.GPSLatitude => new ExifRationalArray(ExifTag.GPSLatitude), + ExifTagValue.GPSLongitude => new ExifRationalArray(ExifTag.GPSLongitude), + ExifTagValue.GPSTimestamp => new ExifRationalArray(ExifTag.GPSTimestamp), + ExifTagValue.GPSDestLatitude => new ExifRationalArray(ExifTag.GPSDestLatitude), + ExifTagValue.GPSDestLongitude => new ExifRationalArray(ExifTag.GPSDestLongitude), + ExifTagValue.LensSpecification => new ExifRationalArray(ExifTag.LensSpecification), + ExifTagValue.OldSubfileType => new ExifShort(ExifTag.OldSubfileType), + ExifTagValue.Compression => new ExifShort(ExifTag.Compression), + ExifTagValue.PhotometricInterpretation => new ExifShort(ExifTag.PhotometricInterpretation), + ExifTagValue.Thresholding => new ExifShort(ExifTag.Thresholding), + ExifTagValue.CellWidth => new ExifShort(ExifTag.CellWidth), + ExifTagValue.CellLength => new ExifShort(ExifTag.CellLength), + ExifTagValue.FillOrder => new ExifShort(ExifTag.FillOrder), + ExifTagValue.Orientation => new ExifShort(ExifTag.Orientation), + ExifTagValue.SamplesPerPixel => new ExifShort(ExifTag.SamplesPerPixel), + ExifTagValue.PlanarConfiguration => new ExifShort(ExifTag.PlanarConfiguration), + ExifTagValue.Predictor => new ExifShort(ExifTag.Predictor), + ExifTagValue.GrayResponseUnit => new ExifShort(ExifTag.GrayResponseUnit), + ExifTagValue.ResolutionUnit => new ExifShort(ExifTag.ResolutionUnit), + ExifTagValue.CleanFaxData => new ExifShort(ExifTag.CleanFaxData), + ExifTagValue.InkSet => new ExifShort(ExifTag.InkSet), + ExifTagValue.NumberOfInks => new ExifShort(ExifTag.NumberOfInks), + ExifTagValue.DotRange => new ExifShort(ExifTag.DotRange), + ExifTagValue.Indexed => new ExifShort(ExifTag.Indexed), + ExifTagValue.OPIProxy => new ExifShort(ExifTag.OPIProxy), + ExifTagValue.JPEGProc => new ExifShort(ExifTag.JPEGProc), + ExifTagValue.JPEGRestartInterval => new ExifShort(ExifTag.JPEGRestartInterval), + ExifTagValue.YCbCrPositioning => new ExifShort(ExifTag.YCbCrPositioning), + ExifTagValue.Rating => new ExifShort(ExifTag.Rating), + ExifTagValue.RatingPercent => new ExifShort(ExifTag.RatingPercent), + ExifTagValue.ExposureProgram => new ExifShort(ExifTag.ExposureProgram), + ExifTagValue.Interlace => new ExifShort(ExifTag.Interlace), + ExifTagValue.SelfTimerMode => new ExifShort(ExifTag.SelfTimerMode), + ExifTagValue.SensitivityType => new ExifShort(ExifTag.SensitivityType), + ExifTagValue.MeteringMode => new ExifShort(ExifTag.MeteringMode), + ExifTagValue.LightSource => new ExifShort(ExifTag.LightSource), + ExifTagValue.FocalPlaneResolutionUnit2 => new ExifShort(ExifTag.FocalPlaneResolutionUnit2), + ExifTagValue.SensingMethod2 => new ExifShort(ExifTag.SensingMethod2), + ExifTagValue.Flash => new ExifShort(ExifTag.Flash), + ExifTagValue.ColorSpace => new ExifShort(ExifTag.ColorSpace), + ExifTagValue.FocalPlaneResolutionUnit => new ExifShort(ExifTag.FocalPlaneResolutionUnit), + ExifTagValue.SensingMethod => new ExifShort(ExifTag.SensingMethod), + ExifTagValue.CustomRendered => new ExifShort(ExifTag.CustomRendered), + ExifTagValue.ExposureMode => new ExifShort(ExifTag.ExposureMode), + ExifTagValue.WhiteBalance => new ExifShort(ExifTag.WhiteBalance), + ExifTagValue.FocalLengthIn35mmFilm => new ExifShort(ExifTag.FocalLengthIn35mmFilm), + ExifTagValue.SceneCaptureType => new ExifShort(ExifTag.SceneCaptureType), + ExifTagValue.GainControl => new ExifShort(ExifTag.GainControl), + ExifTagValue.Contrast => new ExifShort(ExifTag.Contrast), + ExifTagValue.Saturation => new ExifShort(ExifTag.Saturation), + ExifTagValue.Sharpness => new ExifShort(ExifTag.Sharpness), + ExifTagValue.SubjectDistanceRange => new ExifShort(ExifTag.SubjectDistanceRange), + ExifTagValue.GPSDifferential => new ExifShort(ExifTag.GPSDifferential), + ExifTagValue.BitsPerSample => new ExifShortArray(ExifTag.BitsPerSample), + ExifTagValue.MinSampleValue => new ExifShortArray(ExifTag.MinSampleValue), + ExifTagValue.MaxSampleValue => new ExifShortArray(ExifTag.MaxSampleValue), + ExifTagValue.GrayResponseCurve => new ExifShortArray(ExifTag.GrayResponseCurve), + ExifTagValue.ColorMap => new ExifShortArray(ExifTag.ColorMap), + ExifTagValue.ExtraSamples => new ExifShortArray(ExifTag.ExtraSamples), + ExifTagValue.PageNumber => new ExifShortArray(ExifTag.PageNumber), + ExifTagValue.TransferFunction => new ExifShortArray(ExifTag.TransferFunction), + ExifTagValue.HalftoneHints => new ExifShortArray(ExifTag.HalftoneHints), + ExifTagValue.SampleFormat => new ExifShortArray(ExifTag.SampleFormat), + ExifTagValue.TransferRange => new ExifShortArray(ExifTag.TransferRange), + ExifTagValue.DefaultImageColor => new ExifShortArray(ExifTag.DefaultImageColor), + ExifTagValue.JPEGLosslessPredictors => new ExifShortArray(ExifTag.JPEGLosslessPredictors), + ExifTagValue.JPEGPointTransforms => new ExifShortArray(ExifTag.JPEGPointTransforms), + ExifTagValue.YCbCrSubsampling => new ExifShortArray(ExifTag.YCbCrSubsampling), + ExifTagValue.CFARepeatPatternDim => new ExifShortArray(ExifTag.CFARepeatPatternDim), + ExifTagValue.IntergraphPacketData => new ExifShortArray(ExifTag.IntergraphPacketData), + ExifTagValue.ISOSpeedRatings => new ExifShortArray(ExifTag.ISOSpeedRatings), + ExifTagValue.SubjectArea => new ExifShortArray(ExifTag.SubjectArea), + ExifTagValue.SubjectLocation => new ExifShortArray(ExifTag.SubjectLocation), + ExifTagValue.ShutterSpeedValue => new ExifSignedRational(ExifTag.ShutterSpeedValue), + ExifTagValue.BrightnessValue => new ExifSignedRational(ExifTag.BrightnessValue), + ExifTagValue.ExposureBiasValue => new ExifSignedRational(ExifTag.ExposureBiasValue), + ExifTagValue.AmbientTemperature => new ExifSignedRational(ExifTag.AmbientTemperature), + ExifTagValue.WaterDepth => new ExifSignedRational(ExifTag.WaterDepth), + ExifTagValue.CameraElevationAngle => new ExifSignedRational(ExifTag.CameraElevationAngle), + ExifTagValue.Decode => new ExifSignedRationalArray(ExifTag.Decode), + ExifTagValue.TimeZoneOffset => new ExifSignedShortArray(ExifTag.TimeZoneOffset), + ExifTagValue.ImageDescription => new ExifString(ExifTag.ImageDescription), + ExifTagValue.Make => new ExifString(ExifTag.Make), + ExifTagValue.Model => new ExifString(ExifTag.Model), + ExifTagValue.Software => new ExifString(ExifTag.Software), + ExifTagValue.DateTime => new ExifString(ExifTag.DateTime), + ExifTagValue.Artist => new ExifString(ExifTag.Artist), + ExifTagValue.HostComputer => new ExifString(ExifTag.HostComputer), + ExifTagValue.Copyright => new ExifString(ExifTag.Copyright), + ExifTagValue.DocumentName => new ExifString(ExifTag.DocumentName), + ExifTagValue.PageName => new ExifString(ExifTag.PageName), + ExifTagValue.InkNames => new ExifString(ExifTag.InkNames), + ExifTagValue.TargetPrinter => new ExifString(ExifTag.TargetPrinter), + ExifTagValue.ImageID => new ExifString(ExifTag.ImageID), + ExifTagValue.MDLabName => new ExifString(ExifTag.MDLabName), + ExifTagValue.MDSampleInfo => new ExifString(ExifTag.MDSampleInfo), + ExifTagValue.MDPrepDate => new ExifString(ExifTag.MDPrepDate), + ExifTagValue.MDPrepTime => new ExifString(ExifTag.MDPrepTime), + ExifTagValue.MDFileUnits => new ExifString(ExifTag.MDFileUnits), + ExifTagValue.SEMInfo => new ExifString(ExifTag.SEMInfo), + ExifTagValue.SpectralSensitivity => new ExifString(ExifTag.SpectralSensitivity), + ExifTagValue.DateTimeOriginal => new ExifString(ExifTag.DateTimeOriginal), + ExifTagValue.DateTimeDigitized => new ExifString(ExifTag.DateTimeDigitized), + ExifTagValue.SubsecTime => new ExifString(ExifTag.SubsecTime), + ExifTagValue.SubsecTimeOriginal => new ExifString(ExifTag.SubsecTimeOriginal), + ExifTagValue.SubsecTimeDigitized => new ExifString(ExifTag.SubsecTimeDigitized), + ExifTagValue.RelatedSoundFile => new ExifString(ExifTag.RelatedSoundFile), + ExifTagValue.FaxSubaddress => new ExifString(ExifTag.FaxSubaddress), + ExifTagValue.OffsetTime => new ExifString(ExifTag.OffsetTime), + ExifTagValue.OffsetTimeOriginal => new ExifString(ExifTag.OffsetTimeOriginal), + ExifTagValue.OffsetTimeDigitized => new ExifString(ExifTag.OffsetTimeDigitized), + ExifTagValue.SecurityClassification => new ExifString(ExifTag.SecurityClassification), + ExifTagValue.ImageHistory => new ExifString(ExifTag.ImageHistory), + ExifTagValue.ImageUniqueID => new ExifString(ExifTag.ImageUniqueID), + ExifTagValue.OwnerName => new ExifString(ExifTag.OwnerName), + ExifTagValue.SerialNumber => new ExifString(ExifTag.SerialNumber), + ExifTagValue.LensMake => new ExifString(ExifTag.LensMake), + ExifTagValue.LensModel => new ExifString(ExifTag.LensModel), + ExifTagValue.LensSerialNumber => new ExifString(ExifTag.LensSerialNumber), + ExifTagValue.GDALMetadata => new ExifString(ExifTag.GDALMetadata), + ExifTagValue.GDALNoData => new ExifString(ExifTag.GDALNoData), + ExifTagValue.GPSLatitudeRef => new ExifString(ExifTag.GPSLatitudeRef), + ExifTagValue.GPSLongitudeRef => new ExifString(ExifTag.GPSLongitudeRef), + ExifTagValue.GPSSatellites => new ExifString(ExifTag.GPSSatellites), + ExifTagValue.GPSStatus => new ExifString(ExifTag.GPSStatus), + ExifTagValue.GPSMeasureMode => new ExifString(ExifTag.GPSMeasureMode), + ExifTagValue.GPSSpeedRef => new ExifString(ExifTag.GPSSpeedRef), + ExifTagValue.GPSTrackRef => new ExifString(ExifTag.GPSTrackRef), + ExifTagValue.GPSImgDirectionRef => new ExifString(ExifTag.GPSImgDirectionRef), + ExifTagValue.GPSMapDatum => new ExifString(ExifTag.GPSMapDatum), + ExifTagValue.GPSDestLatitudeRef => new ExifString(ExifTag.GPSDestLatitudeRef), + ExifTagValue.GPSDestLongitudeRef => new ExifString(ExifTag.GPSDestLongitudeRef), + ExifTagValue.GPSDestBearingRef => new ExifString(ExifTag.GPSDestBearingRef), + ExifTagValue.GPSDestDistanceRef => new ExifString(ExifTag.GPSDestDistanceRef), + ExifTagValue.GPSDateStamp => new ExifString(ExifTag.GPSDateStamp), + ExifTagValue.FileSource => new ExifByte(ExifTag.FileSource, ExifDataType.Undefined), + ExifTagValue.SceneType => new ExifByte(ExifTag.SceneType, ExifDataType.Undefined), + ExifTagValue.JPEGTables => new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined), + ExifTagValue.OECF => new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined), + ExifTagValue.ExifVersion => new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined), + ExifTagValue.ComponentsConfiguration => new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined), + ExifTagValue.MakerNote => new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined), + ExifTagValue.FlashpixVersion => new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined), + ExifTagValue.SpatialFrequencyResponse => new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined), + ExifTagValue.SpatialFrequencyResponse2 => new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined), + ExifTagValue.Noise => new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined), + ExifTagValue.CFAPattern => new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined), + ExifTagValue.DeviceSettingDescription => new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined), + ExifTagValue.ImageSourceData => new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined), + ExifTagValue.XPTitle => new ExifUcs2String(ExifTag.XPTitle), + ExifTagValue.XPComment => new ExifUcs2String(ExifTag.XPComment), + ExifTagValue.XPAuthor => new ExifUcs2String(ExifTag.XPAuthor), + ExifTagValue.XPKeywords => new ExifUcs2String(ExifTag.XPKeywords), + ExifTagValue.XPSubject => new ExifUcs2String(ExifTag.XPSubject), + ExifTagValue.UserComment => new ExifEncodedString(ExifTag.UserComment), + ExifTagValue.GPSProcessingMethod => new ExifEncodedString(ExifTag.GPSProcessingMethod), + ExifTagValue.GPSAreaInformation => new ExifEncodedString(ExifTag.GPSAreaInformation), + _ => null, + }; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs index c5b1574f30..3bb5551920 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs @@ -4,35 +4,35 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif; /// -/// A value of the exif profile. +/// A value of the Exif profile. /// public interface IExifValue : IDeepCloneable { /// - /// Gets the data type of the exif value. + /// Gets the data type of the Exif value. /// - ExifDataType DataType { get; } + public ExifDataType DataType { get; } /// /// Gets a value indicating whether the value is an array. /// - bool IsArray { get; } + public bool IsArray { get; } /// - /// Gets the tag of the exif value. + /// Gets the tag of the Exif value. /// - ExifTag Tag { get; } + public ExifTag Tag { get; } /// - /// Gets the value of this exif value. + /// Gets the value of this Exif value. /// - /// The value of this exif value. - object? GetValue(); + /// The value of this Exif value. + public object? GetValue(); /// - /// Sets the value of this exif value. + /// Sets the value of this Exif value. /// - /// The value of this exif value. + /// The value of this Exif value. /// A value indicating whether the value could be set. - bool TrySetValue(object? value); + public bool TrySetValue(object? value); } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs index 3cf66c0c16..8663ebccc2 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs @@ -18,13 +18,13 @@ public IccOneDimensionalCurve ReadOneDimensionalCurve() { ushort segmentCount = this.ReadUInt16(); this.AddIndex(2); // 2 bytes reserved - var breakPoints = new float[segmentCount - 1]; + float[] breakPoints = new float[segmentCount - 1]; for (int i = 0; i < breakPoints.Length; i++) { breakPoints[i] = this.ReadSingle(); } - var segments = new IccCurveSegment[segmentCount]; + IccCurveSegment[] segments = new IccCurveSegment[segmentCount]; for (int i = 0; i < segmentCount; i++) { segments[i] = this.ReadCurveSegment(); @@ -40,20 +40,20 @@ public IccOneDimensionalCurve ReadOneDimensionalCurve() /// The read curve public IccResponseCurve ReadResponseCurve(int channelCount) { - var type = (IccCurveMeasurementEncodings)this.ReadUInt32(); - var measurement = new uint[channelCount]; + IccCurveMeasurementEncodings type = (IccCurveMeasurementEncodings)this.ReadUInt32(); + uint[] measurement = new uint[channelCount]; for (int i = 0; i < channelCount; i++) { measurement[i] = this.ReadUInt32(); } - var xyzValues = new Vector3[channelCount]; + Vector3[] xyzValues = new Vector3[channelCount]; for (int i = 0; i < channelCount; i++) { xyzValues[i] = this.ReadXyzNumber(); } - var response = new IccResponseNumber[channelCount][]; + IccResponseNumber[][] response = new IccResponseNumber[channelCount][]; for (int i = 0; i < channelCount; i++) { response[i] = new IccResponseNumber[measurement[i]]; @@ -121,7 +121,7 @@ public IccParametricCurve ReadParametricCurve() /// The read segment public IccCurveSegment ReadCurveSegment() { - var signature = (IccCurveSegmentSignature)this.ReadUInt32(); + IccCurveSegmentSignature signature = (IccCurveSegmentSignature)this.ReadUInt32(); this.AddIndex(4); // 4 bytes reserved switch (signature) @@ -141,7 +141,7 @@ public IccCurveSegment ReadCurveSegment() /// The read segment public IccFormulaCurveElement ReadFormulaCurveElement() { - var type = (IccFormulaCurveType)this.ReadUInt16(); + IccFormulaCurveType type = (IccFormulaCurveType)this.ReadUInt16(); this.AddIndex(2); // 2 bytes reserved float gamma, a, b, c, d, e; gamma = d = e = 0; @@ -175,7 +175,7 @@ public IccFormulaCurveElement ReadFormulaCurveElement() public IccSampledCurveElement ReadSampledCurveElement() { uint count = this.ReadUInt32(); - var entries = new float[count]; + float[] entries = new float[count]; for (int i = 0; i < count; i++) { entries[i] = this.ReadSingle(); @@ -191,7 +191,7 @@ public IccSampledCurveElement ReadSampledCurveElement() /// The curve data private IccTagDataEntry[] ReadCurves(int count) { - var tdata = new IccTagDataEntry[count]; + IccTagDataEntry[] tdata = new IccTagDataEntry[count]; for (int i = 0; i < count; i++) { IccTypeSignature type = this.ReadTagDataEntryHeader(); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs index e88dd8d9e1..da2f37efd3 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -4,27 +4,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to read ICC data types +/// Provides methods to read ICC data types. /// internal sealed partial class IccDataReader { /// - /// Reads an 8bit lookup table + /// Reads an 8bit lookup table. /// - /// The read LUT - public IccLut ReadLut8() - { - return new IccLut(this.ReadBytes(256)); - } + /// The read LUT. + public IccLut ReadLut8() => new(this.ReadBytes(256)); /// - /// Reads a 16bit lookup table + /// Reads a 16bit lookup table. /// - /// The number of entries - /// The read LUT + /// The number of entries. + /// The read LUT. public IccLut ReadLut16(int count) { - var values = new ushort[count]; + ushort[] values = new ushort[count]; for (int i = 0; i < count; i++) { values[i] = this.ReadUInt16(); @@ -34,17 +31,17 @@ public IccLut ReadLut16(int count) } /// - /// Reads a CLUT depending on type + /// Reads a CLUT depending on type. /// - /// Input channel count - /// Output channel count + /// Input channel count. + /// Output channel count. /// If true, it's read as CLUTf32, - /// else read as either CLUT8 or CLUT16 depending on embedded information - /// The read CLUT + /// else read as either CLUT8 or CLUT16 depending on embedded information. + /// The read CLUT. public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat) { - // Grid-points are always 16 bytes long but only 0-inChCount are used - var gridPointCount = new byte[inChannelCount]; + // Grid-points are always 16 bytes long but only 0-inChCount are used. + byte[] gridPointCount = new byte[inChannelCount]; Buffer.BlockCopy(this.data, this.AddIndex(16), gridPointCount, 0, inChannelCount); if (!isFloat) @@ -67,102 +64,106 @@ public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat) } /// - /// Reads an 8 bit CLUT + /// Reads an 8 bit CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUT8 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUT8. public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount) { - int start = this.currentIndex; - int length = 0; - for (int i = 0; i < inChannelCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChannelCount); - } - - length /= inChannelCount; + int length = this.GetClutLength(inChannelCount, outChannelCount, gridPointCount, 1); const float Max = byte.MaxValue; - var values = new float[length][]; + float[] values = new float[length * outChannelCount]; + int offset = 0; for (int i = 0; i < length; i++) { - values[i] = new float[outChannelCount]; for (int j = 0; j < outChannelCount; j++) { - values[i][j] = this.data[this.currentIndex++] / Max; + values[offset++] = this.data[this.currentIndex++] / Max; } } - this.currentIndex = start + (length * outChannelCount); - return new IccClut(values, gridPointCount, IccClutDataType.UInt8); + return new IccClut(values, gridPointCount, IccClutDataType.UInt8, outChannelCount); } /// - /// Reads a 16 bit CLUT + /// Reads a 16 bit CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUT16 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUT16. public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int start = this.currentIndex; - int length = 0; - for (int i = 0; i < inChannelCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChannelCount); - } - - length /= inChannelCount; + int length = this.GetClutLength(inChannelCount, outChannelCount, gridPointCount, 2); const float Max = ushort.MaxValue; - var values = new float[length][]; + float[] values = new float[length * outChannelCount]; + int offset = 0; for (int i = 0; i < length; i++) { - values[i] = new float[outChannelCount]; for (int j = 0; j < outChannelCount; j++) { - values[i][j] = this.ReadUInt16() / Max; + values[offset++] = this.ReadUInt16() / Max; } } this.currentIndex = start + (length * outChannelCount * 2); - return new IccClut(values, gridPointCount, IccClutDataType.UInt16); + return new IccClut(values, gridPointCount, IccClutDataType.UInt16, outChannelCount); } /// - /// Reads a 32bit floating point CLUT + /// Reads a 32bit floating point CLUT. /// - /// Input channel count - /// Output channel count - /// Grid point count for each CLUT channel - /// The read CLUTf32 + /// Input channel count. + /// Output channel count. + /// Grid point count for each CLUT channel. + /// The read CLUTf32. public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount) { int start = this.currentIndex; - int length = 0; - for (int i = 0; i < inChCount; i++) - { - length += (int)Math.Pow(gridPointCount[i], inChCount); - } - - length /= inChCount; + int length = this.GetClutLength(inChCount, outChCount, gridPointCount, 4); - var values = new float[length][]; + float[] values = new float[length * outChCount]; + int offset = 0; for (int i = 0; i < length; i++) { - values[i] = new float[outChCount]; for (int j = 0; j < outChCount; j++) { - values[i][j] = this.ReadSingle(); + values[offset++] = this.ReadSingle(); } } this.currentIndex = start + (length * outChCount * 4); - return new IccClut(values, gridPointCount, IccClutDataType.Float); + return new IccClut(values, gridPointCount, IccClutDataType.Float, outChCount); + } + + private int GetClutLength(int inputChannelCount, int outputChannelCount, byte[] gridPointCount, int bytesPerValue) + { + int length = 1; + for (int i = 0; i < inputChannelCount; i++) + { + int gridPoints = gridPointCount[i]; + if (gridPoints == 0 || length > int.MaxValue / gridPoints) + { + throw new InvalidIccProfileException("Invalid CLUT dimensions."); + } + + length *= gridPoints; + } + + long valueCount = (long)length * outputChannelCount; + long byteCount = valueCount * bytesPerValue; + if (valueCount > int.MaxValue || byteCount > this.data.Length - this.currentIndex) + { + throw new InvalidIccProfileException("The CLUT data is shorter than its declared dimensions."); + } + + return length; } } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs index 61ecda4aab..ecc9bfbffb 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs @@ -17,16 +17,23 @@ internal sealed partial class IccDataReader /// The read matrix public float[,] ReadMatrix(int xCount, int yCount, bool isSingle) { - var matrix = new float[xCount, yCount]; - for (int y = 0; y < yCount; y++) + float[,] matrix = new float[xCount, yCount]; + + if (isSingle) { - for (int x = 0; x < xCount; x++) + for (int y = 0; y < yCount; y++) { - if (isSingle) + for (int x = 0; x < xCount; x++) { matrix[x, y] = this.ReadSingle(); } - else + } + } + else + { + for (int y = 0; y < yCount; y++) + { + for (int x = 0; x < xCount; x++) { matrix[x, y] = this.ReadFix16(); } @@ -44,14 +51,17 @@ internal sealed partial class IccDataReader /// The read matrix public float[] ReadMatrix(int yCount, bool isSingle) { - var matrix = new float[yCount]; - for (int i = 0; i < yCount; i++) + float[] matrix = new float[yCount]; + if (isSingle) { - if (isSingle) + for (int i = 0; i < yCount; i++) { matrix[i] = this.ReadSingle(); } - else + } + else + { + for (int i = 0; i < yCount; i++) { matrix[i] = this.ReadFix16(); } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs index 5baa904048..98b269f0bf 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs @@ -48,7 +48,7 @@ public IccMultiProcessElement ReadMultiProcessElement() /// The read public IccCurveSetProcessElement ReadCurveSetProcessElement(int inChannelCount, int outChannelCount) { - var curves = new IccOneDimensionalCurve[inChannelCount]; + IccOneDimensionalCurve[] curves = new IccOneDimensionalCurve[inChannelCount]; for (int i = 0; i < inChannelCount; i++) { curves[i] = this.ReadOneDimensionalCurve(); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs index d5369e5fc4..219e785590 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs @@ -103,8 +103,8 @@ public IccResponseNumber ReadResponseNumber() public IccNamedColor ReadNamedColor(uint deviceCoordCount) { string name = this.ReadAsciiString(32); - ushort[] pcsCoord = { this.ReadUInt16(), this.ReadUInt16(), this.ReadUInt16() }; - var deviceCoord = new ushort[deviceCoordCount]; + ushort[] pcsCoord = [this.ReadUInt16(), this.ReadUInt16(), this.ReadUInt16()]; + ushort[] deviceCoord = new ushort[deviceCoordCount]; for (int i = 0; i < deviceCoordCount; i++) { @@ -122,8 +122,8 @@ public IccProfileDescription ReadProfileDescription() { uint manufacturer = this.ReadUInt32(); uint model = this.ReadUInt32(); - var attributes = (IccDeviceAttribute)this.ReadInt64(); - var technologyInfo = (IccProfileTag)this.ReadUInt32(); + IccDeviceAttribute attributes = (IccDeviceAttribute)this.ReadInt64(); + IccProfileTag technologyInfo = (IccProfileTag)this.ReadUInt32(); IccMultiLocalizedUnicodeTagDataEntry manufacturerInfo = ReadText(); IccMultiLocalizedUnicodeTagDataEntry modelInfo = ReadText(); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs index 47d946d443..99dc3ce3c2 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs @@ -16,55 +16,37 @@ internal sealed partial class IccDataReader /// Reads an ushort /// /// the value - public ushort ReadUInt16() - { - return BinaryPrimitives.ReadUInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2)); - } + public ushort ReadUInt16() => BinaryPrimitives.ReadUInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2)); /// /// Reads a short /// /// the value - public short ReadInt16() - { - return BinaryPrimitives.ReadInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2)); - } + public short ReadInt16() => BinaryPrimitives.ReadInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2)); /// /// Reads an uint /// /// the value - public uint ReadUInt32() - { - return BinaryPrimitives.ReadUInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4)); - } + public uint ReadUInt32() => BinaryPrimitives.ReadUInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4)); /// /// Reads an int /// /// the value - public int ReadInt32() - { - return BinaryPrimitives.ReadInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4)); - } + public int ReadInt32() => BinaryPrimitives.ReadInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4)); /// /// Reads an ulong /// /// the value - public ulong ReadUInt64() - { - return BinaryPrimitives.ReadUInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8)); - } + public ulong ReadUInt64() => BinaryPrimitives.ReadUInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8)); /// /// Reads a long /// /// the value - public long ReadInt64() - { - return BinaryPrimitives.ReadInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8)); - } + public long ReadInt64() => BinaryPrimitives.ReadInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8)); /// /// Reads a float. @@ -152,10 +134,7 @@ public string ReadUnicodeString(int length) /// Reads an unsigned 16bit number with 8 value bits and 8 fractional bits. /// /// The number as double - public float ReadUFix8() - { - return this.ReadUInt16() / 256f; - } + public float ReadUFix8() => this.ReadUInt16() / 256f; /// /// Reads a number of bytes and advances the index. @@ -164,7 +143,7 @@ public float ReadUFix8() /// The read bytes public byte[] ReadBytes(int count) { - var bytes = new byte[count]; + byte[] bytes = new byte[count]; Buffer.BlockCopy(this.data, this.AddIndex(count), bytes, 0, count); return bytes; } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index 0d50b98095..2f1b15b6b7 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -8,105 +8,67 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to read ICC data types +/// Provides methods to read ICC data types. /// internal sealed partial class IccDataReader { /// - /// Reads a tag data entry + /// Reads a tag data entry. /// - /// The table entry with reading information - /// the tag data entry + /// The table entry with reading information. + /// The tag data entry. public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info) { this.currentIndex = (int)info.Offset; - IccTypeSignature type = this.ReadTagDataEntryHeader(); - - switch (type) - { - case IccTypeSignature.Chromaticity: - return this.ReadChromaticityTagDataEntry(); - case IccTypeSignature.ColorantOrder: - return this.ReadColorantOrderTagDataEntry(); - case IccTypeSignature.ColorantTable: - return this.ReadColorantTableTagDataEntry(); - case IccTypeSignature.Curve: - return this.ReadCurveTagDataEntry(); - case IccTypeSignature.Data: - return this.ReadDataTagDataEntry(info.DataSize); - case IccTypeSignature.DateTime: - return this.ReadDateTimeTagDataEntry(); - case IccTypeSignature.Lut16: - return this.ReadLut16TagDataEntry(); - case IccTypeSignature.Lut8: - return this.ReadLut8TagDataEntry(); - case IccTypeSignature.LutAToB: - return this.ReadLutAtoBTagDataEntry(); - case IccTypeSignature.LutBToA: - return this.ReadLutBtoATagDataEntry(); - case IccTypeSignature.Measurement: - return this.ReadMeasurementTagDataEntry(); - case IccTypeSignature.MultiLocalizedUnicode: - return this.ReadMultiLocalizedUnicodeTagDataEntry(); - case IccTypeSignature.MultiProcessElements: - return this.ReadMultiProcessElementsTagDataEntry(); - case IccTypeSignature.NamedColor2: - return this.ReadNamedColor2TagDataEntry(); - case IccTypeSignature.ParametricCurve: - return this.ReadParametricCurveTagDataEntry(); - case IccTypeSignature.ProfileSequenceDesc: - return this.ReadProfileSequenceDescTagDataEntry(); - case IccTypeSignature.ProfileSequenceIdentifier: - return this.ReadProfileSequenceIdentifierTagDataEntry(); - case IccTypeSignature.ResponseCurveSet16: - return this.ReadResponseCurveSet16TagDataEntry(); - case IccTypeSignature.S15Fixed16Array: - return this.ReadFix16ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.Signature: - return this.ReadSignatureTagDataEntry(); - case IccTypeSignature.Text: - return this.ReadTextTagDataEntry(info.DataSize); - case IccTypeSignature.U16Fixed16Array: - return this.ReadUFix16ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.UInt16Array: - return this.ReadUInt16ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.UInt32Array: - return this.ReadUInt32ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.UInt64Array: - return this.ReadUInt64ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.UInt8Array: - return this.ReadUInt8ArrayTagDataEntry(info.DataSize); - case IccTypeSignature.ViewingConditions: - return this.ReadViewingConditionsTagDataEntry(); - case IccTypeSignature.Xyz: - return this.ReadXyzTagDataEntry(info.DataSize); + return this.ReadTagDataEntryHeader() switch + { + IccTypeSignature.Chromaticity => this.ReadChromaticityTagDataEntry(), + IccTypeSignature.ColorantOrder => this.ReadColorantOrderTagDataEntry(), + IccTypeSignature.ColorantTable => this.ReadColorantTableTagDataEntry(), + IccTypeSignature.Curve => this.ReadCurveTagDataEntry(), + IccTypeSignature.Data => this.ReadDataTagDataEntry(info.DataSize), + IccTypeSignature.DateTime => this.ReadDateTimeTagDataEntry(), + IccTypeSignature.Lut16 => this.ReadLut16TagDataEntry(), + IccTypeSignature.Lut8 => this.ReadLut8TagDataEntry(), + IccTypeSignature.LutAToB => this.ReadLutAtoBTagDataEntry(), + IccTypeSignature.LutBToA => this.ReadLutBtoATagDataEntry(), + IccTypeSignature.Measurement => this.ReadMeasurementTagDataEntry(), + IccTypeSignature.MultiLocalizedUnicode => this.ReadMultiLocalizedUnicodeTagDataEntry(), + IccTypeSignature.MultiProcessElements => this.ReadMultiProcessElementsTagDataEntry(), + IccTypeSignature.NamedColor2 => this.ReadNamedColor2TagDataEntry(), + IccTypeSignature.ParametricCurve => this.ReadParametricCurveTagDataEntry(), + IccTypeSignature.ProfileSequenceDesc => this.ReadProfileSequenceDescTagDataEntry(), + IccTypeSignature.ProfileSequenceIdentifier => this.ReadProfileSequenceIdentifierTagDataEntry(), + IccTypeSignature.ResponseCurveSet16 => this.ReadResponseCurveSet16TagDataEntry(), + IccTypeSignature.S15Fixed16Array => this.ReadFix16ArrayTagDataEntry(info.DataSize), + IccTypeSignature.Signature => this.ReadSignatureTagDataEntry(), + IccTypeSignature.Text => this.ReadTextTagDataEntry(info.DataSize), + IccTypeSignature.U16Fixed16Array => this.ReadUFix16ArrayTagDataEntry(info.DataSize), + IccTypeSignature.UInt16Array => this.ReadUInt16ArrayTagDataEntry(info.DataSize), + IccTypeSignature.UInt32Array => this.ReadUInt32ArrayTagDataEntry(info.DataSize), + IccTypeSignature.UInt64Array => this.ReadUInt64ArrayTagDataEntry(info.DataSize), + IccTypeSignature.UInt8Array => this.ReadUInt8ArrayTagDataEntry(info.DataSize), + IccTypeSignature.ViewingConditions => this.ReadViewingConditionsTagDataEntry(), + IccTypeSignature.Xyz => this.ReadXyzTagDataEntry(info.DataSize), // V2 Types: - case IccTypeSignature.TextDescription: - return this.ReadTextDescriptionTagDataEntry(); - case IccTypeSignature.CrdInfo: - return this.ReadCrdInfoTagDataEntry(); - case IccTypeSignature.Screening: - return this.ReadScreeningTagDataEntry(); - case IccTypeSignature.UcrBg: - return this.ReadUcrBgTagDataEntry(info.DataSize); + IccTypeSignature.TextDescription => this.ReadTextDescriptionTagDataEntry(), + IccTypeSignature.CrdInfo => this.ReadCrdInfoTagDataEntry(), + IccTypeSignature.Screening => this.ReadScreeningTagDataEntry(), + IccTypeSignature.UcrBg => this.ReadUcrBgTagDataEntry(info.DataSize), // Unsupported or unknown - case IccTypeSignature.DeviceSettings: - case IccTypeSignature.NamedColor: - case IccTypeSignature.Unknown: - default: - return this.ReadUnknownTagDataEntry(info.DataSize); - } + _ => this.ReadUnknownTagDataEntry(info.DataSize), + }; } /// /// Reads the header of a /// - /// The read signature + /// The read signature. public IccTypeSignature ReadTagDataEntryHeader() { - var type = (IccTypeSignature)this.ReadUInt32(); + IccTypeSignature type = (IccTypeSignature)this.ReadUInt32(); this.AddIndex(4); // 4 bytes are not used return type; } @@ -114,7 +76,7 @@ public IccTypeSignature ReadTagDataEntryHeader() /// /// Reads the header of a and checks if it's the expected value /// - /// expected value to check against + /// The expected value to check against. public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) { IccTypeSignature type = this.ReadTagDataEntryHeader(); @@ -127,8 +89,8 @@ public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) /// /// Reads a with an unknown /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) { int count = (int)size - 8; // 8 is the tag header size @@ -138,13 +100,13 @@ public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) /// /// Reads a /// - /// The read entry + /// The read entry. public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() { ushort channelCount = this.ReadUInt16(); - var colorant = (IccColorantEncoding)this.ReadUInt16(); + IccColorantEncoding colorant = (IccColorantEncoding)this.ReadUInt16(); - if (Enum.IsDefined(typeof(IccColorantEncoding), colorant) && colorant != IccColorantEncoding.Unknown) + if (Enum.IsDefined(colorant) && colorant != IccColorantEncoding.Unknown) { // The type is known and so are the values (they are constant) // channelCount should always be 3 but it doesn't really matter if it's not @@ -152,11 +114,11 @@ public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() } else { - // The type is not know, so the values need be read + // The type is not know, so the values need be read. double[][] values = new double[channelCount][]; for (int i = 0; i < channelCount; i++) { - values[i] = new double[] { this.ReadUFix16(), this.ReadUFix16() }; + values[i] = [this.ReadUFix16(), this.ReadUFix16()]; } return new IccChromaticityTagDataEntry(values); @@ -166,7 +128,7 @@ public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() { uint colorantCount = this.ReadUInt32(); @@ -177,11 +139,11 @@ public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() { uint colorantCount = this.ReadUInt32(); - var cdata = new IccColorantTableEntry[colorantCount]; + IccColorantTableEntry[] cdata = new IccColorantTableEntry[colorantCount]; for (int i = 0; i < colorantCount; i++) { cdata[i] = this.ReadColorantTableEntry(); @@ -193,7 +155,7 @@ public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccCurveTagDataEntry ReadCurveTagDataEntry() { uint pointCount = this.ReadUInt32(); @@ -222,7 +184,7 @@ public IccCurveTagDataEntry ReadCurveTagDataEntry() /// /// Reads a /// - /// The size of the entry in bytes + /// The size of the entry in bytes. /// The read entry public IccDataTagDataEntry ReadDataTagDataEntry(uint size) { @@ -240,16 +202,13 @@ public IccDataTagDataEntry ReadDataTagDataEntry(uint size) /// /// Reads a /// - /// The read entry - public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() - { - return new IccDateTimeTagDataEntry(this.ReadDateTime()); - } + /// The read entry. + public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() => new(this.ReadDateTime()); /// /// Reads a /// - /// The read entry + /// The read entry. public IccLut16TagDataEntry ReadLut16TagDataEntry() { byte inChCount = this.data[this.AddIndex(1)]; @@ -263,7 +222,7 @@ public IccLut16TagDataEntry ReadLut16TagDataEntry() ushort outTableCount = this.ReadUInt16(); // Input LUT - var inValues = new IccLut[inChCount]; + IccLut[] inValues = new IccLut[inChCount]; byte[] gridPointCount = new byte[inChCount]; for (int i = 0; i < inChCount; i++) { @@ -275,7 +234,7 @@ public IccLut16TagDataEntry ReadLut16TagDataEntry() IccClut clut = this.ReadClut16(inChCount, outChCount, gridPointCount); // Output LUT - var outValues = new IccLut[outChCount]; + IccLut[] outValues = new IccLut[outChCount]; for (int i = 0; i < outChCount; i++) { outValues[i] = this.ReadLut16(outTableCount); @@ -287,7 +246,7 @@ public IccLut16TagDataEntry ReadLut16TagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccLut8TagDataEntry ReadLut8TagDataEntry() { byte inChCount = this.data[this.AddIndex(1)]; @@ -298,7 +257,7 @@ public IccLut8TagDataEntry ReadLut8TagDataEntry() float[,] matrix = this.ReadMatrix(3, 3, false); // Input LUT - var inValues = new IccLut[inChCount]; + IccLut[] inValues = new IccLut[inChCount]; byte[] gridPointCount = new byte[inChCount]; for (int i = 0; i < inChCount; i++) { @@ -310,7 +269,7 @@ public IccLut8TagDataEntry ReadLut8TagDataEntry() IccClut clut = this.ReadClut8(inChCount, outChCount, gridPointCount); // Output LUT - var outValues = new IccLut[outChCount]; + IccLut[] outValues = new IccLut[outChCount]; for (int i = 0; i < outChCount; i++) { outValues[i] = this.ReadLut8(); @@ -322,7 +281,7 @@ public IccLut8TagDataEntry ReadLut8TagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -381,7 +340,7 @@ public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -440,30 +399,27 @@ public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() /// /// Reads a /// - /// The read entry - public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() - { - return new IccMeasurementTagDataEntry( + /// The read entry. + public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() => new( observer: (IccStandardObserver)this.ReadUInt32(), xyzBacking: this.ReadXyzNumber(), geometry: (IccMeasurementGeometry)this.ReadUInt32(), flare: this.ReadUFix16(), illuminant: (IccStandardIlluminant)this.ReadUInt32()); - } /// /// Reads a /// - /// The read entry + /// The read entry. public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size uint recordCount = this.ReadUInt32(); this.ReadUInt32(); // Record size (always 12) - var text = new IccLocalizedString[recordCount]; + IccLocalizedString[] text = new IccLocalizedString[recordCount]; - var culture = new CultureInfo[recordCount]; + CultureInfo[] culture = new CultureInfo[recordCount]; uint[] length = new uint[recordCount]; uint[] offset = new uint[recordCount]; @@ -485,7 +441,7 @@ public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntr return new IccMultiLocalizedUnicodeTagDataEntry(text); - CultureInfo ReadCulture(string language, string country) + static CultureInfo ReadCulture(string language, string country) { if (string.IsNullOrWhiteSpace(language)) { @@ -519,7 +475,7 @@ CultureInfo ReadCulture(string language, string country) /// /// Reads a /// - /// The read entry + /// The read entry. public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry() { int start = this.currentIndex - 8; @@ -528,13 +484,13 @@ public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry( this.ReadUInt16(); uint elementCount = this.ReadUInt32(); - var positionTable = new IccPositionNumber[elementCount]; + IccPositionNumber[] positionTable = new IccPositionNumber[elementCount]; for (int i = 0; i < elementCount; i++) { positionTable[i] = this.ReadPositionNumber(); } - var elements = new IccMultiProcessElement[elementCount]; + IccMultiProcessElement[] elements = new IccMultiProcessElement[elementCount]; for (int i = 0; i < elementCount; i++) { this.currentIndex = (int)positionTable[i].Offset + start; @@ -547,7 +503,7 @@ public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry( /// /// Reads a /// - /// The read entry + /// The read entry. public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() { int vendorFlag = this.ReadInt32(); @@ -556,7 +512,7 @@ public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() string prefix = this.ReadAsciiString(32); string suffix = this.ReadAsciiString(32); - var colors = new IccNamedColor[colorCount]; + IccNamedColor[] colors = new IccNamedColor[colorCount]; for (int i = 0; i < colorCount; i++) { colors[i] = this.ReadNamedColor(coordCount); @@ -569,19 +525,16 @@ public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() /// Reads a /// /// The read entry - public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() - { - return new IccParametricCurveTagDataEntry(this.ReadParametricCurve()); - } + public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() => new(this.ReadParametricCurve()); /// /// Reads a /// - /// The read entry + /// The read entry. public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() { uint count = this.ReadUInt32(); - var description = new IccProfileDescription[count]; + IccProfileDescription[] description = new IccProfileDescription[count]; for (int i = 0; i < count; i++) { description[i] = this.ReadProfileDescription(); @@ -593,18 +546,18 @@ public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size uint count = this.ReadUInt32(); - var table = new IccPositionNumber[count]; + IccPositionNumber[] table = new IccPositionNumber[count]; for (int i = 0; i < count; i++) { table[i] = this.ReadPositionNumber(); } - var entries = new IccProfileSequenceIdentifier[count]; + IccProfileSequenceIdentifier[] entries = new IccProfileSequenceIdentifier[count]; for (int i = 0; i < count; i++) { this.currentIndex = (int)(start + table[i].Offset); @@ -620,7 +573,7 @@ public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTag /// /// Reads a /// - /// The read entry + /// The read entry. public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() { int start = this.currentIndex - 8; // 8 is the tag header size @@ -633,7 +586,7 @@ public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() offset[i] = this.ReadUInt32(); } - var curves = new IccResponseCurve[measurementCount]; + IccResponseCurve[] curves = new IccResponseCurve[measurementCount]; for (int i = 0; i < measurementCount; i++) { this.currentIndex = (int)(start + offset[i]); @@ -646,8 +599,8 @@ public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -663,27 +616,21 @@ public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The read entry - public IccSignatureTagDataEntry ReadSignatureTagDataEntry() - { - return new IccSignatureTagDataEntry(this.ReadAsciiString(4)); - } + /// The read entry. + public IccSignatureTagDataEntry ReadSignatureTagDataEntry() => new(this.ReadAsciiString(4)); /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry - public IccTextTagDataEntry ReadTextTagDataEntry(uint size) - { - return new IccTextTagDataEntry(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size - } + /// The size of the entry in bytes. + /// The read entry. + public IccTextTagDataEntry ReadTextTagDataEntry(uint size) => new(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -699,8 +646,8 @@ public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) { uint count = (size - 8) / 2; @@ -716,8 +663,8 @@ public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) { uint count = (size - 8) / 4; @@ -733,8 +680,8 @@ public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) { uint count = (size - 8) / 8; @@ -750,8 +697,8 @@ public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) { int count = (int)size - 8; // 8 is the tag header size @@ -763,24 +710,21 @@ public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) /// /// Reads a /// - /// The read entry - public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() - { - return new IccViewingConditionsTagDataEntry( + /// The read entry. + public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() => new( illuminantXyz: this.ReadXyzNumber(), surroundXyz: this.ReadXyzNumber(), illuminant: (IccStandardIlluminant)this.ReadUInt32()); - } /// /// Reads a /// - /// The size of the entry in bytes - /// The read entry + /// The size of the entry in bytes. + /// The read entry. public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) { uint count = (size - 8) / 12; - var arrayData = new Vector3[count]; + Vector3[] arrayData = new Vector3[count]; for (int i = 0; i < count; i++) { arrayData[i] = this.ReadXyzNumber(); @@ -792,7 +736,7 @@ public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) /// /// Reads a /// - /// The read entry + /// The read entry. public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() { string unicodeValue, scriptcodeValue; @@ -832,7 +776,7 @@ public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry() { uint productNameCount = this.ReadUInt32(); @@ -856,12 +800,12 @@ public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry() /// /// Reads a /// - /// The read entry + /// The read entry. public IccScreeningTagDataEntry ReadScreeningTagDataEntry() { - var flags = (IccScreeningFlag)this.ReadInt32(); + IccScreeningFlag flags = (IccScreeningFlag)this.ReadInt32(); uint channelCount = this.ReadUInt32(); - var channels = new IccScreeningChannel[channelCount]; + IccScreeningChannel[] channels = new IccScreeningChannel[channelCount]; for (int i = 0; i < channels.Length; i++) { channels[i] = this.ReadScreeningChannel(); @@ -873,7 +817,7 @@ public IccScreeningTagDataEntry ReadScreeningTagDataEntry() /// /// Reads a /// - /// The size of the entry in bytes + /// The size of the entry in bytes. /// The read entry public IccUcrBgTagDataEntry ReadUcrBgTagDataEntry(uint size) { diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs index 703a3896bb..29394c0820 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs @@ -4,15 +4,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Provides methods to write ICC data types +/// Provides methods to write ICC data types. /// internal sealed partial class IccDataWriter { /// - /// Writes an 8bit lookup table + /// Writes an 8bit lookup table. /// - /// The LUT to write - /// The number of bytes written + /// The LUT to write. + /// The number of bytes written. public int WriteLut8(IccLut value) { foreach (float item in value.Values) @@ -24,10 +24,10 @@ public int WriteLut8(IccLut value) } /// - /// Writes an 16bit lookup table + /// Writes an 16bit lookup table. /// - /// The LUT to write - /// The number of bytes written + /// The LUT to write. + /// The number of bytes written. public int WriteLut16(IccLut value) { foreach (float item in value.Values) @@ -39,10 +39,10 @@ public int WriteLut16(IccLut value) } /// - /// Writes an color lookup table + /// Writes an color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut(IccClut value) { int count = this.WriteArray(value.GridPointCount); @@ -67,57 +67,48 @@ public int WriteClut(IccClut value) } /// - /// Writes a 8bit color lookup table + /// Writes a 8bit color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut8(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue)); - } + count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue)); } return count; } /// - /// Writes a 16bit color lookup table + /// Writes a 16bit color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClut16(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue)); - } + count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue)); } return count; } /// - /// Writes a 32bit float color lookup table + /// Writes a 32bit float color lookup table. /// - /// The CLUT to write - /// The number of bytes written + /// The CLUT to write. + /// The number of bytes written. public int WriteClutF32(IccClut value) { int count = 0; - foreach (float[] inArray in value.Values) + foreach (float item in value.Values) { - foreach (float item in inArray) - { - count += this.WriteSingle(item); - } + count += this.WriteSingle(item); } return count; diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index 1e5f359e09..636cc90a57 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; @@ -61,15 +61,21 @@ public int WriteMatrix(Matrix4x4 value, bool isSingle) public int WriteMatrix(in DenseMatrix value, bool isSingle) { int count = 0; - for (int y = 0; y < value.Rows; y++) + if (isSingle) { - for (int x = 0; x < value.Columns; x++) + for (int y = 0; y < value.Rows; y++) { - if (isSingle) + for (int x = 0; x < value.Columns; x++) { count += this.WriteSingle(value[x, y]); } - else + } + } + else + { + for (int y = 0; y < value.Rows; y++) + { + for (int x = 0; x < value.Columns; x++) { count += this.WriteFix16(value[x, y]); } @@ -88,15 +94,22 @@ public int WriteMatrix(in DenseMatrix value, bool isSingle) public int WriteMatrix(float[,] value, bool isSingle) { int count = 0; - for (int y = 0; y < value.GetLength(1); y++) + + if (isSingle) { - for (int x = 0; x < value.GetLength(0); x++) + for (int y = 0; y < value.GetLength(1); y++) { - if (isSingle) + for (int x = 0; x < value.GetLength(0); x++) { count += this.WriteSingle(value[x, y]); } - else + } + } + else + { + for (int y = 0; y < value.GetLength(1); y++) + { + for (int x = 0; x < value.GetLength(0); x++) { count += this.WriteFix16(value[x, y]); } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 1f9c101fb5..6019a0bff7 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -231,7 +231,7 @@ public int WriteLut8TagDataEntry(IccLut8TagDataEntry value) { int count = this.WriteByte((byte)value.InputChannelCount); count += this.WriteByte((byte)value.OutputChannelCount); - count += this.WriteByte((byte)value.ClutValues.Values[0].Length); + count += this.WriteByte((byte)value.ClutValues.OutputChannelCount); count += this.WriteEmpty(1); count += this.WriteMatrix(value.Matrix, false); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs index e0c6f4c962..27af2a91f7 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs @@ -14,7 +14,7 @@ internal enum IccFormulaCurveType : ushort Type1 = 0, /// - /// Type 1: Y = a * log10 (b * X^γ + c) + d + /// Type 2: Y = a * log10 (b * X^γ + c) + d /// Type2 = 1, diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs index c1cb3f10f0..c6b4b65773 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.SRGB.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.SRGB.cs new file mode 100644 index 0000000000..bfa4ab9bdb --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.SRGB.cs @@ -0,0 +1,346 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; + +/// +/// Provides logic for identifying canonical IEC 61966-2-1 (sRGB) matrix-TRC ICC profiles, +/// distinguishing them from appearance or device-specific variants. +/// +public sealed partial class IccProfile +{ + // sRGB v2 Preference + private static readonly IccProfileId StandardRgbV2 = new(0x3D0EB2DE, 0xAE9397BE, 0x9B6726CE, 0x8C0A43CE); + + // sRGB v4 Preference + private static readonly IccProfileId StandardRgbV4 = new(0x34562ABF, 0x994CCD06, 0x6D2C5721, 0xD0D68C5D); + + /// + /// Detects canonical sRGB matrix+TRC profiles quickly and safely. + /// Rules: + /// 1) Accept known IEC sRGB v2 and v4 by profile ID. + /// 2) Require RGB, PCS=XYZ, ICC v2 or v4, and no A2B*/B2A* LUTs. + /// 3) Require rTRC, gTRC, bTRC to exist and be identical by parameters or sampled shape. + /// 4) Accept if rXYZ/gXYZ/bXYZ already match the D50-adapted sRGB colorants within tolerance. + /// 5) If white point ≈ D65, adapt only the colorant columns to D50 using Bradford + /// via and then compare. + /// This rejects channel-swapped and appearance profiles while allowing real sRGB. + /// + /// + /// Reference D50-adapted sRGB colorants from Bruce Lindbloom: + /// + /// R=(0.4360747, 0.2225045, 0.0139322) + /// G=(0.3850649, 0.7168786, 0.0971045) + /// B=(0.1430804, 0.0606169, 0.7141733) + /// + internal bool IsCanonicalSrgbMatrixTrc() + { + IccProfileHeader h = this.Header; + + // Fast path for known IEC sRGB profile IDs + if (h.Id == StandardRgbV2 || h.Id == StandardRgbV4) + { + return true; + } + + // Header gating to avoid parsing work for obvious non-matches + if (h.FileSignature != "acsp") + { + return false; + } + + if (h.DataColorSpace != IccColorSpaceType.Rgb) + { + return false; + } + + if (h.ProfileConnectionSpace != IccColorSpaceType.CieXyz) + { + return false; + } + + if (h.Version.Major is not 2 and not 4) + { + return false; + } + + this.InitializeEntries(); + IccTagDataEntry[] entries = this.entries; + + // Reject device/display LUT profiles. We only accept matrix+TRC encodings. + if (Has(entries, IccProfileTag.AToB0) || Has(entries, IccProfileTag.AToB1) || Has(entries, IccProfileTag.AToB2) || + Has(entries, IccProfileTag.BToA0) || Has(entries, IccProfileTag.BToA1) || Has(entries, IccProfileTag.BToA2)) + { + return false; + } + + // Required matrix+TRC tags + if (!TryGetXyz(entries, IccProfileTag.MediaWhitePoint, out Vector3 wtpt)) + { + return false; + } + + if (!TryGetXyz(entries, IccProfileTag.RedMatrixColumn, out Vector3 rXYZ)) + { + return false; + } + + if (!TryGetXyz(entries, IccProfileTag.GreenMatrixColumn, out Vector3 gXYZ)) + { + return false; + } + + if (!TryGetXyz(entries, IccProfileTag.BlueMatrixColumn, out Vector3 bXYZ)) + { + return false; + } + + // TRCs must exist and be identical across channels. This filters many trick profiles. + if (!TryGetTrc(entries, IccProfileTag.RedTrc, out Trc tR)) + { + return false; + } + + if (!TryGetTrc(entries, IccProfileTag.GreenTrc, out Trc tG)) + { + return false; + } + + if (!TryGetTrc(entries, IccProfileTag.BlueTrc, out Trc tB)) + { + return false; + } + + if (!tR.Equals(tG) || !tR.Equals(tB)) + { + return false; + } + + // D50-adapted sRGB colorants (compare as columns: r,g,b), tight epsilon + const float eps = 2e-3F; + Vector3 rRef = new(0.4360747F, 0.2225045F, 0.0139322F); + Vector3 gRef = new(0.3850649F, 0.7168786F, 0.0971045F); + Vector3 bRef = new(0.1430804F, 0.0606169F, 0.7141733F); + + // First, accept if the stored colorants are already the D50 sRGB primaries. + // Many v2 sRGB profiles store D50-adapted colorants while declaring wtpt≈D65. + if (Near(rXYZ, rRef, eps) && Near(gXYZ, gRef, eps) && Near(bXYZ, bRef, eps)) + { + return true; + } + + // If the profile declares a D65 white, adapt the colorant columns to D50 and compare again. + // We never adapt when they already match, to avoid compounding rounding. + if (Near(wtpt, KnownIlluminants.D65.AsVector3Unsafe(), 2e-3F)) + { + CieXyz fromWp = new(wtpt); // Declared white + CieXyz toWp = KnownIlluminants.D50; // PCS white + Matrix4x4 matrix = KnownChromaticAdaptationMatrices.Bradford; + + rXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(rXYZ), (fromWp, toWp), matrix).AsVector3Unsafe(); + gXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(gXYZ), (fromWp, toWp), matrix).AsVector3Unsafe(); + bXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(bXYZ), (fromWp, toWp), matrix).AsVector3Unsafe(); + } + + // Require identity mapping of primaries, no permutation + if (!Near(rXYZ, rRef, eps) || !Near(gXYZ, gRef, eps) || !Near(bXYZ, bRef, eps)) + { + return false; + } + + return true; + + static bool Has(ReadOnlySpan span, IccProfileTag tag) + { + for (int i = 0; i < span.Length; i++) + { + if (span[i].TagSignature == tag) + { + return true; + } + } + + return false; + } + + static bool TryGetXyz(ReadOnlySpan span, IccProfileTag tag, out Vector3 xyz) + { + for (int i = 0; i < span.Length; i++) + { + IccTagDataEntry e = span[i]; + if (e.TagSignature != tag) + { + continue; + } + + if (e is IccXyzTagDataEntry x && x.Data is { Length: >= 1 }) + { + xyz = x.Data[0]; + return true; + } + + break; + } + + xyz = default; + return false; + } + + static bool TryGetTrc(ReadOnlySpan span, IccProfileTag tag, out Trc trc) + { + for (int i = 0; i < span.Length; i++) + { + IccTagDataEntry e = span[i]; + if (e.TagSignature != tag) + { + continue; + } + + if (e is IccParametricCurveTagDataEntry p) + { + trc = Trc.FromParametric(p.Curve); + return true; + } + + if (e is IccCurveTagDataEntry c) + { + trc = Trc.FromCurveLut(c.CurveData); + return true; + } + + break; + } + + trc = default; + return false; + } + + static bool Near(in Vector3 a, in Vector3 b, float tol) + => MathF.Abs(a.X - b.X) <= tol && + MathF.Abs(a.Y - b.Y) <= tol && + MathF.Abs(a.Z - b.Z) <= tol; + } + + /// + /// Compact, allocation-free descriptor of a TRC for equality and optional sRGB check. + /// + private readonly struct Trc : IEquatable + { + private readonly byte kind; // 0 = none, 1 = parametric, 2 = sampled + private readonly float g; // parametric payload or downsampled hash + private readonly float a; + private readonly float b; + private readonly float c; + private readonly float d; + private readonly float e; + private readonly float f; + private readonly int n; // for sampled, length or a small signature + + private Trc(byte kind, float g, float a, float b, float c, float d, float e, float f, int n) + { + this.kind = kind; + this.g = g; + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.n = n; + } + + public static Trc FromParametric(IccParametricCurve c) + + // Normalize by curve type to a stable tuple + // The types map to piecewise forms, but equality across channels is the key requirement here + => new(1, c.G, c.A, c.B, c.C, c.D, c.E, c.F, (int)c.Type); + + public static Trc FromCurveLut(float[] data) + { + // Exact sequence equality is enforced by the calling code using the same Trc construction + // Record a short signature to compare cheaply, avoid copying + if (data == null) + { + return default; + } + + int n = data.Length; + if (n == 0) + { + return default; + } + + // Downsample a few points to a robust fingerprint + // Use fixed indices to avoid allocations + float s0 = data[0]; + float s1 = data[n >> 2]; + float s2 = data[n >> 1]; + float s3 = data[(n * 3) >> 2]; + float s4 = data[n - 1]; + + return new Trc( + 2, + s0, + s1, + s2, + s3, + s4, + 0F, + 0F, + n); + } + + public override bool Equals(object? obj) => obj is Trc trc && this.Equals(trc); + + public bool Equals(Trc other) + { + if (this.kind != other.kind) + { + return false; + } + + if (this.kind == 0) + { + return false; + } + + if (this.kind == 1) + { + // parametric: exact parameter match and type match + return this.n == other.n && + this.g == other.g && this.a == other.a && + this.b == other.b && this.c == other.c && + this.d == other.d && this.e == other.e && this.f == other.f; + } + + // sampled: same length and same 5-point fingerprint + return this.n == other.n && + this.g == other.g && this.a == other.a && + this.b == other.b && this.c == other.c && this.d == other.d; + } + + // Optional stricter sRGB check if you need it later + public bool IsSrgbLike() + { + if (this.kind == 1) + { + // Accept common sRGB parametric encodings where type and parameters match + // IEC 61966-2-1 maps to Type4 or Type5 forms in practice + // Tighten only if you must exclude gamma~2.2 profiles that share primaries + return true; + } + + return true; + } + + public override int GetHashCode() + { + int a = HashCode.Combine(this.kind, this.g, this.a, this.b, this.c, this.d, this.e); + int b = HashCode.Combine(this.f, this.n); + return HashCode.Combine(a, b); + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs index be7350bc44..eaba0a045c 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// /// Represents an ICC profile /// -public sealed class IccProfile : IDeepCloneable +public sealed partial class IccProfile : IDeepCloneable { /// /// The byte array to read the ICC profile from @@ -110,8 +110,8 @@ public static IccProfileId CalculateHash(byte[] data) // need to copy some values because they need to be zero for the hashing Span temp = stackalloc byte[24]; data.AsSpan(profileFlagPos, 4).CopyTo(temp); - data.AsSpan(renderingIntentPos, 4).CopyTo(temp.Slice(4)); - data.AsSpan(profileIdPos, 16).CopyTo(temp.Slice(8)); + data.AsSpan(renderingIntentPos, 4).CopyTo(temp[4..]); + data.AsSpan(profileIdPos, 16).CopyTo(temp[8..]); try { @@ -131,7 +131,7 @@ public static IccProfileId CalculateHash(byte[] data) } finally { - temp.Slice(0, 4).CopyTo(data.AsSpan(profileFlagPos)); + temp[..4].CopyTo(data.AsSpan(profileFlagPos)); temp.Slice(4, 4).CopyTo(data.AsSpan(renderingIntentPos)); temp.Slice(8, 16).CopyTo(data.AsSpan(profileIdPos)); } @@ -155,9 +155,9 @@ public bool CheckIsValid() } return arrayValid && - Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) && - Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) && - Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) && + Enum.IsDefined(this.Header.DataColorSpace) && + Enum.IsDefined(this.Header.ProfileConnectionSpace) && + Enum.IsDefined(this.Header.RenderingIntent) && this.Header.Size is >= minSize and < maxSize; } @@ -190,7 +190,6 @@ private void InitializeHeader() return; } - IccReader reader = new(); this.header = IccReader.ReadHeader(this.data); } @@ -203,11 +202,10 @@ private void InitializeEntries() if (this.data is null) { - this.entries = Array.Empty(); + this.entries = []; return; } - IccReader reader = new(); this.entries = IccReader.ReadTagData(this.data); } } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs index 4da5a6aa56..959668aaf9 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. #nullable disable diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs index 45074c9a6e..d4ee3bded1 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs @@ -83,28 +83,19 @@ private static IccTagDataEntry[] ReadTagData(IccDataReader reader) { IccTagTableEntry[] tagTable = ReadTagTable(reader); List entries = new(tagTable.Length); - Dictionary store = new(); foreach (IccTagTableEntry tag in tagTable) { IccTagDataEntry entry; - if (store.TryGetValue(tag.Offset, out IccTagDataEntry? value)) + + try { - entry = value; + entry = reader.ReadTagDataEntry(tag); } - else + catch { - try - { - entry = reader.ReadTagDataEntry(tag); - } - catch - { - // Ignore tags that could not be read - continue; - } - - store.Add(tag.Offset, entry); + // Ignore tags that could not be read + continue; } entry.TagSignature = tag.Signature; @@ -124,10 +115,11 @@ private static IccTagTableEntry[] ReadTagTable(IccDataReader reader) // A normal profile usually has 5-15 entries if (tagCount > 100) { - return Array.Empty(); + return []; } List table = new((int)tagCount); + uint dataLength = (uint)reader.DataLength; for (int i = 0; i < tagCount; i++) { uint tagSignature = reader.ReadUInt32(); @@ -135,7 +127,7 @@ private static IccTagTableEntry[] ReadTagTable(IccDataReader reader) uint tagSize = reader.ReadUInt32(); // Exclude entries that have nonsense values and could cause exceptions further on - if (tagOffset < reader.DataLength && tagSize < reader.DataLength - 128) + if (tagSize >= 8 && tagOffset <= dataLength && tagSize <= dataLength - tagOffset) { table.Add(new IccTagTableEntry((IccProfileTag)tagSignature, tagOffset, tagSize)); } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs index 12228f3f58..f7a99645bb 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -41,9 +41,7 @@ protected IccTagDataEntry(IccTypeSignature signature, IccProfileTag tagSignature /// public override bool Equals(object? obj) - { - return obj is IccTagDataEntry entry && this.Equals(entry); - } + => obj is IccTagDataEntry entry && this.Equals(entry); /// public virtual bool Equals(IccTagDataEntry? other) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs index 5e73e2dd00..6bc0560f02 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs @@ -71,7 +71,7 @@ private static IccTagTableEntry[] WriteTagData(IccDataWriter writer, IccTagDataE // (Header size) + (entry count) + (nr of entries) * (size of table entry) writer.SetIndex(128 + 4 + (entries.Length * 12)); - List table = new(); + List table = []; foreach (IGrouping group in grouped) { writer.WriteTagDataEntry(group.Key, out IccTagTableEntry tableEntry); diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs index cc44762919..0f61857ba7 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs @@ -112,35 +112,35 @@ private static double[][] GetColorantArray(IccColorantEncoding colorantType) switch (colorantType) { case IccColorantEncoding.EbuTech3213E: - return new[] - { - new[] { 0.640, 0.330 }, - new[] { 0.290, 0.600 }, - new[] { 0.150, 0.060 }, - }; + return + [ + [0.640, 0.330], + [0.290, 0.600], + [0.150, 0.060] + ]; case IccColorantEncoding.ItuRBt709_2: - return new[] - { - new[] { 0.640, 0.330 }, - new[] { 0.300, 0.600 }, - new[] { 0.150, 0.060 }, - }; + return + [ + [0.640, 0.330], + [0.300, 0.600], + [0.150, 0.060] + ]; case IccColorantEncoding.P22: - return new[] - { - new[] { 0.625, 0.340 }, - new[] { 0.280, 0.605 }, - new[] { 0.155, 0.070 }, - }; + return + [ + [0.625, 0.340], + [0.280, 0.605], + [0.155, 0.070] + ]; case IccColorantEncoding.SmpteRp145: - return new[] - { - new[] { 0.630, 0.340 }, - new[] { 0.310, 0.595 }, - new[] { 0.155, 0.070 }, - }; + return + [ + [0.630, 0.340], + [0.310, 0.595], + [0.155, 0.070] + ]; default: - throw new ArgumentException("Unrecognized colorant encoding"); + throw new InvalidIccProfileException("Unrecognized colorant encoding"); } } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs index 5605d92559..7330cbdf00 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs @@ -12,7 +12,7 @@ internal sealed class IccCurveTagDataEntry : IccTagDataEntry, IEquatable class. /// public IccCurveTagDataEntry() - : this(Array.Empty(), IccProfileTag.Unknown) + : this([], IccProfileTag.Unknown) { } @@ -21,7 +21,7 @@ public IccCurveTagDataEntry() ///
/// Gamma value public IccCurveTagDataEntry(float gamma) - : this(new[] { gamma }, IccProfileTag.Unknown) + : this([gamma], IccProfileTag.Unknown) { } @@ -39,7 +39,7 @@ public IccCurveTagDataEntry(float[] curveData) ///
/// Tag Signature public IccCurveTagDataEntry(IccProfileTag tagSignature) - : this(Array.Empty(), tagSignature) + : this([], tagSignature) { } @@ -49,7 +49,7 @@ public IccCurveTagDataEntry(IccProfileTag tagSignature) /// Gamma value /// Tag Signature public IccCurveTagDataEntry(float gamma, IccProfileTag tagSignature) - : this(new[] { gamma }, tagSignature) + : this([gamma], tagSignature) { } @@ -61,7 +61,7 @@ public IccCurveTagDataEntry(float gamma, IccProfileTag tagSignature) public IccCurveTagDataEntry(float[] curveData, IccProfileTag tagSignature) : base(IccTypeSignature.Curve, tagSignature) { - this.CurveData = curveData ?? Array.Empty(); + this.CurveData = curveData ?? []; } /// diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index 9bf3232633..77bc45bd4f 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -64,44 +64,7 @@ public IccLutAToBTagDataEntry( this.CurveM = curveM; this.ClutValues = clutValues; - if (this.IsAClutMMatrixB()) - { - Guard.IsTrue(this.CurveB.Length == 3, nameof(this.CurveB), $"{nameof(this.CurveB)} must have a length of three"); - Guard.IsTrue(this.CurveM.Length == 3, nameof(this.CurveM), $"{nameof(this.CurveM)} must have a length of three"); - Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); - - this.InputChannelCount = curveA.Length; - this.OutputChannelCount = 3; - - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); - } - else if (this.IsMMatrixB()) - { - Guard.IsTrue(this.CurveB.Length == 3, nameof(this.CurveB), $"{nameof(this.CurveB)} must have a length of three"); - Guard.IsTrue(this.CurveM.Length == 3, nameof(this.CurveM), $"{nameof(this.CurveM)} must have a length of three"); - - this.InputChannelCount = this.OutputChannelCount = 3; - } - else if (this.IsAClutB()) - { - Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); - Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); - - this.InputChannelCount = curveA.Length; - this.OutputChannelCount = curveB.Length; - - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); - } - else if (this.IsB()) - { - this.InputChannelCount = this.OutputChannelCount = this.CurveB.Length; - } - else - { - throw new ArgumentException("Invalid combination of values given"); - } + (this.InputChannelCount, this.OutputChannelCount) = this.GetChannelCounts(); } /// @@ -165,7 +128,7 @@ public bool Equals(IccLutAToBTagDataEntry other) && this.OutputChannelCount == other.OutputChannelCount && this.Matrix3x3.Equals(other.Matrix3x3) && this.Matrix3x1.Equals(other.Matrix3x1) - && this.ClutValues.Equals(other.ClutValues) + && Equals(this.ClutValues, other.ClutValues) && EqualsCurve(this.CurveB, other.CurveB) && EqualsCurve(this.CurveM, other.CurveM) && EqualsCurve(this.CurveA, other.CurveA); @@ -192,6 +155,9 @@ public override int GetHashCode() return hashCode.ToHashCode(); } + /// + /// Compares two curve arrays, treating consistently. + /// private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) { bool thisNull = thisCurves is null; @@ -202,7 +168,7 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return true; } - if (entryNull) + if (thisNull || entryNull) { return false; } @@ -210,27 +176,63 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return thisCurves.SequenceEqual(entryCurves); } - private bool IsAClutMMatrixB() - => this.CurveB != null - && this.Matrix3x3 != null - && this.Matrix3x1 != null - && this.CurveM != null - && this.ClutValues != null - && this.CurveA != null; + /// + /// Validates the configured processing stages and derives the external channel counts. + /// + /// + /// Stages are evaluated in ICC mAB order: A, CLUT, M, Matrix, B. + /// Sparse pipelines are valid as long as adjacent stages agree on channel counts. + /// + private (int InputChannelCount, int OutputChannelCount) GetChannelCounts() + { + // There are at most five possible mAB stages: A, CLUT, M, Matrix, and B. + List<(int Input, int Output, string Name)> stages = new(5); - private bool IsMMatrixB() - => this.CurveB != null - && this.Matrix3x3 != null - && this.Matrix3x1 != null - && this.CurveM != null; + if (this.CurveA != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); + stages.Add((this.CurveA.Length, this.CurveA.Length, nameof(this.CurveA))); + } - private bool IsAClutB() - => this.CurveB != null - && this.ClutValues != null - && this.CurveA != null; + if (this.ClutValues != null) + { + stages.Add((this.ClutValues.InputChannelCount, this.ClutValues.OutputChannelCount, nameof(this.ClutValues))); + } - private bool IsB() => this.CurveB != null; + if (this.CurveM != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveM.Length, 1, 15, nameof(this.CurveM)); + stages.Add((this.CurveM.Length, this.CurveM.Length, nameof(this.CurveM))); + } + + if (this.Matrix3x3 != null || this.Matrix3x1 != null) + { + Guard.IsTrue(this.Matrix3x3 != null && this.Matrix3x1 != null, nameof(this.Matrix3x3), "Matrix must include both the 3x3 and 3x1 components"); + stages.Add((3, 3, nameof(this.Matrix3x3))); + } + if (this.CurveB != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); + stages.Add((this.CurveB.Length, this.CurveB.Length, nameof(this.CurveB))); + } + + Guard.IsTrue(stages.Count > 0, nameof(this.CurveB), "AToB tag must contain at least one processing element"); + + for (int i = 1; i < stages.Count; i++) + { + Guard.IsTrue( + stages[i - 1].Output == stages[i].Input, + stages[i].Name, + $"Output channel count of {stages[i - 1].Name} does not match input channel count of {stages[i].Name}"); + } + + return (stages[0].Input, stages[^1].Output); + } + + /// + /// Verifies that every supplied curve entry is a supported one-dimensional curve type. + /// private void VerifyCurve(IccTagDataEntry[] curves, string name) { if (curves != null) @@ -240,6 +242,9 @@ private void VerifyCurve(IccTagDataEntry[] curves, string name) } } + /// + /// Verifies the dimensions of the optional matrix components. + /// private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) { if (matrix3x1 != null) @@ -254,6 +259,9 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) } } + /// + /// Creates the one-dimensional matrix vector when present. + /// private static Vector3? CreateMatrix3x1(float[] matrix) { if (matrix is null) @@ -264,6 +272,9 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) return new Vector3(matrix[0], matrix[1], matrix[2]); } + /// + /// Creates the three-by-three matrix when present. + /// private static Matrix4x4? CreateMatrix3x3(float[,] matrix) { if (matrix is null) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 033b809894..37e7b408d2 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -64,44 +64,7 @@ public IccLutBToATagDataEntry( this.CurveM = curveM; this.ClutValues = clutValues; - if (this.IsBMatrixMClutA()) - { - Guard.IsTrue(this.CurveB.Length == 3, nameof(this.CurveB), $"{nameof(this.CurveB)} must have a length of three"); - Guard.IsTrue(this.CurveM.Length == 3, nameof(this.CurveM), $"{nameof(this.CurveM)} must have a length of three"); - Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); - - this.InputChannelCount = 3; - this.OutputChannelCount = curveA.Length; - - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); - } - else if (this.IsBMatrixM()) - { - Guard.IsTrue(this.CurveB.Length == 3, nameof(this.CurveB), $"{nameof(this.CurveB)} must have a length of three"); - Guard.IsTrue(this.CurveM.Length == 3, nameof(this.CurveM), $"{nameof(this.CurveM)} must have a length of three"); - - this.InputChannelCount = this.OutputChannelCount = 3; - } - else if (this.IsBClutA()) - { - Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); - Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); - - this.InputChannelCount = curveB.Length; - this.OutputChannelCount = curveA.Length; - - Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size"); - Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size"); - } - else if (this.IsB()) - { - this.InputChannelCount = this.OutputChannelCount = this.CurveB.Length; - } - else - { - throw new ArgumentException("Invalid combination of values given"); - } + (this.InputChannelCount, this.OutputChannelCount) = this.GetChannelCounts(); } /// @@ -165,7 +128,7 @@ public bool Equals(IccLutBToATagDataEntry other) && this.OutputChannelCount == other.OutputChannelCount && this.Matrix3x3.Equals(other.Matrix3x3) && this.Matrix3x1.Equals(other.Matrix3x1) - && this.ClutValues.Equals(other.ClutValues) + && Equals(this.ClutValues, other.ClutValues) && EqualsCurve(this.CurveB, other.CurveB) && EqualsCurve(this.CurveM, other.CurveM) && EqualsCurve(this.CurveA, other.CurveA); @@ -191,6 +154,9 @@ public override int GetHashCode() return hashCode.ToHashCode(); } + /// + /// Compares two curve arrays, treating consistently. + /// private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) { bool thisNull = thisCurves is null; @@ -201,7 +167,7 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return true; } - if (entryNull) + if (thisNull || entryNull) { return false; } @@ -209,17 +175,63 @@ private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] return thisCurves.SequenceEqual(entryCurves); } - private bool IsBMatrixMClutA() - => this.CurveB != null && this.Matrix3x3 != null && this.Matrix3x1 != null && this.CurveM != null && this.ClutValues != null && this.CurveA != null; + /// + /// Validates the configured processing stages and derives the external channel counts. + /// + /// + /// Stages are evaluated in ICC mBA order: B, Matrix, M, CLUT, A. + /// Sparse pipelines are valid as long as adjacent stages agree on channel counts. + /// + private (int InputChannelCount, int OutputChannelCount) GetChannelCounts() + { + // There are at most five possible mBA stages: B, Matrix, M, CLUT, and A. + List<(int Input, int Output, string Name)> stages = new(5); - private bool IsBMatrixM() - => this.CurveB != null && this.Matrix3x3 != null && this.Matrix3x1 != null && this.CurveM != null; + if (this.CurveB != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveB.Length, 1, 15, nameof(this.CurveB)); + stages.Add((this.CurveB.Length, this.CurveB.Length, nameof(this.CurveB))); + } - private bool IsBClutA() - => this.CurveB != null && this.ClutValues != null && this.CurveA != null; + if (this.Matrix3x3 != null || this.Matrix3x1 != null) + { + Guard.IsTrue(this.Matrix3x3 != null && this.Matrix3x1 != null, nameof(this.Matrix3x3), "Matrix must include both the 3x3 and 3x1 components"); + stages.Add((3, 3, nameof(this.Matrix3x3))); + } - private bool IsB() => this.CurveB != null; + if (this.CurveM != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveM.Length, 1, 15, nameof(this.CurveM)); + stages.Add((this.CurveM.Length, this.CurveM.Length, nameof(this.CurveM))); + } + + if (this.ClutValues != null) + { + stages.Add((this.ClutValues.InputChannelCount, this.ClutValues.OutputChannelCount, nameof(this.ClutValues))); + } + if (this.CurveA != null) + { + Guard.MustBeBetweenOrEqualTo(this.CurveA.Length, 1, 15, nameof(this.CurveA)); + stages.Add((this.CurveA.Length, this.CurveA.Length, nameof(this.CurveA))); + } + + Guard.IsTrue(stages.Count > 0, nameof(this.CurveB), "BToA tag must contain at least one processing element"); + + for (int i = 1; i < stages.Count; i++) + { + Guard.IsTrue( + stages[i - 1].Output == stages[i].Input, + stages[i].Name, + $"Output channel count of {stages[i - 1].Name} does not match input channel count of {stages[i].Name}"); + } + + return (stages[0].Input, stages[^1].Output); + } + + /// + /// Verifies that every supplied curve entry is a supported one-dimensional curve type. + /// private void VerifyCurve(IccTagDataEntry[] curves, string name) { if (curves != null) @@ -229,6 +241,9 @@ private void VerifyCurve(IccTagDataEntry[] curves, string name) } } + /// + /// Verifies the dimensions of the optional matrix components. + /// private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) { if (matrix3x1 != null) @@ -243,6 +258,9 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) } } + /// + /// Creates the one-dimensional matrix vector when present. + /// private static Vector3? CreateMatrix3x1(float[] matrix) { if (matrix is null) @@ -253,6 +271,9 @@ private static void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) return new Vector3(matrix[0], matrix[1], matrix[2]); } + /// + /// Creates the three-by-three matrix when present. + /// private static Matrix4x4? CreateMatrix3x3(float[,] matrix) { if (matrix is null) diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs index 26a882810e..22165ec5b6 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs @@ -1,20 +1,21 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; /// -/// Color Lookup Table +/// Color Lookup Table. /// internal sealed class IccClut : IEquatable { /// /// Initializes a new instance of the class. /// - /// The CLUT values - /// The gridpoint count - /// The data type of this CLUT - public IccClut(float[][] values, byte[] gridPointCount, IccClutDataType type) + /// The CLUT values. + /// The gridpoint count. + /// The data type of this CLUT. + /// The output channels count. + public IccClut(float[] values, byte[] gridPointCount, IccClutDataType type, int outputChannelCount) { Guard.NotNull(values, nameof(values)); Guard.NotNull(gridPointCount, nameof(gridPointCount)); @@ -22,91 +23,33 @@ public IccClut(float[][] values, byte[] gridPointCount, IccClutDataType type) this.Values = values; this.DataType = type; this.InputChannelCount = gridPointCount.Length; - this.OutputChannelCount = values[0].Length; + this.OutputChannelCount = outputChannelCount; this.GridPointCount = gridPointCount; this.CheckValues(); } /// - /// Initializes a new instance of the class. + /// Gets the values that make up this table. /// - /// The CLUT values - /// The gridpoint count - public IccClut(ushort[][] values, byte[] gridPointCount) - { - Guard.NotNull(values, nameof(values)); - Guard.NotNull(gridPointCount, nameof(gridPointCount)); - - const float Max = ushort.MaxValue; - - this.Values = new float[values.Length][]; - for (int i = 0; i < values.Length; i++) - { - this.Values[i] = new float[values[i].Length]; - for (int j = 0; j < values[i].Length; j++) - { - this.Values[i][j] = values[i][j] / Max; - } - } - - this.DataType = IccClutDataType.UInt16; - this.InputChannelCount = gridPointCount.Length; - this.OutputChannelCount = values[0].Length; - this.GridPointCount = gridPointCount; - this.CheckValues(); - } + public float[] Values { get; } /// - /// Initializes a new instance of the class. - /// - /// The CLUT values - /// The gridpoint count - public IccClut(byte[][] values, byte[] gridPointCount) - { - Guard.NotNull(values, nameof(values)); - Guard.NotNull(gridPointCount, nameof(gridPointCount)); - - const float Max = byte.MaxValue; - - this.Values = new float[values.Length][]; - for (int i = 0; i < values.Length; i++) - { - this.Values[i] = new float[values[i].Length]; - for (int j = 0; j < values[i].Length; j++) - { - this.Values[i][j] = values[i][j] / Max; - } - } - - this.DataType = IccClutDataType.UInt8; - this.InputChannelCount = gridPointCount.Length; - this.OutputChannelCount = values[0].Length; - this.GridPointCount = gridPointCount; - this.CheckValues(); - } - - /// - /// Gets the values that make up this table - /// - public float[][] Values { get; } - - /// - /// Gets the CLUT data type (important when writing a profile) + /// Gets the CLUT data type (important when writing a profile). /// public IccClutDataType DataType { get; } /// - /// Gets the number of input channels + /// Gets the number of input channels. /// public int InputChannelCount { get; } /// - /// Gets the number of output channels + /// Gets the number of output channels. /// public int OutputChannelCount { get; } /// - /// Gets the number of grid points per input channel + /// Gets the number of grid points per input channel. /// public byte[] GridPointCount { get; } @@ -134,15 +77,12 @@ public bool Equals(IccClut? other) public override bool Equals(object? obj) => obj is IccClut other && this.Equals(other); /// - public override int GetHashCode() - { - return HashCode.Combine( + public override int GetHashCode() => HashCode.Combine( this.Values, this.DataType, this.InputChannelCount, this.OutputChannelCount, this.GridPointCount); - } private bool EqualsValuesArray(IccClut other) { @@ -151,15 +91,7 @@ private bool EqualsValuesArray(IccClut other) return false; } - for (int i = 0; i < this.Values.Length; i++) - { - if (!this.Values[i].AsSpan().SequenceEqual(other.Values[i])) - { - return false; - } - } - - return true; + return this.Values.SequenceEqual(other.Values); } private void CheckValues() @@ -167,17 +99,13 @@ private void CheckValues() Guard.MustBeBetweenOrEqualTo(this.InputChannelCount, 1, 15, nameof(this.InputChannelCount)); Guard.MustBeBetweenOrEqualTo(this.OutputChannelCount, 1, 15, nameof(this.OutputChannelCount)); - bool isLengthDifferent = this.Values.Any(t => t.Length != this.OutputChannelCount); - Guard.IsFalse(isLengthDifferent, nameof(this.Values), "The number of output values varies"); - int length = 0; for (int i = 0; i < this.InputChannelCount; i++) { length += (int)Math.Pow(this.GridPointCount[i], this.InputChannelCount); } - length /= this.InputChannelCount; - - Guard.IsTrue(this.Values.Length == length, nameof(this.Values), "Length of values array does not match the grid points"); + // TODO: Disabled this check, not sure if this check is correct. + // Guard.IsTrue(this.Values.Length == length, nameof(this.Values), "Length of values array does not match the grid points"); } } diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs index 5f07e55890..fb47409bea 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs @@ -23,13 +23,8 @@ public IccLut(ushort[] values) { Guard.NotNull(values, nameof(values)); - const float max = ushort.MaxValue; - this.Values = new float[values.Length]; - for (int i = 0; i < values.Length; i++) - { - this.Values[i] = values[i] / max; - } + IccLutNormalizer.Normalize(values, this.Values); } /// @@ -40,13 +35,8 @@ public IccLut(byte[] values) { Guard.NotNull(values, nameof(values)); - const float max = byte.MaxValue; - this.Values = new float[values.Length]; - for (int i = 0; i < values.Length; i++) - { - this.Values[i] = values[i] / max; - } + IccLutNormalizer.Normalize(values, this.Values); } /// diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLutNormalizer.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLutNormalizer.cs new file mode 100644 index 0000000000..67796cad3a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLutNormalizer.cs @@ -0,0 +1,253 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; + +/// +/// Converts integer ICC lookup-table entries to their normalized single-precision representation. +/// +internal static class IccLutNormalizer +{ + /// + /// Defines the scalar and SIMD conversion for an integer lookup-table element type. + /// + /// The integer element type. + private interface INormalizeOperator + where T : unmanaged + { + /// + /// Gets the divisor that maps the integer range to [0, 1]. + /// + public static abstract float Divisor { get; } + + /// + /// Converts one scalar value. + /// + /// The integer value. + /// The normalized value. + public static abstract float Invoke(T source); + + /// + /// Converts one 128-bit input vector and stores the expanded single-precision results. + /// + /// The packed integer values. + /// The normalization divisor. + /// The first destination element. + public static abstract void Invoke(Vector128 source, Vector128 divisor, ref float destination); + + /// + /// Converts one 256-bit input vector and stores the expanded single-precision results. + /// + /// The packed integer values. + /// The normalization divisor. + /// The first destination element. + public static abstract void Invoke(Vector256 source, Vector256 divisor, ref float destination); + + /// + /// Converts one 512-bit input vector and stores the expanded single-precision results. + /// + /// The packed integer values. + /// The normalization divisor. + /// The first destination element. + public static abstract void Invoke(Vector512 source, Vector512 divisor, ref float destination); + } + + /// + /// Converts byte lookup-table entries to normalized single-precision values. + /// + /// The integer lookup-table entries. + /// The normalized destination values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ReadOnlySpan source, Span destination) + => Normalize(source, destination); + + /// + /// Converts unsigned-short lookup-table entries to normalized single-precision values. + /// + /// The integer lookup-table entries. + /// The normalized destination values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(ReadOnlySpan source, Span destination) + => Normalize(source, destination); + + /// + /// Converts an integer lookup table using the widest available portable SIMD width, followed by narrower + /// widths and a scalar remainder. + /// + /// The integer element type. + /// The conversion implementation. + /// The integer lookup-table entries. + /// The normalized destination values. + private static void Normalize(ReadOnlySpan source, Span destination) + where T : unmanaged + where TOperator : struct, INormalizeOperator + { + ref T sourceRef = ref MemoryMarshal.GetReference(source); + ref float destinationRef = ref MemoryMarshal.GetReference(destination); + nuint length = (uint)source.Length; + nuint index = 0; + + if (Vector512.IsHardwareAccelerated) + { + Vector512 divisor = Vector512.Create(TOperator.Divisor); + nuint count = (uint)Vector512.Count; + + while (length - index >= count) + { + ref float destinationStart = ref Unsafe.Add(ref destinationRef, index); + TOperator.Invoke(Vector512.LoadUnsafe(ref sourceRef, index), divisor, ref destinationStart); + index += count; + } + } + + if (Vector256.IsHardwareAccelerated) + { + Vector256 divisor = Vector256.Create(TOperator.Divisor); + nuint count = (uint)Vector256.Count; + + while (length - index >= count) + { + ref float destinationStart = ref Unsafe.Add(ref destinationRef, index); + TOperator.Invoke(Vector256.LoadUnsafe(ref sourceRef, index), divisor, ref destinationStart); + index += count; + } + } + + if (Vector128.IsHardwareAccelerated) + { + Vector128 divisor = Vector128.Create(TOperator.Divisor); + nuint count = (uint)Vector128.Count; + + while (length - index >= count) + { + ref float destinationStart = ref Unsafe.Add(ref destinationRef, index); + TOperator.Invoke(Vector128.LoadUnsafe(ref sourceRef, index), divisor, ref destinationStart); + index += count; + } + } + + // Preserve the scalar division expression for the final partial vector. Multiplication by a reciprocal + // is not bit-equivalent for every input and would change the values stored in the ICC profile model. + while (index < length) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke(Unsafe.Add(ref sourceRef, index)); + index++; + } + } + + /// + /// Converts packed byte entries to normalized single-precision values. + /// + private readonly struct ByteNormalizeOperator : INormalizeOperator + { + /// + public static float Divisor => byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Invoke(byte source) + => source / (float)byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector128 source, Vector128 divisor, ref float destination) + { + // [b0..b15] becomes four ordered groups of four UInt32 values. Every widened value is at most + // 255, so reinterpreting UInt32 as Int32 before conversion preserves its numeric value. + (Vector128 lower16, Vector128 upper16) = Vector128.Widen(source); + (Vector128 values0, Vector128 values1) = Vector128.Widen(lower16); + (Vector128 values2, Vector128 values3) = Vector128.Widen(upper16); + + (Vector128.ConvertToSingle(values0.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector128.ConvertToSingle(values1.AsInt32()) / divisor).StoreUnsafe(ref destination, 4); + (Vector128.ConvertToSingle(values2.AsInt32()) / divisor).StoreUnsafe(ref destination, 8); + (Vector128.ConvertToSingle(values3.AsInt32()) / divisor).StoreUnsafe(ref destination, 12); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector256 source, Vector256 divisor, ref float destination) + { + // [b0..b31] becomes four ordered groups of eight UInt32 values, matching four contiguous + // Vector256 stores without shuffling the converted results. + (Vector256 lower16, Vector256 upper16) = Vector256.Widen(source); + (Vector256 values0, Vector256 values1) = Vector256.Widen(lower16); + (Vector256 values2, Vector256 values3) = Vector256.Widen(upper16); + + (Vector256.ConvertToSingle(values0.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector256.ConvertToSingle(values1.AsInt32()) / divisor).StoreUnsafe(ref destination, 8); + (Vector256.ConvertToSingle(values2.AsInt32()) / divisor).StoreUnsafe(ref destination, 16); + (Vector256.ConvertToSingle(values3.AsInt32()) / divisor).StoreUnsafe(ref destination, 24); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector512 source, Vector512 divisor, ref float destination) + { + // [b0..b63] becomes four ordered groups of sixteen UInt32 values, matching four contiguous + // Vector512 stores. The portable widening APIs map to zero-extension instructions. + (Vector512 lower16, Vector512 upper16) = Vector512.Widen(source); + (Vector512 values0, Vector512 values1) = Vector512.Widen(lower16); + (Vector512 values2, Vector512 values3) = Vector512.Widen(upper16); + + (Vector512.ConvertToSingle(values0.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector512.ConvertToSingle(values1.AsInt32()) / divisor).StoreUnsafe(ref destination, 16); + (Vector512.ConvertToSingle(values2.AsInt32()) / divisor).StoreUnsafe(ref destination, 32); + (Vector512.ConvertToSingle(values3.AsInt32()) / divisor).StoreUnsafe(ref destination, 48); + } + } + + /// + /// Converts packed unsigned-short entries to normalized single-precision values. + /// + private readonly struct UInt16NormalizeOperator : INormalizeOperator + { + /// + public static float Divisor => ushort.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Invoke(ushort source) + => source / (float)ushort.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector128 source, Vector128 divisor, ref float destination) + { + // [u0..u7] becomes two ordered groups of four UInt32 values. Every value is at most 65535, + // so signed conversion after reinterpretation is numerically identical to unsigned conversion. + (Vector128 lower, Vector128 upper) = Vector128.Widen(source); + + (Vector128.ConvertToSingle(lower.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector128.ConvertToSingle(upper.AsInt32()) / divisor).StoreUnsafe(ref destination, 4); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector256 source, Vector256 divisor, ref float destination) + { + // [u0..u15] becomes two ordered groups of eight UInt32 values, matching two contiguous + // Vector256 stores without a result shuffle. + (Vector256 lower, Vector256 upper) = Vector256.Widen(source); + + (Vector256.ConvertToSingle(lower.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector256.ConvertToSingle(upper.AsInt32()) / divisor).StoreUnsafe(ref destination, 8); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invoke(Vector512 source, Vector512 divisor, ref float destination) + { + // [u0..u31] becomes two ordered groups of sixteen UInt32 values, matching two contiguous + // Vector512 stores. The portable widening APIs map to zero-extension instructions. + (Vector512 lower, Vector512 upper) = Vector512.Widen(source); + + (Vector512.ConvertToSingle(lower.AsInt32()) / divisor).StoreUnsafe(ref destination); + (Vector512.ConvertToSingle(upper.AsInt32()) / divisor).StoreUnsafe(ref destination, 16); + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs index e7d7461d5d..a71cbfaf5a 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -49,9 +49,7 @@ public IccTagTableEntry(IccProfileTag signature, uint offset, uint dataSize) /// True if the parameter is equal to the parameter; otherwise, false. /// public static bool operator ==(IccTagTableEntry left, IccTagTableEntry right) - { - return left.Equals(right); - } + => left.Equals(right); /// /// Compares two objects for equality. @@ -62,9 +60,7 @@ public IccTagTableEntry(IccProfileTag signature, uint offset, uint dataSize) /// True if the parameter is not equal to the parameter; otherwise, false. /// public static bool operator !=(IccTagTableEntry left, IccTagTableEntry right) - { - return !left.Equals(right); - } + => !left.Equals(right); /// public override bool Equals(object? obj) => obj is IccTagTableEntry other && this.Equals(other); diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 162fae96b3..306924e62c 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -3,7 +3,6 @@ using System.Buffers.Binary; using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using SixLabors.ImageSharp.Metadata.Profiles.IPTC; @@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; /// public sealed class IptcProfile : IDeepCloneable { - private readonly Collection values = new(); + private readonly Collection values = []; private const byte IptcTagMarkerByte = 0x1c; @@ -68,7 +67,7 @@ private IptcProfile(IptcProfile other) /// /// Gets a byte array marking that UTF-8 encoding is used in application records. /// - private static ReadOnlySpan CodedCharacterSetUtf8Value => new byte[] { 0x1B, 0x25, 0x47 }; // Uses C#'s optimization to refer to the data segment in the assembly directly, no allocation occurs. + private static ReadOnlySpan CodedCharacterSetUtf8Value => [0x1B, 0x25, 0x47]; // Uses C#'s optimization to refer to the data segment in the assembly directly, no allocation occurs. /// /// Gets the byte data of the IPTC profile. @@ -90,7 +89,7 @@ private IptcProfile(IptcProfile other) /// The values found with the specified tag. public List GetValues(IptcTag tag) { - List iptcValues = new(); + List iptcValues = []; foreach (IptcValue iptcValue in this.Values) { if (iptcValue.Tag == tag) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs index 2d5fe6a09a..bbbeb83e09 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcRecordNumber.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.IPTC; internal enum IptcRecordNumber : byte { /// - /// A Envelope Record. + /// An Envelope Record. /// Envelope = 0x01, /// - /// A Application Record. + /// An Application Record. /// Application = 0x02 } diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs index 1a75ecba22..65daf59368 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; +using System.Globalization; using System.Text; namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; @@ -8,9 +10,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; /// /// Represents a single value of the IPTC profile. /// +[DebuggerDisplay("{Tag} = {DebuggerDisplayValue(),nq} ({GetType().Name,nq})")] public sealed class IptcValue : IDeepCloneable { - private byte[] data = Array.Empty(); + private byte[] data = []; private Encoding encoding; internal IptcValue(IptcValue other) @@ -89,7 +92,7 @@ public string Value { if (string.IsNullOrEmpty(value)) { - this.data = Array.Empty(); + this.data = []; } else { @@ -122,7 +125,7 @@ public string Value public int Length => this.data.Length; /// - public IptcValue DeepClone() => new IptcValue(this); + public IptcValue DeepClone() => new(this); /// /// Determines whether the specified object is equal to the current . @@ -189,7 +192,7 @@ public bool Equals(IptcValue? other) /// A array. public byte[] ToByteArray() { - var result = new byte[this.data.Length]; + byte[] result = new byte[this.data.Length]; this.data.CopyTo(result, 0); return result; } @@ -211,4 +214,37 @@ public string ToString(Encoding encoding) return encoding.GetString(this.data); } + + private string DebuggerDisplayValue() + { + // IPTC RecordVersion (2:00) is a 2-byte binary value, commonly 0x0004. + // Showing it as UTF-8 produces control characters like "\0\u0004". + if (this.Tag == IptcTag.RecordVersion && this.data.Length == 2) + { + int version = (this.data[0] << 8) | this.data[1]; + return version.ToString(CultureInfo.InvariantCulture); + } + + // Prefer readable text if it looks like it, otherwise show hex. + // (Avoid surprising debugger output for binary payloads.) + bool printable = true; + for (int i = 0; i < this.data.Length; i++) + { + byte b = this.data[i]; + + // If any byte is an ASCII control character, treat this value as binary. + if (b is < 0x20 or 0x7F) + { + printable = false; + break; + } + } + + if (printable) + { + return this.Value; + } + + return Convert.ToHexString(this.data); + } } diff --git a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs index 77ff35df0e..639f097226 100644 --- a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Diagnostics; using System.Text; +using System.Xml; using System.Xml.Linq; namespace SixLabors.ImageSharp.Metadata.Profiles.Xmp; @@ -25,18 +25,17 @@ public XmpProfile() /// Initializes a new instance of the class. /// /// The UTF8 encoded byte array to read the XMP profile from. - public XmpProfile(byte[]? data) => this.Data = data; + public XmpProfile(byte[]? data) => this.Data = NormalizeDataIfNeeded(data); /// - /// Initializes a new instance of the class - /// by making a copy from another XMP profile. + /// Initializes a new instance of the class from an XML document. + /// The document is serialized as UTF-8 without BOM. /// - /// The other XMP profile, from which the clone should be made from. - private XmpProfile(XmpProfile other) + /// The XMP XML document. + public XmpProfile(XDocument document) { - Guard.NotNull(other, nameof(other)); - - this.Data = other.Data; + Guard.NotNull(document, nameof(document)); + this.Data = SerializeDocument(document); } /// @@ -45,30 +44,28 @@ private XmpProfile(XmpProfile other) internal byte[]? Data { get; private set; } /// - /// Gets the raw XML document containing the XMP profile. + /// Convert the content of this into an . /// - /// The - public XDocument? GetDocument() + /// The instance, or if no XMP data is present. + public XDocument? ToXDocument() { - byte[]? byteArray = this.Data; - if (byteArray is null) + byte[]? data = this.Data; + if (data is null || data.Length == 0) { return null; } - // Strip leading whitespace, as the XmlReader doesn't like them. - int count = byteArray.Length; - for (int i = count - 1; i > 0; i--) + using MemoryStream stream = new(data, writable: false); + + XmlReaderSettings settings = new() { - if (byteArray[i] is 0 or 0x0f) - { - count--; - } - } + DtdProcessing = DtdProcessing.Ignore, + XmlResolver = null, + CloseInput = false + }; - using MemoryStream stream = new(byteArray, 0, count); - using StreamReader reader = new(stream, Encoding.UTF8); - return XDocument.Load(reader); + using XmlReader reader = XmlReader.Create(stream, settings); + return XDocument.Load(reader, LoadOptions.PreserveWhitespace); } /// @@ -77,12 +74,101 @@ private XmpProfile(XmpProfile other) /// The public byte[] ToByteArray() { - Guard.NotNull(this.Data); - byte[] result = new byte[this.Data.Length]; + byte[]? data = this.Data; + + if (data is null) + { + return []; + } + + byte[] result = new byte[data.Length]; this.Data.AsSpan().CopyTo(result); return result; } /// - public XmpProfile DeepClone() => new(this); + public XmpProfile DeepClone() + { + byte[]? data = this.Data; + if (data is null) + { + // Preserve the semantics of an "empty" profile when cloning. + return new XmpProfile(); + } + + byte[] clone = new byte[data.Length]; + data.AsSpan().CopyTo(clone); + return new XmpProfile(clone); + } + + private static byte[] SerializeDocument(XDocument document) + { + using MemoryStream ms = new(); + + XmlWriterSettings writerSettings = new() + { + Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), // no BOM + OmitXmlDeclaration = true, // generally safer for XMP consumers + Indent = false, + NewLineHandling = NewLineHandling.None + }; + + using (XmlWriter xw = XmlWriter.Create(ms, writerSettings)) + { + document.Save(xw); + } + + return ms.ToArray(); + } + + private static byte[]? NormalizeDataIfNeeded(byte[]? data) + { + if (data is null || data.Length == 0) + { + return data; + } + + // Allocation-free fast path for the normal case. + + // Check for UTF-8 BOM (0xEF,0xBB,0xBF) + bool hasBom = data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF; + + // XMP metadata is commonly stored in fixed-size container blocks (e.g. TIFF tag 700). + // Producers often pad unused space so the packet can be updated in-place without + // rewriting the file. In practice this padding is either NUL (0x00) from the container + // or 0x0F used by Adobe XMP writers. Both are invalid XML and must be trimmed. + bool hasTrailingPad = data[^1] is 0 or 0x0F; + + if (!hasBom && !hasTrailingPad) + { + return data; + } + + int start = hasBom ? 3 : 0; + int end = data.Length; + + if (hasTrailingPad) + { + while (end > start) + { + byte b = data[end - 1]; + if (b is not 0 and not 0x0F) + { + break; + } + + end--; + } + } + + int length = end - start; + if (length <= 0) + { + return null; + } + + byte[] normalized = new byte[length]; + Buffer.BlockCopy(data, start, normalized, 0, length); + return normalized; + } } diff --git a/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs new file mode 100644 index 0000000000..ad26eb4e61 --- /dev/null +++ b/src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs @@ -0,0 +1,387 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides bulk operations for pixel formats that store associated alpha. +/// +/// The associated-alpha pixel format. +public abstract class AssociatedAlphaPixelOperations : PixelOperations + where TPixel : unmanaged, IPixel +{ + /// + public override PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + => AssociatedAlphaPixelBlenders.GetPixelBlender(colorMode, alphaMode); + + /// + protected abstract override void ToUnassociatedVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination); + + /// + protected abstract override void ToAssociatedVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination); + + /// + protected abstract override void FromUnassociatedVector4Destructive( + Configuration configuration, + Span source, + Span destination); + + /// + protected abstract override void FromAssociatedVector4Destructive( + Configuration configuration, + Span source, + Span destination); + + /// + protected abstract override void ToUnassociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination); + + /// + protected abstract override void ToAssociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination); + + /// + protected abstract override void FromUnassociatedScaledVector4Destructive( + Configuration configuration, + Span source, + Span destination); + + /// + protected abstract override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination); + + /// + public override void From( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + if (source.IsEmpty) + { + return; + } + + // Cap large conversions at 1,024 vectors while avoiding a 16 KiB rental for short spans. + int sliceLength = Math.Min(source.Length, 1024); + int numberOfSlices = source.Length / sliceLength; + + using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength); + Span vectorSpan = tempVectors.GetSpan()[..sliceLength]; + + // Convert through unassociated vectors so the destination operation can quantize alpha to its own storage before associating RGB. + for (int i = 0; i < numberOfSlices; i++) + { + int start = i * sliceLength; + ReadOnlySpan sourceSlice = source.Slice(start, sliceLength); + Span destinationSlice = destination.Slice(start, sliceLength); + PixelOperations.Instance.ToVector4( + configuration, + sourceSlice, + vectorSpan, + PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + this.FromUnassociatedScaledVector4Destructive(configuration, vectorSpan, destinationSlice); + } + + int endOfCompleteSlices = numberOfSlices * sliceLength; + int remainder = source.Length - endOfCompleteSlices; + + if (remainder > 0) + { + ReadOnlySpan sourceSlice = source[endOfCompleteSlices..]; + Span destinationSlice = destination.Slice(endOfCompleteSlices, remainder); + vectorSpan = vectorSpan[..remainder]; + PixelOperations.Instance.ToVector4( + configuration, + sourceSlice, + vectorSpan, + PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + this.FromUnassociatedScaledVector4Destructive(configuration, vectorSpan, destinationSlice); + } + } + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destination, + PixelConversionModifiers modifiers) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply) || !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply); + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + // Transfer functions operate on straight color components. Associated input must therefore be unassociated before companding. + if (associated) + { + Numerics.UnPremultiply(sourceVectors); + } + + SRgbCompanding.Compress(sourceVectors); + + if (scaled) + { + this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); + } + + return; + } + + if (scaled) + { + if (associated) + { + this.FromAssociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + } + else if (associated) + { + this.FromAssociatedVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); + } + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan source, + Span destinationVectors, + PixelConversionModifiers modifiers) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destinationVectors, nameof(destinationVectors)); + + bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply) || !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply); + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + // Extract straight color before applying the transfer function; companding associated components would make RGB depend on alpha. + if (scaled) + { + this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedVector4(configuration, source, destinationVectors); + } + + Span converted = destinationVectors[..source.Length]; + SRgbCompanding.Expand(converted); + + if (associated) + { + Numerics.Premultiply(converted); + } + + return; + } + + if (scaled) + { + if (associated) + { + this.ToAssociatedScaledVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); + } + } + else if (associated) + { + this.ToAssociatedVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedVector4(configuration, source, destinationVectors); + } + } + + /// + public override void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromL8(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromL16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromLa16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromLa32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) + => this.From(configuration, source, destination); + + /// + public override void ToArgb32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToAbgr32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgr24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgra32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToL8(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToL16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToLa16(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToLa32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgb24(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgba32(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgb48(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToRgba64(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + public override void ToBgra5551(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ConvertToUnassociated(configuration, source, destination); + + /// + /// Converts associated source pixels to an unassociated destination format. + /// + /// The destination pixel format. + /// The configuration. + /// The source pixels. + /// The destination pixels. + private void ConvertToUnassociated( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TDestinationPixel : unmanaged, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + if (source.IsEmpty) + { + return; + } + + int sliceLength = Math.Min(source.Length, 1024); + int numberOfSlices = source.Length / sliceLength; + + using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength); + Span vectorSpan = tempVectors.GetSpan()[..sliceLength]; + PixelOperations destinationOperations = PixelOperations.Instance; + + // Generated destination dispatch routes conversion back through this source operation's ToX override. Extract through this + // operation's protected bulk hook, then use the destination's public modifier contract to avoid recursive dispatch. + for (int i = 0; i < numberOfSlices; i++) + { + int start = i * sliceLength; + ReadOnlySpan sourceSlice = source.Slice(start, sliceLength); + Span destinationSlice = destination.Slice(start, sliceLength); + this.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); + destinationOperations.FromVector4Destructive(configuration, vectorSpan, destinationSlice, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + } + + int endOfCompleteSlices = numberOfSlices * sliceLength; + int remainder = source.Length - endOfCompleteSlices; + + if (remainder > 0) + { + ReadOnlySpan sourceSlice = source[endOfCompleteSlices..]; + Span destinationSlice = destination.Slice(endOfCompleteSlices, remainder); + vectorSpan = vectorSpan[..remainder]; + this.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan); + destinationOperations.FromVector4Destructive(configuration, vectorSpan, destinationSlice, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + } + } +} diff --git a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs index 02936f1602..6de4b30b9e 100644 --- a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs +++ b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs @@ -1,7 +1,10 @@ // 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; namespace SixLabors.ImageSharp.PixelFormats; @@ -10,6 +13,27 @@ namespace SixLabors.ImageSharp.PixelFormats; /// internal static class HalfTypeHelper { + // IEEE 754 binary16 has a largest finite magnitude of 65504. Scaled pixel vectors map that complete finite + // interval to [0, 1], while native vectors continue to expose the stored floating-point value directly. + internal const float FiniteMinimum = -65504F; + internal const float FiniteMaximum = 65504F; + internal const float FiniteRange = FiniteMaximum - FiniteMinimum; + internal const float InverseFiniteRange = (float)(1D / FiniteRange); + internal const float ScaledMidpoint = .5F; + + // These constants mirror the binary16 conversion used by System.Half. Keeping the vector conversion + // bit-for-bit equivalent to the scalar runtime conversion makes SIMD a pure throughput optimization. + private const uint HalfExponentMask = 0x7C00; + private const uint HalfSignMask = 0x8000; + private const uint HalfToSingleBitsMask = 0x0FFF_E000; + private const uint SingleExponentLowerBound = 0x3880_0000; + private const uint SingleExponentOffset = 0x3800_0000; + private const uint SingleExponent126 = 0x3F00_0000; + private const uint SingleBiasedExponentMask = 0x7F80_0000; + private const uint SingleExponent13 = 0x0680_0000; + private const uint SingleSignMask = 0x8000_0000; + private const float MaxHalfValueBelowInfinity = 65520F; + /// /// Packs a into an /// @@ -25,4 +49,480 @@ internal static class HalfTypeHelper /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static float Unpack(ushort value) => (float)BitConverter.UInt16BitsToHalf(value); + + /// + /// Normalizes a binary16 value to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The native binary16 value represented as a . + /// The normalized value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ToScaled(float value) + { + // Clamp after mapping so native infinities reach the scaled endpoints and NaN becomes zero. + return Numerics.Clamp((value * InverseFiniteRange) + ScaledMidpoint, 0F, 1F); + } + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The native binary16 values. + /// The normalized values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 ToScaled(Vector2 value) => ToScaled(value.AsVector128()).AsVector2(); + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The native binary16 values. + /// The normalized values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ToScaled(Vector4 value) => ToScaled(value.AsVector128()).AsVector4(); + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 ToScaled(Vector128 value) + { + Vector128 scaled = (value * Vector128.Create(InverseFiniteRange)) + Vector128.Create(ScaledMidpoint); + + return Numerics.Clamp(scaled, Vector128.Zero, Vector128.One); + } + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ToScaled(Vector256 value) + { + Vector256 scaled = (value * Vector256.Create(InverseFiniteRange)) + Vector256.Create(ScaledMidpoint); + + return Numerics.Clamp(scaled, Vector256.Zero, Vector256.One); + } + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ToScaled(Vector512 value) + { + Vector512 scaled = (value * Vector512.Create(InverseFiniteRange)) + Vector512.Create(ScaledMidpoint); + + return Numerics.Clamp(scaled, Vector512.Zero, Vector512.One); + } + + /// + /// Normalizes binary16 values to [0, 1], saturating infinities and mapping NaN to zero. + /// + /// The component values to convert in place. + public static void ToScaled(Span values) + { + ref Vector4 source = ref MemoryMarshal.GetReference(values); + int i = 0; + + // Each register contains whole RGBA pixels. Convert wide groups first, then narrower + // remainders without revisiting any pixel: mapping the same pixel twice would change its value. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerRegister = Vector512.Count / Vector128.Count; + + for (; i <= values.Length - pixelsPerRegister; i += pixelsPerRegister) + { + ref Vector512 vector = ref Unsafe.As>(ref Unsafe.Add(ref source, (uint)i)); + + vector = ToScaled(vector); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerRegister = Vector256.Count / Vector128.Count; + + for (; i <= values.Length - pixelsPerRegister; i += pixelsPerRegister) + { + ref Vector256 vector = ref Unsafe.As>(ref Unsafe.Add(ref source, (uint)i)); + + vector = ToScaled(vector); + } + } + + // One Vector4 uses the same 128-bit conversion as an individual pixel, including the + // runtime's software fallback when SIMD is unavailable. No separate scalar mapping is needed. + for (; i < values.Length; i++) + { + ref Vector4 vector = ref Unsafe.Add(ref source, (uint)i); + + vector = ToScaled(vector); + } + } + + /// + /// Normalizes a scaled value, mapping NaN to zero, and expands it to the finite binary16 range. + /// + /// The normalized value. + /// The native binary16 value represented as a . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FromScaled(float value) + { + // Clamp before expanding so nonfinite scaled input cannot become nonfinite half storage. + return (Numerics.Clamp(value, 0F, 1F) * FiniteRange) + FiniteMinimum; + } + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The normalized values. + /// The native binary16 values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 FromScaled(Vector2 value) => FromScaled(value.AsVector128()).AsVector2(); + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The normalized values. + /// The native binary16 values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 FromScaled(Vector4 value) => FromScaled(value.AsVector128()).AsVector4(); + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 FromScaled(Vector128 value) + { + Vector128 scaled = Numerics.Clamp(value, Vector128.Zero, Vector128.One); + + return (scaled * Vector128.Create(FiniteRange)) + Vector128.Create(FiniteMinimum); + } + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 FromScaled(Vector256 value) + { + Vector256 scaled = Numerics.Clamp(value, Vector256.Zero, Vector256.One); + + return (scaled * Vector256.Create(FiniteRange)) + Vector256.Create(FiniteMinimum); + } + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The component values. + /// The converted values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 FromScaled(Vector512 value) + { + Vector512 scaled = Numerics.Clamp(value, Vector512.Zero, Vector512.One); + + return (scaled * Vector512.Create(FiniteRange)) + Vector512.Create(FiniteMinimum); + } + + /// + /// Normalizes scaled values, mapping NaN to zero, and expands them to the finite binary16 range. + /// + /// The component values to convert in place. + public static void FromScaled(Span values) + { + ref Vector4 source = ref MemoryMarshal.GetReference(values); + int i = 0; + + // Each register contains whole RGBA pixels. Clamping and expansion happen together in + // the conversion overload, so each pixel is loaded and stored once without a clamp-only pass. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerRegister = Vector512.Count / Vector128.Count; + + for (; i <= values.Length - pixelsPerRegister; i += pixelsPerRegister) + { + ref Vector512 vector = ref Unsafe.As>(ref Unsafe.Add(ref source, (uint)i)); + + vector = FromScaled(vector); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerRegister = Vector256.Count / Vector128.Count; + + for (; i <= values.Length - pixelsPerRegister; i += pixelsPerRegister) + { + ref Vector256 vector = ref Unsafe.As>(ref Unsafe.Add(ref source, (uint)i)); + + vector = FromScaled(vector); + } + } + + // The remaining whole pixels use the same 128-bit conversion as individual pixels, + // or its software fallback. Narrowing the remainder never reprocesses a converted pixel. + for (; i < values.Length; i++) + { + ref Vector4 vector = ref Unsafe.Add(ref source, (uint)i); + + vector = FromScaled(vector); + } + } + + /// + /// Unpacks eight binary16 values into two vectors of single-precision values. + /// + /// The packed binary16 values. + /// The unpacked lower and upper values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static (Vector128 Lower, Vector128 Upper) Unpack(Vector128 value) + { + (Vector128 lower, Vector128 upper) = Vector128.Widen(value); + return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper)); + } + + /// + /// Unpacks sixteen binary16 values into two vectors of single-precision values. + /// + /// The packed binary16 values. + /// The unpacked lower and upper values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static (Vector256 Lower, Vector256 Upper) Unpack(Vector256 value) + { + (Vector256 lower, Vector256 upper) = Vector256.Widen(value); + return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper)); + } + + /// + /// Unpacks thirty-two binary16 values into two vectors of single-precision values. + /// + /// The packed binary16 values. + /// The unpacked lower and upper values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static (Vector512 Lower, Vector512 Upper) Unpack(Vector512 value) + { + (Vector512 lower, Vector512 upper) = Vector512.Widen(value); + return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper)); + } + + /// + /// Packs eight single-precision values into binary16 storage. + /// + /// The lower single-precision values. + /// The upper single-precision values. + /// The packed binary16 values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector128 Pack(Vector128 lower, Vector128 upper) + => Vector128.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper)); + + /// + /// Packs sixteen single-precision values into binary16 storage. + /// + /// The lower single-precision values. + /// The upper single-precision values. + /// The packed binary16 values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector256 Pack(Vector256 lower, Vector256 upper) + => Vector256.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper)); + + /// + /// Packs thirty-two single-precision values into binary16 storage. + /// + /// The lower single-precision values. + /// The upper single-precision values. + /// The packed binary16 values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector512 Pack(Vector512 lower, Vector512 upper) + => Vector512.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper)); + + /// + /// Rounds single-precision values through binary16 without changing the vector width. + /// + /// The single-precision values. + /// The values after binary16 quantization. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector128 RoundToHalf(Vector128 value) + => ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value)); + + /// + /// Rounds single-precision values through binary16 without changing the vector width. + /// + /// The single-precision values. + /// The values after binary16 quantization. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector256 RoundToHalf(Vector256 value) + => ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value)); + + /// + /// Rounds single-precision values through binary16 without changing the vector width. + /// + /// The single-precision values. + /// The values after binary16 quantization. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector512 RoundToHalf(Vector512 value) + => ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value)); + + /// + /// Converts zero-extended binary16 bit patterns to single-precision values. + /// + /// The binary16 bit patterns. + /// The converted single-precision values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ConvertHalfBitsToSingle(Vector128 value) + { + Vector128 sign = Vector128.ShiftLeft(value & Vector128.Create(HalfSignMask), 16); + Vector128 exponent = value & Vector128.Create(HalfExponentMask); + Vector128 subnormalMask = Vector128.Equals(exponent, Vector128.Zero); + Vector128 infinityOrNaNMask = Vector128.Equals(exponent, Vector128.Create(HalfExponentMask)); + Vector128 maskedExponentLowerBound = subnormalMask & Vector128.Create(SingleExponentLowerBound); + Vector128 exponentOffset = Vector128.Create(SingleExponentOffset) | maskedExponentLowerBound; + + // Binary16 and binary32 fraction fields differ by thirteen bits. Subnormals and special values + // need different exponent offsets before that shared field layout can be reinterpreted as float. + Vector128 bits = Vector128.ShiftLeft(value, 13) & Vector128.Create(HalfToSingleBitsMask); + exponentOffset = Vector128.ConditionalSelect(infinityOrNaNMask, Vector128.ShiftLeft(exponentOffset, 1), exponentOffset); + bits += exponentOffset; + Vector128 absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32(); + return (absoluteValue | sign).AsSingle(); + } + + /// + /// Converts zero-extended binary16 bit patterns to single-precision values. + /// + /// The binary16 bit patterns. + /// The converted single-precision values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertHalfBitsToSingle(Vector256 value) + { + Vector256 sign = Vector256.ShiftLeft(value & Vector256.Create(HalfSignMask), 16); + Vector256 exponent = value & Vector256.Create(HalfExponentMask); + Vector256 subnormalMask = Vector256.Equals(exponent, Vector256.Zero); + Vector256 infinityOrNaNMask = Vector256.Equals(exponent, Vector256.Create(HalfExponentMask)); + Vector256 maskedExponentLowerBound = subnormalMask & Vector256.Create(SingleExponentLowerBound); + Vector256 exponentOffset = Vector256.Create(SingleExponentOffset) | maskedExponentLowerBound; + + // Binary16 and binary32 fraction fields differ by thirteen bits. Subnormals and special values + // need different exponent offsets before that shared field layout can be reinterpreted as float. + Vector256 bits = Vector256.ShiftLeft(value, 13) & Vector256.Create(HalfToSingleBitsMask); + exponentOffset = Vector256.ConditionalSelect(infinityOrNaNMask, Vector256.ShiftLeft(exponentOffset, 1), exponentOffset); + bits += exponentOffset; + Vector256 absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32(); + return (absoluteValue | sign).AsSingle(); + } + + /// + /// Converts zero-extended binary16 bit patterns to single-precision values. + /// + /// The binary16 bit patterns. + /// The converted single-precision values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ConvertHalfBitsToSingle(Vector512 value) + { + Vector512 sign = Vector512.ShiftLeft(value & Vector512.Create(HalfSignMask), 16); + Vector512 exponent = value & Vector512.Create(HalfExponentMask); + Vector512 subnormalMask = Vector512.Equals(exponent, Vector512.Zero); + Vector512 infinityOrNaNMask = Vector512.Equals(exponent, Vector512.Create(HalfExponentMask)); + Vector512 maskedExponentLowerBound = subnormalMask & Vector512.Create(SingleExponentLowerBound); + Vector512 exponentOffset = Vector512.Create(SingleExponentOffset) | maskedExponentLowerBound; + + // Binary16 and binary32 fraction fields differ by thirteen bits. Subnormals and special values + // need different exponent offsets before that shared field layout can be reinterpreted as float. + Vector512 bits = Vector512.ShiftLeft(value, 13) & Vector512.Create(HalfToSingleBitsMask); + exponentOffset = Vector512.ConditionalSelect(infinityOrNaNMask, Vector512.ShiftLeft(exponentOffset, 1), exponentOffset); + bits += exponentOffset; + Vector512 absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32(); + return (absoluteValue | sign).AsSingle(); + } + + /// + /// Converts single-precision values to zero-extended binary16 bit patterns. + /// + /// The single-precision values. + /// The binary16 bit patterns in 32-bit lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ConvertSingleToHalfBits(Vector128 value) + { + Vector128 bits = value.AsUInt32(); + Vector128 sign = Vector128.ShiftRightLogical(bits & Vector128.Create(SingleSignMask), 16); + Vector128 realMask = Vector128.Equals(value, value).AsUInt32(); + value = Vector128.Abs(value); + value = Vector128.Min(Vector128.Create(MaxHalfValueBelowInfinity), value); + Vector128 exponentOffset = Vector128.Max(value, Vector128.Create(SingleExponentLowerBound).AsSingle()).AsUInt32(); + exponentOffset &= Vector128.Create(SingleBiasedExponentMask); + exponentOffset += Vector128.Create(SingleExponent13); + + // Adding an exponent-sized float rounds the significand to binary16 precision using IEEE + // round-to-nearest-even. The remaining integer operations realign the exponent and sign fields. + value += exponentOffset.AsSingle(); + bits = value.AsUInt32() - Vector128.Create(SingleExponent126); + Vector128 newExponent = Vector128.ShiftRightLogical(bits, 13); + Vector128 maskedHalfExponentForNaN = ~realMask & Vector128.Create(HalfExponentMask); + bits &= realMask; + bits += newExponent; + bits &= ~maskedHalfExponentForNaN; + return bits | maskedHalfExponentForNaN | sign; + } + + /// + /// Converts single-precision values to zero-extended binary16 bit patterns. + /// + /// The single-precision values. + /// The binary16 bit patterns in 32-bit lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertSingleToHalfBits(Vector256 value) + { + Vector256 bits = value.AsUInt32(); + Vector256 sign = Vector256.ShiftRightLogical(bits & Vector256.Create(SingleSignMask), 16); + Vector256 realMask = Vector256.Equals(value, value).AsUInt32(); + value = Vector256.Abs(value); + value = Vector256.Min(Vector256.Create(MaxHalfValueBelowInfinity), value); + Vector256 exponentOffset = Vector256.Max(value, Vector256.Create(SingleExponentLowerBound).AsSingle()).AsUInt32(); + exponentOffset &= Vector256.Create(SingleBiasedExponentMask); + exponentOffset += Vector256.Create(SingleExponent13); + + // Adding an exponent-sized float rounds the significand to binary16 precision using IEEE + // round-to-nearest-even. The remaining integer operations realign the exponent and sign fields. + value += exponentOffset.AsSingle(); + bits = value.AsUInt32() - Vector256.Create(SingleExponent126); + Vector256 newExponent = Vector256.ShiftRightLogical(bits, 13); + Vector256 maskedHalfExponentForNaN = ~realMask & Vector256.Create(HalfExponentMask); + bits &= realMask; + bits += newExponent; + bits &= ~maskedHalfExponentForNaN; + return bits | maskedHalfExponentForNaN | sign; + } + + /// + /// Converts single-precision values to zero-extended binary16 bit patterns. + /// + /// The single-precision values. + /// The binary16 bit patterns in 32-bit lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ConvertSingleToHalfBits(Vector512 value) + { + Vector512 bits = value.AsUInt32(); + Vector512 sign = Vector512.ShiftRightLogical(bits & Vector512.Create(SingleSignMask), 16); + Vector512 realMask = Vector512.Equals(value, value).AsUInt32(); + value = Vector512.Abs(value); + value = Vector512.Min(Vector512.Create(MaxHalfValueBelowInfinity), value); + Vector512 exponentOffset = Vector512.Max(value, Vector512.Create(SingleExponentLowerBound).AsSingle()).AsUInt32(); + exponentOffset &= Vector512.Create(SingleBiasedExponentMask); + exponentOffset += Vector512.Create(SingleExponent13); + + // Adding an exponent-sized float rounds the significand to binary16 precision using IEEE + // round-to-nearest-even. The remaining integer operations realign the exponent and sign fields. + value += exponentOffset.AsSingle(); + bits = value.AsUInt32() - Vector512.Create(SingleExponent126); + Vector512 newExponent = Vector512.ShiftRightLogical(bits, 13); + Vector512 maskedHalfExponentForNaN = ~realMask & Vector512.Create(HalfExponentMask); + bits &= realMask; + bits += newExponent; + bits &= ~maskedHalfExponentForNaN; + return bits | maskedHalfExponentForNaN | sign; + } } diff --git a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs index b74c0ff44e..18a8df3481 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 0994444668..a4f6312dd2 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -19,122 +19,208 @@ public interface IPixel : IPixel, IEquatable /// This method is not intended to be consumed directly. Use instead. /// /// The instance. - PixelOperations CreatePixelOperations(); -} + public static abstract 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") . + /// Initializes the pixel instance from a generic ("scaled") representation using the pixel type's native alpha representation. + /// The scaled representation uses 0 and 1 as its nominal component bounds. /// - /// The vector to load the pixel from. - void FromScaledVector4(Vector4 vector); + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromScaledVector4(Vector4 source); /// - /// 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. + /// Initializes the pixel instance from a generic ("scaled") whose color components use unassociated alpha. + /// The scaled representation uses 0 and 1 as its nominal component bounds. /// - /// The . - Vector4 ToScaledVector4(); + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromUnassociatedScaledVector4(Vector4 source); /// - /// Initializes the pixel instance from a which is specific to the current pixel type. + /// Initializes the pixel instance from a generic ("scaled") whose color components use associated alpha, + /// representing color multiplied by the logical opacity represented by alpha. + /// The scaled representation uses 0 and 1 as its nominal component bounds. /// - /// The vector to load the pixel from. - void FromVector4(Vector4 vector); + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromAssociatedScaledVector4(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 and uses its native alpha representation. /// - /// The . - Vector4 ToVector4(); + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromVector4(Vector4 source); + + /// + /// Initializes the pixel instance from a which is specific to the current pixel type and whose color components use unassociated alpha. + /// + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromUnassociatedVector4(Vector4 source); + + /// + /// Initializes the pixel instance from a which is specific to the current pixel type and whose color components use associated alpha, + /// representing color multiplied by the logical opacity represented by alpha. + /// + /// The vector to load the pixel from. + /// The . + public static abstract TSelf FromAssociatedVector4(Vector4 source); + + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + /// The . + public static abstract TSelf FromAbgr32(Abgr32 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromArgb32(Argb32 source); + /// The . + public static abstract TSelf FromArgb32(Argb32 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgra5551(Bgra5551 source); + /// The . + public static abstract TSelf FromBgra5551(Bgra5551 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgr24(Bgr24 source); + /// The . + public static abstract TSelf FromBgr24(Bgr24 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromBgra32(Bgra32 source); - - /// - /// Initializes the pixel instance from an value. - /// - /// The value. - void FromAbgr32(Abgr32 source); + /// The . + public static abstract TSelf FromBgra32(Bgra32 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromL8(L8 source); + /// The . + public static abstract TSelf FromL8(L8 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromL16(L16 source); + /// The . + public static abstract TSelf FromL16(L16 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromLa16(La16 source); + /// The . + public static abstract TSelf FromLa16(La16 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromLa32(La32 source); + /// The . + public static abstract TSelf FromLa32(La32 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgb24(Rgb24 source); + /// The . + public static abstract TSelf FromRgb24(Rgb24 source); /// /// 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 . + public static abstract TSelf FromRgba32(Rgba32 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgb48(Rgb48 source); + /// The . + public static abstract TSelf FromRgb48(Rgb48 source); /// /// Initializes the pixel instance from an value. /// /// The value. - void FromRgba64(Rgba64 source); + /// The . + public static abstract TSelf FromRgba64(Rgba64 source); +} + +/// +/// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type. +/// +public interface IPixel +{ + /// + /// Gets the pixel type information. + /// + /// The . + public static abstract PixelTypeInfo GetPixelTypeInfo(); + + /// + /// Convert the pixel instance into representation. + /// + /// The + public Rgba32 ToRgba32(); + + /// + /// Expands the pixel into a generic ("scaled") representation using the pixel type's native alpha representation. + /// The scaled representation uses 0 and 1 as its nominal component bounds. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + public Vector4 ToScaledVector4(); + + /// + /// Expands the pixel into a generic ("scaled") whose color components use unassociated alpha. + /// The scaled representation uses 0 and 1 as its nominal component bounds. + /// When alpha is zero and the pixel's native representation is associated, the color components remain unchanged + /// because no unassociated value can be recovered. + /// + /// The . + public Vector4 ToUnassociatedScaledVector4(); + + /// + /// Expands the pixel into a generic ("scaled") whose color components use associated alpha, + /// representing color multiplied by the logical opacity represented by alpha. + /// The scaled representation uses 0 and 1 as its nominal component bounds. + /// + /// The . + public Vector4 ToAssociatedScaledVector4(); + + /// + /// Expands the pixel into a which is specific to the current pixel type and uses its native alpha representation. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + public Vector4 ToVector4(); + + /// + /// Expands the pixel into a which is specific to the current pixel type and whose color components use unassociated alpha. + /// When alpha is zero and the pixel's native representation is associated, the color components remain unchanged + /// because no unassociated value can be recovered. + /// + /// The . + public Vector4 ToUnassociatedVector4(); + + /// + /// Expands the pixel into a which is specific to the current pixel type and whose color components use associated alpha, + /// representing color multiplied by the logical opacity represented by alpha. + /// + /// The . + public Vector4 ToAssociatedVector4(); } diff --git a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs index f42a264dbc..b003e62a44 100644 --- a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs +++ b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs @@ -66,5 +66,10 @@ public enum PixelAlphaCompositionMode /// /// Clear where they overlap. /// - Xor + Xor, + + /// + /// Adds the source and destination, clamping the result to the supported color and alpha range. + /// + Plus, } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs new file mode 100644 index 0000000000..b553e34125 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs @@ -0,0 +1,6108 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Provides the associated-alpha equations consumed by the shared pixel-blending traversal. +/// +internal static class AssociatedAlphaPixelBlenderOperators +{ + /// + /// Applies the "NormalSrc" associated-alpha composition equation. + /// + public readonly struct NormalSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrc(background, source, amount); + } + + /// + /// Applies the "MultiplySrc" associated-alpha composition equation. + /// + public readonly struct MultiplySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrc(background, source, amount); + } + + /// + /// Applies the "AddSrc" associated-alpha composition equation. + /// + public readonly struct AddSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrc(background, source, amount); + } + + /// + /// Applies the "SubtractSrc" associated-alpha composition equation. + /// + public readonly struct SubtractSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrc(background, source, amount); + } + + /// + /// Applies the "ScreenSrc" associated-alpha composition equation. + /// + public readonly struct ScreenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrc(background, source, amount); + } + + /// + /// Applies the "DarkenSrc" associated-alpha composition equation. + /// + public readonly struct DarkenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrc(background, source, amount); + } + + /// + /// Applies the "LightenSrc" associated-alpha composition equation. + /// + public readonly struct LightenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrc(background, source, amount); + } + + /// + /// Applies the "OverlaySrc" associated-alpha composition equation. + /// + public readonly struct OverlaySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrc(background, source, amount); + } + + /// + /// Applies the "HardLightSrc" associated-alpha composition equation. + /// + public readonly struct HardLightSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrc(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrc" associated-alpha composition equation. + /// + public readonly struct ColorDodgeSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrc(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrc" associated-alpha composition equation. + /// + public readonly struct ColorBurnSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrc(background, source, amount); + } + + /// + /// Applies the "SoftLightSrc" associated-alpha composition equation. + /// + public readonly struct SoftLightSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrc(background, source, amount); + } + + /// + /// Applies the "DifferenceSrc" associated-alpha composition equation. + /// + public readonly struct DifferenceSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrc(background, source, amount); + } + + /// + /// Applies the "ExclusionSrc" associated-alpha composition equation. + /// + public readonly struct ExclusionSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrc(background, source, amount); + } + + /// + /// Applies the "HueSrc" associated-alpha composition equation. + /// + public readonly struct HueSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrc(background, source, amount); + } + + /// + /// Applies the "SaturationSrc" associated-alpha composition equation. + /// + public readonly struct SaturationSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrc(background, source, amount); + } + + /// + /// Applies the "ColorSrc" associated-alpha composition equation. + /// + public readonly struct ColorSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrc(background, source, amount); + } + + /// + /// Applies the "LuminositySrc" associated-alpha composition equation. + /// + public readonly struct LuminositySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrc(background, source, amount); + } + + /// + /// Applies the "NormalSrcAtop" associated-alpha composition equation. + /// + public readonly struct NormalSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcAtop(background, source, amount); + } + + /// + /// Applies the "MultiplySrcAtop" associated-alpha composition equation. + /// + public readonly struct MultiplySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcAtop(background, source, amount); + } + + /// + /// Applies the "AddSrcAtop" associated-alpha composition equation. + /// + public readonly struct AddSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcAtop(background, source, amount); + } + + /// + /// Applies the "SubtractSrcAtop" associated-alpha composition equation. + /// + public readonly struct SubtractSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcAtop(background, source, amount); + } + + /// + /// Applies the "ScreenSrcAtop" associated-alpha composition equation. + /// + public readonly struct ScreenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcAtop(background, source, amount); + } + + /// + /// Applies the "DarkenSrcAtop" associated-alpha composition equation. + /// + public readonly struct DarkenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcAtop(background, source, amount); + } + + /// + /// Applies the "LightenSrcAtop" associated-alpha composition equation. + /// + public readonly struct LightenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcAtop(background, source, amount); + } + + /// + /// Applies the "OverlaySrcAtop" associated-alpha composition equation. + /// + public readonly struct OverlaySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcAtop(background, source, amount); + } + + /// + /// Applies the "HardLightSrcAtop" associated-alpha composition equation. + /// + public readonly struct HardLightSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcAtop" associated-alpha composition equation. + /// + public readonly struct ColorDodgeSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcAtop" associated-alpha composition equation. + /// + public readonly struct ColorBurnSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcAtop" associated-alpha composition equation. + /// + public readonly struct SoftLightSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcAtop" associated-alpha composition equation. + /// + public readonly struct DifferenceSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcAtop" associated-alpha composition equation. + /// + public readonly struct ExclusionSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + } + + /// + /// Applies the "HueSrcAtop" associated-alpha composition equation. + /// + public readonly struct HueSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcAtop(background, source, amount); + } + + /// + /// Applies the "SaturationSrcAtop" associated-alpha composition equation. + /// + public readonly struct SaturationSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorSrcAtop" associated-alpha composition equation. + /// + public readonly struct ColorSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcAtop(background, source, amount); + } + + /// + /// Applies the "LuminositySrcAtop" associated-alpha composition equation. + /// + public readonly struct LuminositySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcAtop(background, source, amount); + } + + /// + /// Applies the "NormalSrcOver" associated-alpha composition equation. + /// + public readonly struct NormalSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOver(background, source, amount); + } + + /// + /// Applies the "MultiplySrcOver" associated-alpha composition equation. + /// + public readonly struct MultiplySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOver(background, source, amount); + } + + /// + /// Applies the "AddSrcOver" associated-alpha composition equation. + /// + public readonly struct AddSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOver(background, source, amount); + } + + /// + /// Applies the "SubtractSrcOver" associated-alpha composition equation. + /// + public readonly struct SubtractSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOver(background, source, amount); + } + + /// + /// Applies the "ScreenSrcOver" associated-alpha composition equation. + /// + public readonly struct ScreenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOver(background, source, amount); + } + + /// + /// Applies the "DarkenSrcOver" associated-alpha composition equation. + /// + public readonly struct DarkenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOver(background, source, amount); + } + + /// + /// Applies the "LightenSrcOver" associated-alpha composition equation. + /// + public readonly struct LightenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOver(background, source, amount); + } + + /// + /// Applies the "OverlaySrcOver" associated-alpha composition equation. + /// + public readonly struct OverlaySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOver(background, source, amount); + } + + /// + /// Applies the "HardLightSrcOver" associated-alpha composition equation. + /// + public readonly struct HardLightSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcOver" associated-alpha composition equation. + /// + public readonly struct ColorDodgeSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcOver" associated-alpha composition equation. + /// + public readonly struct ColorBurnSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcOver" associated-alpha composition equation. + /// + public readonly struct SoftLightSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOver(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcOver" associated-alpha composition equation. + /// + public readonly struct DifferenceSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOver(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcOver" associated-alpha composition equation. + /// + public readonly struct ExclusionSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOver(background, source, amount); + } + + /// + /// Applies the "HueSrcOver" associated-alpha composition equation. + /// + public readonly struct HueSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOver(background, source, amount); + } + + /// + /// Applies the "SaturationSrcOver" associated-alpha composition equation. + /// + public readonly struct SaturationSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorSrcOver" associated-alpha composition equation. + /// + public readonly struct ColorSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOver(background, source, amount); + } + + /// + /// Applies the "LuminositySrcOver" associated-alpha composition equation. + /// + public readonly struct LuminositySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOver(background, source, amount); + } + + /// + /// Applies the "NormalSrcIn" associated-alpha composition equation. + /// + public readonly struct NormalSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcIn(background, source, amount); + } + + /// + /// Applies the "MultiplySrcIn" associated-alpha composition equation. + /// + public readonly struct MultiplySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcIn(background, source, amount); + } + + /// + /// Applies the "AddSrcIn" associated-alpha composition equation. + /// + public readonly struct AddSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcIn(background, source, amount); + } + + /// + /// Applies the "SubtractSrcIn" associated-alpha composition equation. + /// + public readonly struct SubtractSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcIn(background, source, amount); + } + + /// + /// Applies the "ScreenSrcIn" associated-alpha composition equation. + /// + public readonly struct ScreenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcIn(background, source, amount); + } + + /// + /// Applies the "DarkenSrcIn" associated-alpha composition equation. + /// + public readonly struct DarkenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcIn(background, source, amount); + } + + /// + /// Applies the "LightenSrcIn" associated-alpha composition equation. + /// + public readonly struct LightenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcIn(background, source, amount); + } + + /// + /// Applies the "OverlaySrcIn" associated-alpha composition equation. + /// + public readonly struct OverlaySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcIn(background, source, amount); + } + + /// + /// Applies the "HardLightSrcIn" associated-alpha composition equation. + /// + public readonly struct HardLightSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcIn" associated-alpha composition equation. + /// + public readonly struct ColorDodgeSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcIn" associated-alpha composition equation. + /// + public readonly struct ColorBurnSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcIn" associated-alpha composition equation. + /// + public readonly struct SoftLightSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcIn(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcIn" associated-alpha composition equation. + /// + public readonly struct DifferenceSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcIn(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcIn" associated-alpha composition equation. + /// + public readonly struct ExclusionSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcIn(background, source, amount); + } + + /// + /// Applies the "HueSrcIn" associated-alpha composition equation. + /// + public readonly struct HueSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcIn(background, source, amount); + } + + /// + /// Applies the "SaturationSrcIn" associated-alpha composition equation. + /// + public readonly struct SaturationSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorSrcIn" associated-alpha composition equation. + /// + public readonly struct ColorSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcIn(background, source, amount); + } + + /// + /// Applies the "LuminositySrcIn" associated-alpha composition equation. + /// + public readonly struct LuminositySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcIn(background, source, amount); + } + + /// + /// Applies the "NormalSrcOut" associated-alpha composition equation. + /// + public readonly struct NormalSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalSrcOut(background, source, amount); + } + + /// + /// Applies the "MultiplySrcOut" associated-alpha composition equation. + /// + public readonly struct MultiplySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplySrcOut(background, source, amount); + } + + /// + /// Applies the "AddSrcOut" associated-alpha composition equation. + /// + public readonly struct AddSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddSrcOut(background, source, amount); + } + + /// + /// Applies the "SubtractSrcOut" associated-alpha composition equation. + /// + public readonly struct SubtractSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractSrcOut(background, source, amount); + } + + /// + /// Applies the "ScreenSrcOut" associated-alpha composition equation. + /// + public readonly struct ScreenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenSrcOut(background, source, amount); + } + + /// + /// Applies the "DarkenSrcOut" associated-alpha composition equation. + /// + public readonly struct DarkenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenSrcOut(background, source, amount); + } + + /// + /// Applies the "LightenSrcOut" associated-alpha composition equation. + /// + public readonly struct LightenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenSrcOut(background, source, amount); + } + + /// + /// Applies the "OverlaySrcOut" associated-alpha composition equation. + /// + public readonly struct OverlaySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlaySrcOut(background, source, amount); + } + + /// + /// Applies the "HardLightSrcOut" associated-alpha composition equation. + /// + public readonly struct HardLightSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcOut" associated-alpha composition equation. + /// + public readonly struct ColorDodgeSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcOut" associated-alpha composition equation. + /// + public readonly struct ColorBurnSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcOut" associated-alpha composition equation. + /// + public readonly struct SoftLightSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightSrcOut(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcOut" associated-alpha composition equation. + /// + public readonly struct DifferenceSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceSrcOut(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcOut" associated-alpha composition equation. + /// + public readonly struct ExclusionSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionSrcOut(background, source, amount); + } + + /// + /// Applies the "HueSrcOut" associated-alpha composition equation. + /// + public readonly struct HueSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueSrcOut(background, source, amount); + } + + /// + /// Applies the "SaturationSrcOut" associated-alpha composition equation. + /// + public readonly struct SaturationSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorSrcOut" associated-alpha composition equation. + /// + public readonly struct ColorSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorSrcOut(background, source, amount); + } + + /// + /// Applies the "LuminositySrcOut" associated-alpha composition equation. + /// + public readonly struct LuminositySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminositySrcOut(background, source, amount); + } + + /// + /// Applies the "NormalDest" associated-alpha composition equation. + /// + public readonly struct NormalDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDest(background, source, amount); + } + + /// + /// Applies the "MultiplyDest" associated-alpha composition equation. + /// + public readonly struct MultiplyDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDest(background, source, amount); + } + + /// + /// Applies the "AddDest" associated-alpha composition equation. + /// + public readonly struct AddDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDest(background, source, amount); + } + + /// + /// Applies the "SubtractDest" associated-alpha composition equation. + /// + public readonly struct SubtractDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDest(background, source, amount); + } + + /// + /// Applies the "ScreenDest" associated-alpha composition equation. + /// + public readonly struct ScreenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDest(background, source, amount); + } + + /// + /// Applies the "DarkenDest" associated-alpha composition equation. + /// + public readonly struct DarkenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDest(background, source, amount); + } + + /// + /// Applies the "LightenDest" associated-alpha composition equation. + /// + public readonly struct LightenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDest(background, source, amount); + } + + /// + /// Applies the "OverlayDest" associated-alpha composition equation. + /// + public readonly struct OverlayDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDest(background, source, amount); + } + + /// + /// Applies the "HardLightDest" associated-alpha composition equation. + /// + public readonly struct HardLightDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDest(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDest" associated-alpha composition equation. + /// + public readonly struct ColorDodgeDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDest(background, source, amount); + } + + /// + /// Applies the "ColorBurnDest" associated-alpha composition equation. + /// + public readonly struct ColorBurnDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDest(background, source, amount); + } + + /// + /// Applies the "SoftLightDest" associated-alpha composition equation. + /// + public readonly struct SoftLightDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDest(background, source, amount); + } + + /// + /// Applies the "DifferenceDest" associated-alpha composition equation. + /// + public readonly struct DifferenceDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDest(background, source, amount); + } + + /// + /// Applies the "ExclusionDest" associated-alpha composition equation. + /// + public readonly struct ExclusionDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDest(background, source, amount); + } + + /// + /// Applies the "HueDest" associated-alpha composition equation. + /// + public readonly struct HueDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueDest(background, source, amount); + } + + /// + /// Applies the "SaturationDest" associated-alpha composition equation. + /// + public readonly struct SaturationDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDest(background, source, amount); + } + + /// + /// Applies the "ColorDest" associated-alpha composition equation. + /// + public readonly struct ColorDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDest(background, source, amount); + } + + /// + /// Applies the "LuminosityDest" associated-alpha composition equation. + /// + public readonly struct LuminosityDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDest(background, source, amount); + } + + /// + /// Applies the "NormalDestAtop" associated-alpha composition equation. + /// + public readonly struct NormalDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestAtop(background, source, amount); + } + + /// + /// Applies the "MultiplyDestAtop" associated-alpha composition equation. + /// + public readonly struct MultiplyDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestAtop(background, source, amount); + } + + /// + /// Applies the "AddDestAtop" associated-alpha composition equation. + /// + public readonly struct AddDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestAtop(background, source, amount); + } + + /// + /// Applies the "SubtractDestAtop" associated-alpha composition equation. + /// + public readonly struct SubtractDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestAtop(background, source, amount); + } + + /// + /// Applies the "ScreenDestAtop" associated-alpha composition equation. + /// + public readonly struct ScreenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestAtop(background, source, amount); + } + + /// + /// Applies the "DarkenDestAtop" associated-alpha composition equation. + /// + public readonly struct DarkenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestAtop(background, source, amount); + } + + /// + /// Applies the "LightenDestAtop" associated-alpha composition equation. + /// + public readonly struct LightenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestAtop(background, source, amount); + } + + /// + /// Applies the "OverlayDestAtop" associated-alpha composition equation. + /// + public readonly struct OverlayDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestAtop(background, source, amount); + } + + /// + /// Applies the "HardLightDestAtop" associated-alpha composition equation. + /// + public readonly struct HardLightDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestAtop" associated-alpha composition equation. + /// + public readonly struct ColorDodgeDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestAtop" associated-alpha composition equation. + /// + public readonly struct ColorBurnDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + } + + /// + /// Applies the "SoftLightDestAtop" associated-alpha composition equation. + /// + public readonly struct SoftLightDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestAtop(background, source, amount); + } + + /// + /// Applies the "DifferenceDestAtop" associated-alpha composition equation. + /// + public readonly struct DifferenceDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestAtop(background, source, amount); + } + + /// + /// Applies the "ExclusionDestAtop" associated-alpha composition equation. + /// + public readonly struct ExclusionDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestAtop(background, source, amount); + } + + /// + /// Applies the "HueDestAtop" associated-alpha composition equation. + /// + public readonly struct HueDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestAtop(background, source, amount); + } + + /// + /// Applies the "SaturationDestAtop" associated-alpha composition equation. + /// + public readonly struct SaturationDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorDestAtop" associated-alpha composition equation. + /// + public readonly struct ColorDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestAtop(background, source, amount); + } + + /// + /// Applies the "LuminosityDestAtop" associated-alpha composition equation. + /// + public readonly struct LuminosityDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestAtop(background, source, amount); + } + + /// + /// Applies the "NormalDestOver" associated-alpha composition equation. + /// + public readonly struct NormalDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOver(background, source, amount); + } + + /// + /// Applies the "MultiplyDestOver" associated-alpha composition equation. + /// + public readonly struct MultiplyDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOver(background, source, amount); + } + + /// + /// Applies the "AddDestOver" associated-alpha composition equation. + /// + public readonly struct AddDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOver(background, source, amount); + } + + /// + /// Applies the "SubtractDestOver" associated-alpha composition equation. + /// + public readonly struct SubtractDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOver(background, source, amount); + } + + /// + /// Applies the "ScreenDestOver" associated-alpha composition equation. + /// + public readonly struct ScreenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOver(background, source, amount); + } + + /// + /// Applies the "DarkenDestOver" associated-alpha composition equation. + /// + public readonly struct DarkenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOver(background, source, amount); + } + + /// + /// Applies the "LightenDestOver" associated-alpha composition equation. + /// + public readonly struct LightenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOver(background, source, amount); + } + + /// + /// Applies the "OverlayDestOver" associated-alpha composition equation. + /// + public readonly struct OverlayDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOver(background, source, amount); + } + + /// + /// Applies the "HardLightDestOver" associated-alpha composition equation. + /// + public readonly struct HardLightDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOver(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestOver" associated-alpha composition equation. + /// + public readonly struct ColorDodgeDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestOver" associated-alpha composition equation. + /// + public readonly struct ColorBurnDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOver(background, source, amount); + } + + /// + /// Applies the "SoftLightDestOver" associated-alpha composition equation. + /// + public readonly struct SoftLightDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOver(background, source, amount); + } + + /// + /// Applies the "DifferenceDestOver" associated-alpha composition equation. + /// + public readonly struct DifferenceDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOver(background, source, amount); + } + + /// + /// Applies the "ExclusionDestOver" associated-alpha composition equation. + /// + public readonly struct ExclusionDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOver(background, source, amount); + } + + /// + /// Applies the "HueDestOver" associated-alpha composition equation. + /// + public readonly struct HueDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOver(background, source, amount); + } + + /// + /// Applies the "SaturationDestOver" associated-alpha composition equation. + /// + public readonly struct SaturationDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOver(background, source, amount); + } + + /// + /// Applies the "ColorDestOver" associated-alpha composition equation. + /// + public readonly struct ColorDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOver(background, source, amount); + } + + /// + /// Applies the "LuminosityDestOver" associated-alpha composition equation. + /// + public readonly struct LuminosityDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOver(background, source, amount); + } + + /// + /// Applies the "NormalDestIn" associated-alpha composition equation. + /// + public readonly struct NormalDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestIn(background, source, amount); + } + + /// + /// Applies the "MultiplyDestIn" associated-alpha composition equation. + /// + public readonly struct MultiplyDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestIn(background, source, amount); + } + + /// + /// Applies the "AddDestIn" associated-alpha composition equation. + /// + public readonly struct AddDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestIn(background, source, amount); + } + + /// + /// Applies the "SubtractDestIn" associated-alpha composition equation. + /// + public readonly struct SubtractDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestIn(background, source, amount); + } + + /// + /// Applies the "ScreenDestIn" associated-alpha composition equation. + /// + public readonly struct ScreenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestIn(background, source, amount); + } + + /// + /// Applies the "DarkenDestIn" associated-alpha composition equation. + /// + public readonly struct DarkenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestIn(background, source, amount); + } + + /// + /// Applies the "LightenDestIn" associated-alpha composition equation. + /// + public readonly struct LightenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestIn(background, source, amount); + } + + /// + /// Applies the "OverlayDestIn" associated-alpha composition equation. + /// + public readonly struct OverlayDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestIn(background, source, amount); + } + + /// + /// Applies the "HardLightDestIn" associated-alpha composition equation. + /// + public readonly struct HardLightDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestIn(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestIn" associated-alpha composition equation. + /// + public readonly struct ColorDodgeDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestIn" associated-alpha composition equation. + /// + public readonly struct ColorBurnDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestIn(background, source, amount); + } + + /// + /// Applies the "SoftLightDestIn" associated-alpha composition equation. + /// + public readonly struct SoftLightDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestIn(background, source, amount); + } + + /// + /// Applies the "DifferenceDestIn" associated-alpha composition equation. + /// + public readonly struct DifferenceDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestIn(background, source, amount); + } + + /// + /// Applies the "ExclusionDestIn" associated-alpha composition equation. + /// + public readonly struct ExclusionDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestIn(background, source, amount); + } + + /// + /// Applies the "HueDestIn" associated-alpha composition equation. + /// + public readonly struct HueDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestIn(background, source, amount); + } + + /// + /// Applies the "SaturationDestIn" associated-alpha composition equation. + /// + public readonly struct SaturationDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestIn(background, source, amount); + } + + /// + /// Applies the "ColorDestIn" associated-alpha composition equation. + /// + public readonly struct ColorDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestIn(background, source, amount); + } + + /// + /// Applies the "LuminosityDestIn" associated-alpha composition equation. + /// + public readonly struct LuminosityDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestIn(background, source, amount); + } + + /// + /// Applies the "NormalDestOut" associated-alpha composition equation. + /// + public readonly struct NormalDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalDestOut(background, source, amount); + } + + /// + /// Applies the "MultiplyDestOut" associated-alpha composition equation. + /// + public readonly struct MultiplyDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyDestOut(background, source, amount); + } + + /// + /// Applies the "AddDestOut" associated-alpha composition equation. + /// + public readonly struct AddDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddDestOut(background, source, amount); + } + + /// + /// Applies the "SubtractDestOut" associated-alpha composition equation. + /// + public readonly struct SubtractDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractDestOut(background, source, amount); + } + + /// + /// Applies the "ScreenDestOut" associated-alpha composition equation. + /// + public readonly struct ScreenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenDestOut(background, source, amount); + } + + /// + /// Applies the "DarkenDestOut" associated-alpha composition equation. + /// + public readonly struct DarkenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenDestOut(background, source, amount); + } + + /// + /// Applies the "LightenDestOut" associated-alpha composition equation. + /// + public readonly struct LightenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenDestOut(background, source, amount); + } + + /// + /// Applies the "OverlayDestOut" associated-alpha composition equation. + /// + public readonly struct OverlayDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayDestOut(background, source, amount); + } + + /// + /// Applies the "HardLightDestOut" associated-alpha composition equation. + /// + public readonly struct HardLightDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightDestOut(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestOut" associated-alpha composition equation. + /// + public readonly struct ColorDodgeDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestOut" associated-alpha composition equation. + /// + public readonly struct ColorBurnDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnDestOut(background, source, amount); + } + + /// + /// Applies the "SoftLightDestOut" associated-alpha composition equation. + /// + public readonly struct SoftLightDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightDestOut(background, source, amount); + } + + /// + /// Applies the "DifferenceDestOut" associated-alpha composition equation. + /// + public readonly struct DifferenceDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceDestOut(background, source, amount); + } + + /// + /// Applies the "ExclusionDestOut" associated-alpha composition equation. + /// + public readonly struct ExclusionDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionDestOut(background, source, amount); + } + + /// + /// Applies the "HueDestOut" associated-alpha composition equation. + /// + public readonly struct HueDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueDestOut(background, source, amount); + } + + /// + /// Applies the "SaturationDestOut" associated-alpha composition equation. + /// + public readonly struct SaturationDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationDestOut(background, source, amount); + } + + /// + /// Applies the "ColorDestOut" associated-alpha composition equation. + /// + public readonly struct ColorDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDestOut(background, source, amount); + } + + /// + /// Applies the "LuminosityDestOut" associated-alpha composition equation. + /// + public readonly struct LuminosityDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityDestOut(background, source, amount); + } + + /// + /// Applies the "NormalClear" associated-alpha composition equation. + /// + public readonly struct NormalClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalClear(background, source, amount); + } + + /// + /// Applies the "MultiplyClear" associated-alpha composition equation. + /// + public readonly struct MultiplyClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyClear(background, source, amount); + } + + /// + /// Applies the "AddClear" associated-alpha composition equation. + /// + public readonly struct AddClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddClear(background, source, amount); + } + + /// + /// Applies the "SubtractClear" associated-alpha composition equation. + /// + public readonly struct SubtractClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractClear(background, source, amount); + } + + /// + /// Applies the "ScreenClear" associated-alpha composition equation. + /// + public readonly struct ScreenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenClear(background, source, amount); + } + + /// + /// Applies the "DarkenClear" associated-alpha composition equation. + /// + public readonly struct DarkenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenClear(background, source, amount); + } + + /// + /// Applies the "LightenClear" associated-alpha composition equation. + /// + public readonly struct LightenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenClear(background, source, amount); + } + + /// + /// Applies the "OverlayClear" associated-alpha composition equation. + /// + public readonly struct OverlayClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayClear(background, source, amount); + } + + /// + /// Applies the "HardLightClear" associated-alpha composition equation. + /// + public readonly struct HardLightClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightClear(background, source, amount); + } + + /// + /// Applies the "ColorDodgeClear" associated-alpha composition equation. + /// + public readonly struct ColorDodgeClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeClear(background, source, amount); + } + + /// + /// Applies the "ColorBurnClear" associated-alpha composition equation. + /// + public readonly struct ColorBurnClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnClear(background, source, amount); + } + + /// + /// Applies the "SoftLightClear" associated-alpha composition equation. + /// + public readonly struct SoftLightClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightClear(background, source, amount); + } + + /// + /// Applies the "DifferenceClear" associated-alpha composition equation. + /// + public readonly struct DifferenceClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceClear(background, source, amount); + } + + /// + /// Applies the "ExclusionClear" associated-alpha composition equation. + /// + public readonly struct ExclusionClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionClear(background, source, amount); + } + + /// + /// Applies the "HueClear" associated-alpha composition equation. + /// + public readonly struct HueClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueClear(background, source, amount); + } + + /// + /// Applies the "SaturationClear" associated-alpha composition equation. + /// + public readonly struct SaturationClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationClear(background, source, amount); + } + + /// + /// Applies the "ColorClear" associated-alpha composition equation. + /// + public readonly struct ColorClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorClear(background, source, amount); + } + + /// + /// Applies the "LuminosityClear" associated-alpha composition equation. + /// + public readonly struct LuminosityClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityClear(background, source, amount); + } + + /// + /// Applies the "NormalXor" associated-alpha composition equation. + /// + public readonly struct NormalXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalXor(background, source, amount); + } + + /// + /// Applies the "MultiplyXor" associated-alpha composition equation. + /// + public readonly struct MultiplyXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyXor(background, source, amount); + } + + /// + /// Applies the "AddXor" associated-alpha composition equation. + /// + public readonly struct AddXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddXor(background, source, amount); + } + + /// + /// Applies the "SubtractXor" associated-alpha composition equation. + /// + public readonly struct SubtractXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractXor(background, source, amount); + } + + /// + /// Applies the "ScreenXor" associated-alpha composition equation. + /// + public readonly struct ScreenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenXor(background, source, amount); + } + + /// + /// Applies the "DarkenXor" associated-alpha composition equation. + /// + public readonly struct DarkenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenXor(background, source, amount); + } + + /// + /// Applies the "LightenXor" associated-alpha composition equation. + /// + public readonly struct LightenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenXor(background, source, amount); + } + + /// + /// Applies the "OverlayXor" associated-alpha composition equation. + /// + public readonly struct OverlayXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayXor(background, source, amount); + } + + /// + /// Applies the "HardLightXor" associated-alpha composition equation. + /// + public readonly struct HardLightXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightXor(background, source, amount); + } + + /// + /// Applies the "ColorDodgeXor" associated-alpha composition equation. + /// + public readonly struct ColorDodgeXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgeXor(background, source, amount); + } + + /// + /// Applies the "ColorBurnXor" associated-alpha composition equation. + /// + public readonly struct ColorBurnXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnXor(background, source, amount); + } + + /// + /// Applies the "SoftLightXor" associated-alpha composition equation. + /// + public readonly struct SoftLightXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightXor(background, source, amount); + } + + /// + /// Applies the "DifferenceXor" associated-alpha composition equation. + /// + public readonly struct DifferenceXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferenceXor(background, source, amount); + } + + /// + /// Applies the "ExclusionXor" associated-alpha composition equation. + /// + public readonly struct ExclusionXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionXor(background, source, amount); + } + + /// + /// Applies the "HueXor" associated-alpha composition equation. + /// + public readonly struct HueXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HueXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HueXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HueXor(background, source, amount); + } + + /// + /// Applies the "SaturationXor" associated-alpha composition equation. + /// + public readonly struct SaturationXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationXor(background, source, amount); + } + + /// + /// Applies the "ColorXor" associated-alpha composition equation. + /// + public readonly struct ColorXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorXor(background, source, amount); + } + + /// + /// Applies the "LuminosityXor" associated-alpha composition equation. + /// + public readonly struct LuminosityXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityXor(background, source, amount); + } + + /// + /// Applies the "NormalPlus" associated-alpha composition equation. + /// + public readonly struct NormalPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.NormalPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.NormalPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.NormalPlus(background, source, amount); + } + + /// + /// Applies the "MultiplyPlus" associated-alpha composition equation. + /// + public readonly struct MultiplyPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.MultiplyPlus(background, source, amount); + } + + /// + /// Applies the "AddPlus" associated-alpha composition equation. + /// + public readonly struct AddPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.AddPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.AddPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.AddPlus(background, source, amount); + } + + /// + /// Applies the "SubtractPlus" associated-alpha composition equation. + /// + public readonly struct SubtractPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SubtractPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SubtractPlus(background, source, amount); + } + + /// + /// Applies the "ScreenPlus" associated-alpha composition equation. + /// + public readonly struct ScreenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ScreenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ScreenPlus(background, source, amount); + } + + /// + /// Applies the "DarkenPlus" associated-alpha composition equation. + /// + public readonly struct DarkenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DarkenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DarkenPlus(background, source, amount); + } + + /// + /// Applies the "LightenPlus" associated-alpha composition equation. + /// + public readonly struct LightenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LightenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LightenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LightenPlus(background, source, amount); + } + + /// + /// Applies the "OverlayPlus" associated-alpha composition equation. + /// + public readonly struct OverlayPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.OverlayPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.OverlayPlus(background, source, amount); + } + + /// + /// Applies the "HardLightPlus" associated-alpha composition equation. + /// + public readonly struct HardLightPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HardLightPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HardLightPlus(background, source, amount); + } + + /// + /// Applies the "ColorDodgePlus" associated-alpha composition equation. + /// + public readonly struct ColorDodgePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorDodgePlus(background, source, amount); + } + + /// + /// Applies the "ColorBurnPlus" associated-alpha composition equation. + /// + public readonly struct ColorBurnPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorBurnPlus(background, source, amount); + } + + /// + /// Applies the "SoftLightPlus" associated-alpha composition equation. + /// + public readonly struct SoftLightPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SoftLightPlus(background, source, amount); + } + + /// + /// Applies the "DifferencePlus" associated-alpha composition equation. + /// + public readonly struct DifferencePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.DifferencePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.DifferencePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.DifferencePlus(background, source, amount); + } + + /// + /// Applies the "ExclusionPlus" associated-alpha composition equation. + /// + public readonly struct ExclusionPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ExclusionPlus(background, source, amount); + } + + /// + /// Applies the "HuePlus" associated-alpha composition equation. + /// + public readonly struct HuePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.HuePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.HuePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.HuePlus(background, source, amount); + } + + /// + /// Applies the "SaturationPlus" associated-alpha composition equation. + /// + public readonly struct SaturationPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.SaturationPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.SaturationPlus(background, source, amount); + } + + /// + /// Applies the "ColorPlus" associated-alpha composition equation. + /// + public readonly struct ColorPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.ColorPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.ColorPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.ColorPlus(background, source, amount); + } + + /// + /// Applies the "LuminosityPlus" associated-alpha composition equation. + /// + public readonly struct LuminosityPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.LuminosityPlus(background, source, amount); + } + +} + +/// +/// Provides associated-alpha pixel blenders for each color and alpha composition pair. +/// +/// The associated-alpha destination pixel format. +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the shared blender for the "NormalSrc" composition equation. + /// + public static PixelBlender NormalSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrc" composition equation. + /// + public static PixelBlender MultiplySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrc" composition equation. + /// + public static PixelBlender AddSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrc" composition equation. + /// + public static PixelBlender SubtractSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrc" composition equation. + /// + public static PixelBlender ScreenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrc" composition equation. + /// + public static PixelBlender DarkenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrc" composition equation. + /// + public static PixelBlender LightenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrc" composition equation. + /// + public static PixelBlender OverlaySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrc" composition equation. + /// + public static PixelBlender HardLightSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrc" composition equation. + /// + public static PixelBlender ColorDodgeSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrc" composition equation. + /// + public static PixelBlender ColorBurnSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrc" composition equation. + /// + public static PixelBlender SoftLightSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrc" composition equation. + /// + public static PixelBlender DifferenceSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrc" composition equation. + /// + public static PixelBlender ExclusionSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrc" composition equation. + /// + public static PixelBlender HueSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrc" composition equation. + /// + public static PixelBlender SaturationSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrc" composition equation. + /// + public static PixelBlender ColorSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrc" composition equation. + /// + public static PixelBlender LuminositySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcAtop" composition equation. + /// + public static PixelBlender NormalSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcAtop" composition equation. + /// + public static PixelBlender MultiplySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcAtop" composition equation. + /// + public static PixelBlender AddSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcAtop" composition equation. + /// + public static PixelBlender SubtractSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcAtop" composition equation. + /// + public static PixelBlender ScreenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcAtop" composition equation. + /// + public static PixelBlender DarkenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcAtop" composition equation. + /// + public static PixelBlender LightenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcAtop" composition equation. + /// + public static PixelBlender OverlaySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcAtop" composition equation. + /// + public static PixelBlender HardLightSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcAtop" composition equation. + /// + public static PixelBlender ColorDodgeSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcAtop" composition equation. + /// + public static PixelBlender ColorBurnSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcAtop" composition equation. + /// + public static PixelBlender SoftLightSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcAtop" composition equation. + /// + public static PixelBlender DifferenceSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcAtop" composition equation. + /// + public static PixelBlender ExclusionSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcAtop" composition equation. + /// + public static PixelBlender HueSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcAtop" composition equation. + /// + public static PixelBlender SaturationSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcAtop" composition equation. + /// + public static PixelBlender ColorSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcAtop" composition equation. + /// + public static PixelBlender LuminositySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcOver" composition equation. + /// + public static PixelBlender NormalSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcOver" composition equation. + /// + public static PixelBlender MultiplySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcOver" composition equation. + /// + public static PixelBlender AddSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcOver" composition equation. + /// + public static PixelBlender SubtractSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcOver" composition equation. + /// + public static PixelBlender ScreenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcOver" composition equation. + /// + public static PixelBlender DarkenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcOver" composition equation. + /// + public static PixelBlender LightenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcOver" composition equation. + /// + public static PixelBlender OverlaySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcOver" composition equation. + /// + public static PixelBlender HardLightSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcOver" composition equation. + /// + public static PixelBlender ColorDodgeSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcOver" composition equation. + /// + public static PixelBlender ColorBurnSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcOver" composition equation. + /// + public static PixelBlender SoftLightSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcOver" composition equation. + /// + public static PixelBlender DifferenceSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcOver" composition equation. + /// + public static PixelBlender ExclusionSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcOver" composition equation. + /// + public static PixelBlender HueSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcOver" composition equation. + /// + public static PixelBlender SaturationSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcOver" composition equation. + /// + public static PixelBlender ColorSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcOver" composition equation. + /// + public static PixelBlender LuminositySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcIn" composition equation. + /// + public static PixelBlender NormalSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcIn" composition equation. + /// + public static PixelBlender MultiplySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcIn" composition equation. + /// + public static PixelBlender AddSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcIn" composition equation. + /// + public static PixelBlender SubtractSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcIn" composition equation. + /// + public static PixelBlender ScreenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcIn" composition equation. + /// + public static PixelBlender DarkenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcIn" composition equation. + /// + public static PixelBlender LightenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcIn" composition equation. + /// + public static PixelBlender OverlaySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcIn" composition equation. + /// + public static PixelBlender HardLightSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcIn" composition equation. + /// + public static PixelBlender ColorDodgeSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcIn" composition equation. + /// + public static PixelBlender ColorBurnSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcIn" composition equation. + /// + public static PixelBlender SoftLightSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcIn" composition equation. + /// + public static PixelBlender DifferenceSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcIn" composition equation. + /// + public static PixelBlender ExclusionSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcIn" composition equation. + /// + public static PixelBlender HueSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcIn" composition equation. + /// + public static PixelBlender SaturationSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcIn" composition equation. + /// + public static PixelBlender ColorSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcIn" composition equation. + /// + public static PixelBlender LuminositySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcOut" composition equation. + /// + public static PixelBlender NormalSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcOut" composition equation. + /// + public static PixelBlender MultiplySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcOut" composition equation. + /// + public static PixelBlender AddSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcOut" composition equation. + /// + public static PixelBlender SubtractSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcOut" composition equation. + /// + public static PixelBlender ScreenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcOut" composition equation. + /// + public static PixelBlender DarkenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcOut" composition equation. + /// + public static PixelBlender LightenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcOut" composition equation. + /// + public static PixelBlender OverlaySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcOut" composition equation. + /// + public static PixelBlender HardLightSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcOut" composition equation. + /// + public static PixelBlender ColorDodgeSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcOut" composition equation. + /// + public static PixelBlender ColorBurnSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcOut" composition equation. + /// + public static PixelBlender SoftLightSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcOut" composition equation. + /// + public static PixelBlender DifferenceSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcOut" composition equation. + /// + public static PixelBlender ExclusionSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcOut" composition equation. + /// + public static PixelBlender HueSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcOut" composition equation. + /// + public static PixelBlender SaturationSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcOut" composition equation. + /// + public static PixelBlender ColorSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcOut" composition equation. + /// + public static PixelBlender LuminositySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDest" composition equation. + /// + public static PixelBlender NormalDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDest" composition equation. + /// + public static PixelBlender MultiplyDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDest" composition equation. + /// + public static PixelBlender AddDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDest" composition equation. + /// + public static PixelBlender SubtractDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDest" composition equation. + /// + public static PixelBlender ScreenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDest" composition equation. + /// + public static PixelBlender DarkenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDest" composition equation. + /// + public static PixelBlender LightenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDest" composition equation. + /// + public static PixelBlender OverlayDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDest" composition equation. + /// + public static PixelBlender HardLightDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDest" composition equation. + /// + public static PixelBlender ColorDodgeDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDest" composition equation. + /// + public static PixelBlender ColorBurnDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDest" composition equation. + /// + public static PixelBlender SoftLightDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDest" composition equation. + /// + public static PixelBlender DifferenceDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDest" composition equation. + /// + public static PixelBlender ExclusionDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDest" composition equation. + /// + public static PixelBlender HueDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDest" composition equation. + /// + public static PixelBlender SaturationDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDest" composition equation. + /// + public static PixelBlender ColorDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDest" composition equation. + /// + public static PixelBlender LuminosityDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestAtop" composition equation. + /// + public static PixelBlender NormalDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestAtop" composition equation. + /// + public static PixelBlender MultiplyDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestAtop" composition equation. + /// + public static PixelBlender AddDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestAtop" composition equation. + /// + public static PixelBlender SubtractDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestAtop" composition equation. + /// + public static PixelBlender ScreenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestAtop" composition equation. + /// + public static PixelBlender DarkenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestAtop" composition equation. + /// + public static PixelBlender LightenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestAtop" composition equation. + /// + public static PixelBlender OverlayDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestAtop" composition equation. + /// + public static PixelBlender HardLightDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestAtop" composition equation. + /// + public static PixelBlender ColorDodgeDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestAtop" composition equation. + /// + public static PixelBlender ColorBurnDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestAtop" composition equation. + /// + public static PixelBlender SoftLightDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestAtop" composition equation. + /// + public static PixelBlender DifferenceDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestAtop" composition equation. + /// + public static PixelBlender ExclusionDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestAtop" composition equation. + /// + public static PixelBlender HueDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestAtop" composition equation. + /// + public static PixelBlender SaturationDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestAtop" composition equation. + /// + public static PixelBlender ColorDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestAtop" composition equation. + /// + public static PixelBlender LuminosityDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestOver" composition equation. + /// + public static PixelBlender NormalDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestOver" composition equation. + /// + public static PixelBlender MultiplyDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestOver" composition equation. + /// + public static PixelBlender AddDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestOver" composition equation. + /// + public static PixelBlender SubtractDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestOver" composition equation. + /// + public static PixelBlender ScreenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestOver" composition equation. + /// + public static PixelBlender DarkenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestOver" composition equation. + /// + public static PixelBlender LightenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestOver" composition equation. + /// + public static PixelBlender OverlayDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestOver" composition equation. + /// + public static PixelBlender HardLightDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestOver" composition equation. + /// + public static PixelBlender ColorDodgeDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestOver" composition equation. + /// + public static PixelBlender ColorBurnDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestOver" composition equation. + /// + public static PixelBlender SoftLightDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestOver" composition equation. + /// + public static PixelBlender DifferenceDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestOver" composition equation. + /// + public static PixelBlender ExclusionDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestOver" composition equation. + /// + public static PixelBlender HueDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestOver" composition equation. + /// + public static PixelBlender SaturationDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestOver" composition equation. + /// + public static PixelBlender ColorDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestOver" composition equation. + /// + public static PixelBlender LuminosityDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestIn" composition equation. + /// + public static PixelBlender NormalDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestIn" composition equation. + /// + public static PixelBlender MultiplyDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestIn" composition equation. + /// + public static PixelBlender AddDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestIn" composition equation. + /// + public static PixelBlender SubtractDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestIn" composition equation. + /// + public static PixelBlender ScreenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestIn" composition equation. + /// + public static PixelBlender DarkenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestIn" composition equation. + /// + public static PixelBlender LightenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestIn" composition equation. + /// + public static PixelBlender OverlayDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestIn" composition equation. + /// + public static PixelBlender HardLightDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestIn" composition equation. + /// + public static PixelBlender ColorDodgeDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestIn" composition equation. + /// + public static PixelBlender ColorBurnDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestIn" composition equation. + /// + public static PixelBlender SoftLightDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestIn" composition equation. + /// + public static PixelBlender DifferenceDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestIn" composition equation. + /// + public static PixelBlender ExclusionDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestIn" composition equation. + /// + public static PixelBlender HueDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestIn" composition equation. + /// + public static PixelBlender SaturationDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestIn" composition equation. + /// + public static PixelBlender ColorDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestIn" composition equation. + /// + public static PixelBlender LuminosityDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestOut" composition equation. + /// + public static PixelBlender NormalDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestOut" composition equation. + /// + public static PixelBlender MultiplyDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestOut" composition equation. + /// + public static PixelBlender AddDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestOut" composition equation. + /// + public static PixelBlender SubtractDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestOut" composition equation. + /// + public static PixelBlender ScreenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestOut" composition equation. + /// + public static PixelBlender DarkenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestOut" composition equation. + /// + public static PixelBlender LightenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestOut" composition equation. + /// + public static PixelBlender OverlayDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestOut" composition equation. + /// + public static PixelBlender HardLightDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestOut" composition equation. + /// + public static PixelBlender ColorDodgeDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestOut" composition equation. + /// + public static PixelBlender ColorBurnDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestOut" composition equation. + /// + public static PixelBlender SoftLightDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestOut" composition equation. + /// + public static PixelBlender DifferenceDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestOut" composition equation. + /// + public static PixelBlender ExclusionDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestOut" composition equation. + /// + public static PixelBlender HueDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestOut" composition equation. + /// + public static PixelBlender SaturationDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestOut" composition equation. + /// + public static PixelBlender ColorDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestOut" composition equation. + /// + public static PixelBlender LuminosityDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalClear" composition equation. + /// + public static PixelBlender NormalClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyClear" composition equation. + /// + public static PixelBlender MultiplyClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddClear" composition equation. + /// + public static PixelBlender AddClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractClear" composition equation. + /// + public static PixelBlender SubtractClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenClear" composition equation. + /// + public static PixelBlender ScreenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenClear" composition equation. + /// + public static PixelBlender DarkenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenClear" composition equation. + /// + public static PixelBlender LightenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayClear" composition equation. + /// + public static PixelBlender OverlayClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightClear" composition equation. + /// + public static PixelBlender HardLightClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeClear" composition equation. + /// + public static PixelBlender ColorDodgeClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnClear" composition equation. + /// + public static PixelBlender ColorBurnClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightClear" composition equation. + /// + public static PixelBlender SoftLightClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceClear" composition equation. + /// + public static PixelBlender DifferenceClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionClear" composition equation. + /// + public static PixelBlender ExclusionClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueClear" composition equation. + /// + public static PixelBlender HueClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationClear" composition equation. + /// + public static PixelBlender SaturationClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorClear" composition equation. + /// + public static PixelBlender ColorClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityClear" composition equation. + /// + public static PixelBlender LuminosityClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalXor" composition equation. + /// + public static PixelBlender NormalXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyXor" composition equation. + /// + public static PixelBlender MultiplyXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddXor" composition equation. + /// + public static PixelBlender AddXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractXor" composition equation. + /// + public static PixelBlender SubtractXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenXor" composition equation. + /// + public static PixelBlender ScreenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenXor" composition equation. + /// + public static PixelBlender DarkenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenXor" composition equation. + /// + public static PixelBlender LightenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayXor" composition equation. + /// + public static PixelBlender OverlayXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightXor" composition equation. + /// + public static PixelBlender HardLightXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeXor" composition equation. + /// + public static PixelBlender ColorDodgeXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnXor" composition equation. + /// + public static PixelBlender ColorBurnXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightXor" composition equation. + /// + public static PixelBlender SoftLightXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceXor" composition equation. + /// + public static PixelBlender DifferenceXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionXor" composition equation. + /// + public static PixelBlender ExclusionXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueXor" composition equation. + /// + public static PixelBlender HueXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationXor" composition equation. + /// + public static PixelBlender SaturationXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorXor" composition equation. + /// + public static PixelBlender ColorXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityXor" composition equation. + /// + public static PixelBlender LuminosityXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalPlus" composition equation. + /// + public static PixelBlender NormalPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyPlus" composition equation. + /// + public static PixelBlender MultiplyPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddPlus" composition equation. + /// + public static PixelBlender AddPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractPlus" composition equation. + /// + public static PixelBlender SubtractPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenPlus" composition equation. + /// + public static PixelBlender ScreenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenPlus" composition equation. + /// + public static PixelBlender DarkenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenPlus" composition equation. + /// + public static PixelBlender LightenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayPlus" composition equation. + /// + public static PixelBlender OverlayPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightPlus" composition equation. + /// + public static PixelBlender HardLightPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgePlus" composition equation. + /// + public static PixelBlender ColorDodgePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnPlus" composition equation. + /// + public static PixelBlender ColorBurnPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightPlus" composition equation. + /// + public static PixelBlender SoftLightPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferencePlus" composition equation. + /// + public static PixelBlender DifferencePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionPlus" composition equation. + /// + public static PixelBlender ExclusionPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HuePlus" composition equation. + /// + public static PixelBlender HuePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationPlus" composition equation. + /// + public static PixelBlender SaturationPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorPlus" composition equation. + /// + public static PixelBlender ColorPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityPlus" composition equation. + /// + public static PixelBlender LuminosityPlus => PixelBlender.Instance; + +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt new file mode 100644 index 0000000000..127f45df34 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt @@ -0,0 +1,119 @@ +<# +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +<# +var composers = new [] +{ + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", + "Plus", +}; + +var blenders = new [] +{ + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight", + "ColorDodge", + "ColorBurn", + "SoftLight", + "Difference", + "Exclusion", + "Hue", + "Saturation", + "Color", + "Luminosity", +}; +#> +/// +/// Provides the associated-alpha equations consumed by the shared pixel-blending traversal. +/// +internal static class AssociatedAlphaPixelBlenderOperators +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; +#> + /// + /// Applies the "<#= blenderComposer #>" associated-alpha composition equation. + /// + public readonly struct <#= blenderComposer #> : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => true; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => AssociatedAlphaPorterDuffFunctions.<#= blenderComposer #>(background, source, amount); + } + +<# + } +} +#> +} + +/// +/// Provides associated-alpha pixel blenders for each color and alpha composition pair. +/// +/// The associated-alpha destination pixel format. +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; +#> + /// + /// Gets the shared blender for the "<#= blenderComposer #>" composition equation. + /// + public static PixelBlender <#= blenderComposer #> => PixelBlender>.Instance; + +<# + } +} +#> +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs new file mode 100644 index 0000000000..8650cc9f98 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs @@ -0,0 +1,297 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Provides pixel blenders for formats that store associated alpha. +/// +internal static partial class AssociatedAlphaPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the blender for the requested color blending and alpha composition modes. + /// + /// The color blending mode. + /// The alpha composition mode. + /// The pixel blender. + public static PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + { + return alphaMode switch + { + PixelAlphaCompositionMode.Src => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrc, + PixelColorBlendingMode.Add => AddSrc, + PixelColorBlendingMode.Subtract => SubtractSrc, + PixelColorBlendingMode.Screen => ScreenSrc, + PixelColorBlendingMode.Darken => DarkenSrc, + PixelColorBlendingMode.Lighten => LightenSrc, + PixelColorBlendingMode.Overlay => OverlaySrc, + PixelColorBlendingMode.HardLight => HardLightSrc, + PixelColorBlendingMode.ColorDodge => ColorDodgeSrc, + PixelColorBlendingMode.ColorBurn => ColorBurnSrc, + PixelColorBlendingMode.SoftLight => SoftLightSrc, + PixelColorBlendingMode.Difference => DifferenceSrc, + PixelColorBlendingMode.Exclusion => ExclusionSrc, + PixelColorBlendingMode.Hue => HueSrc, + PixelColorBlendingMode.Saturation => SaturationSrc, + PixelColorBlendingMode.Color => ColorSrc, + PixelColorBlendingMode.Luminosity => LuminositySrc, + _ => NormalSrc, + }, + PixelAlphaCompositionMode.SrcAtop => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcAtop, + PixelColorBlendingMode.Add => AddSrcAtop, + PixelColorBlendingMode.Subtract => SubtractSrcAtop, + PixelColorBlendingMode.Screen => ScreenSrcAtop, + PixelColorBlendingMode.Darken => DarkenSrcAtop, + PixelColorBlendingMode.Lighten => LightenSrcAtop, + PixelColorBlendingMode.Overlay => OverlaySrcAtop, + PixelColorBlendingMode.HardLight => HardLightSrcAtop, + PixelColorBlendingMode.ColorDodge => ColorDodgeSrcAtop, + PixelColorBlendingMode.ColorBurn => ColorBurnSrcAtop, + PixelColorBlendingMode.SoftLight => SoftLightSrcAtop, + PixelColorBlendingMode.Difference => DifferenceSrcAtop, + PixelColorBlendingMode.Exclusion => ExclusionSrcAtop, + PixelColorBlendingMode.Hue => HueSrcAtop, + PixelColorBlendingMode.Saturation => SaturationSrcAtop, + PixelColorBlendingMode.Color => ColorSrcAtop, + PixelColorBlendingMode.Luminosity => LuminositySrcAtop, + _ => NormalSrcAtop, + }, + PixelAlphaCompositionMode.SrcIn => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcIn, + PixelColorBlendingMode.Add => AddSrcIn, + PixelColorBlendingMode.Subtract => SubtractSrcIn, + PixelColorBlendingMode.Screen => ScreenSrcIn, + PixelColorBlendingMode.Darken => DarkenSrcIn, + PixelColorBlendingMode.Lighten => LightenSrcIn, + PixelColorBlendingMode.Overlay => OverlaySrcIn, + PixelColorBlendingMode.HardLight => HardLightSrcIn, + PixelColorBlendingMode.ColorDodge => ColorDodgeSrcIn, + PixelColorBlendingMode.ColorBurn => ColorBurnSrcIn, + PixelColorBlendingMode.SoftLight => SoftLightSrcIn, + PixelColorBlendingMode.Difference => DifferenceSrcIn, + PixelColorBlendingMode.Exclusion => ExclusionSrcIn, + PixelColorBlendingMode.Hue => HueSrcIn, + PixelColorBlendingMode.Saturation => SaturationSrcIn, + PixelColorBlendingMode.Color => ColorSrcIn, + PixelColorBlendingMode.Luminosity => LuminositySrcIn, + _ => NormalSrcIn, + }, + PixelAlphaCompositionMode.SrcOut => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcOut, + PixelColorBlendingMode.Add => AddSrcOut, + PixelColorBlendingMode.Subtract => SubtractSrcOut, + PixelColorBlendingMode.Screen => ScreenSrcOut, + PixelColorBlendingMode.Darken => DarkenSrcOut, + PixelColorBlendingMode.Lighten => LightenSrcOut, + PixelColorBlendingMode.Overlay => OverlaySrcOut, + PixelColorBlendingMode.HardLight => HardLightSrcOut, + PixelColorBlendingMode.ColorDodge => ColorDodgeSrcOut, + PixelColorBlendingMode.ColorBurn => ColorBurnSrcOut, + PixelColorBlendingMode.SoftLight => SoftLightSrcOut, + PixelColorBlendingMode.Difference => DifferenceSrcOut, + PixelColorBlendingMode.Exclusion => ExclusionSrcOut, + PixelColorBlendingMode.Hue => HueSrcOut, + PixelColorBlendingMode.Saturation => SaturationSrcOut, + PixelColorBlendingMode.Color => ColorSrcOut, + PixelColorBlendingMode.Luminosity => LuminositySrcOut, + _ => NormalSrcOut, + }, + PixelAlphaCompositionMode.Dest => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDest, + PixelColorBlendingMode.Add => AddDest, + PixelColorBlendingMode.Subtract => SubtractDest, + PixelColorBlendingMode.Screen => ScreenDest, + PixelColorBlendingMode.Darken => DarkenDest, + PixelColorBlendingMode.Lighten => LightenDest, + PixelColorBlendingMode.Overlay => OverlayDest, + PixelColorBlendingMode.HardLight => HardLightDest, + PixelColorBlendingMode.ColorDodge => ColorDodgeDest, + PixelColorBlendingMode.ColorBurn => ColorBurnDest, + PixelColorBlendingMode.SoftLight => SoftLightDest, + PixelColorBlendingMode.Difference => DifferenceDest, + PixelColorBlendingMode.Exclusion => ExclusionDest, + PixelColorBlendingMode.Hue => HueDest, + PixelColorBlendingMode.Saturation => SaturationDest, + PixelColorBlendingMode.Color => ColorDest, + PixelColorBlendingMode.Luminosity => LuminosityDest, + _ => NormalDest, + }, + PixelAlphaCompositionMode.DestAtop => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestAtop, + PixelColorBlendingMode.Add => AddDestAtop, + PixelColorBlendingMode.Subtract => SubtractDestAtop, + PixelColorBlendingMode.Screen => ScreenDestAtop, + PixelColorBlendingMode.Darken => DarkenDestAtop, + PixelColorBlendingMode.Lighten => LightenDestAtop, + PixelColorBlendingMode.Overlay => OverlayDestAtop, + PixelColorBlendingMode.HardLight => HardLightDestAtop, + PixelColorBlendingMode.ColorDodge => ColorDodgeDestAtop, + PixelColorBlendingMode.ColorBurn => ColorBurnDestAtop, + PixelColorBlendingMode.SoftLight => SoftLightDestAtop, + PixelColorBlendingMode.Difference => DifferenceDestAtop, + PixelColorBlendingMode.Exclusion => ExclusionDestAtop, + PixelColorBlendingMode.Hue => HueDestAtop, + PixelColorBlendingMode.Saturation => SaturationDestAtop, + PixelColorBlendingMode.Color => ColorDestAtop, + PixelColorBlendingMode.Luminosity => LuminosityDestAtop, + _ => NormalDestAtop, + }, + PixelAlphaCompositionMode.DestOver => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestOver, + PixelColorBlendingMode.Add => AddDestOver, + PixelColorBlendingMode.Subtract => SubtractDestOver, + PixelColorBlendingMode.Screen => ScreenDestOver, + PixelColorBlendingMode.Darken => DarkenDestOver, + PixelColorBlendingMode.Lighten => LightenDestOver, + PixelColorBlendingMode.Overlay => OverlayDestOver, + PixelColorBlendingMode.HardLight => HardLightDestOver, + PixelColorBlendingMode.ColorDodge => ColorDodgeDestOver, + PixelColorBlendingMode.ColorBurn => ColorBurnDestOver, + PixelColorBlendingMode.SoftLight => SoftLightDestOver, + PixelColorBlendingMode.Difference => DifferenceDestOver, + PixelColorBlendingMode.Exclusion => ExclusionDestOver, + PixelColorBlendingMode.Hue => HueDestOver, + PixelColorBlendingMode.Saturation => SaturationDestOver, + PixelColorBlendingMode.Color => ColorDestOver, + PixelColorBlendingMode.Luminosity => LuminosityDestOver, + _ => NormalDestOver, + }, + PixelAlphaCompositionMode.DestIn => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestIn, + PixelColorBlendingMode.Add => AddDestIn, + PixelColorBlendingMode.Subtract => SubtractDestIn, + PixelColorBlendingMode.Screen => ScreenDestIn, + PixelColorBlendingMode.Darken => DarkenDestIn, + PixelColorBlendingMode.Lighten => LightenDestIn, + PixelColorBlendingMode.Overlay => OverlayDestIn, + PixelColorBlendingMode.HardLight => HardLightDestIn, + PixelColorBlendingMode.ColorDodge => ColorDodgeDestIn, + PixelColorBlendingMode.ColorBurn => ColorBurnDestIn, + PixelColorBlendingMode.SoftLight => SoftLightDestIn, + PixelColorBlendingMode.Difference => DifferenceDestIn, + PixelColorBlendingMode.Exclusion => ExclusionDestIn, + PixelColorBlendingMode.Hue => HueDestIn, + PixelColorBlendingMode.Saturation => SaturationDestIn, + PixelColorBlendingMode.Color => ColorDestIn, + PixelColorBlendingMode.Luminosity => LuminosityDestIn, + _ => NormalDestIn, + }, + PixelAlphaCompositionMode.DestOut => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyDestOut, + PixelColorBlendingMode.Add => AddDestOut, + PixelColorBlendingMode.Subtract => SubtractDestOut, + PixelColorBlendingMode.Screen => ScreenDestOut, + PixelColorBlendingMode.Darken => DarkenDestOut, + PixelColorBlendingMode.Lighten => LightenDestOut, + PixelColorBlendingMode.Overlay => OverlayDestOut, + PixelColorBlendingMode.HardLight => HardLightDestOut, + PixelColorBlendingMode.ColorDodge => ColorDodgeDestOut, + PixelColorBlendingMode.ColorBurn => ColorBurnDestOut, + PixelColorBlendingMode.SoftLight => SoftLightDestOut, + PixelColorBlendingMode.Difference => DifferenceDestOut, + PixelColorBlendingMode.Exclusion => ExclusionDestOut, + PixelColorBlendingMode.Hue => HueDestOut, + PixelColorBlendingMode.Saturation => SaturationDestOut, + PixelColorBlendingMode.Color => ColorDestOut, + PixelColorBlendingMode.Luminosity => LuminosityDestOut, + _ => NormalDestOut, + }, + PixelAlphaCompositionMode.Clear => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyClear, + PixelColorBlendingMode.Add => AddClear, + PixelColorBlendingMode.Subtract => SubtractClear, + PixelColorBlendingMode.Screen => ScreenClear, + PixelColorBlendingMode.Darken => DarkenClear, + PixelColorBlendingMode.Lighten => LightenClear, + PixelColorBlendingMode.Overlay => OverlayClear, + PixelColorBlendingMode.HardLight => HardLightClear, + PixelColorBlendingMode.ColorDodge => ColorDodgeClear, + PixelColorBlendingMode.ColorBurn => ColorBurnClear, + PixelColorBlendingMode.SoftLight => SoftLightClear, + PixelColorBlendingMode.Difference => DifferenceClear, + PixelColorBlendingMode.Exclusion => ExclusionClear, + PixelColorBlendingMode.Hue => HueClear, + PixelColorBlendingMode.Saturation => SaturationClear, + PixelColorBlendingMode.Color => ColorClear, + PixelColorBlendingMode.Luminosity => LuminosityClear, + _ => NormalClear, + }, + PixelAlphaCompositionMode.Xor => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyXor, + PixelColorBlendingMode.Add => AddXor, + PixelColorBlendingMode.Subtract => SubtractXor, + PixelColorBlendingMode.Screen => ScreenXor, + PixelColorBlendingMode.Darken => DarkenXor, + PixelColorBlendingMode.Lighten => LightenXor, + PixelColorBlendingMode.Overlay => OverlayXor, + PixelColorBlendingMode.HardLight => HardLightXor, + PixelColorBlendingMode.ColorDodge => ColorDodgeXor, + PixelColorBlendingMode.ColorBurn => ColorBurnXor, + PixelColorBlendingMode.SoftLight => SoftLightXor, + PixelColorBlendingMode.Difference => DifferenceXor, + PixelColorBlendingMode.Exclusion => ExclusionXor, + PixelColorBlendingMode.Hue => HueXor, + PixelColorBlendingMode.Saturation => SaturationXor, + PixelColorBlendingMode.Color => ColorXor, + PixelColorBlendingMode.Luminosity => LuminosityXor, + _ => NormalXor, + }, + PixelAlphaCompositionMode.Plus => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplyPlus, + PixelColorBlendingMode.Add => AddPlus, + PixelColorBlendingMode.Subtract => SubtractPlus, + PixelColorBlendingMode.Screen => ScreenPlus, + PixelColorBlendingMode.Darken => DarkenPlus, + PixelColorBlendingMode.Lighten => LightenPlus, + PixelColorBlendingMode.Overlay => OverlayPlus, + PixelColorBlendingMode.HardLight => HardLightPlus, + PixelColorBlendingMode.ColorDodge => ColorDodgePlus, + PixelColorBlendingMode.ColorBurn => ColorBurnPlus, + PixelColorBlendingMode.SoftLight => SoftLightPlus, + PixelColorBlendingMode.Difference => DifferencePlus, + PixelColorBlendingMode.Exclusion => ExclusionPlus, + PixelColorBlendingMode.Hue => HuePlus, + PixelColorBlendingMode.Saturation => SaturationPlus, + PixelColorBlendingMode.Color => ColorPlus, + PixelColorBlendingMode.Luminosity => LuminosityPlus, + _ => NormalPlus, + }, + _ => colorMode switch + { + PixelColorBlendingMode.Multiply => MultiplySrcOver, + PixelColorBlendingMode.Add => AddSrcOver, + PixelColorBlendingMode.Subtract => SubtractSrcOver, + PixelColorBlendingMode.Screen => ScreenSrcOver, + PixelColorBlendingMode.Darken => DarkenSrcOver, + PixelColorBlendingMode.Lighten => LightenSrcOver, + PixelColorBlendingMode.Overlay => OverlaySrcOver, + PixelColorBlendingMode.HardLight => HardLightSrcOver, + PixelColorBlendingMode.ColorDodge => ColorDodgeSrcOver, + PixelColorBlendingMode.ColorBurn => ColorBurnSrcOver, + PixelColorBlendingMode.SoftLight => SoftLightSrcOver, + PixelColorBlendingMode.Difference => DifferenceSrcOver, + PixelColorBlendingMode.Exclusion => ExclusionSrcOver, + PixelColorBlendingMode.Hue => HueSrcOver, + PixelColorBlendingMode.Saturation => SaturationSrcOver, + PixelColorBlendingMode.Color => ColorSrcOver, + PixelColorBlendingMode.Luminosity => LuminositySrcOver, + _ => NormalSrcOver, + }, + }; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs new file mode 100644 index 0000000000..5057a25bd8 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs @@ -0,0 +1,10921 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +internal static partial class AssociatedAlphaPorterDuffFunctions +{ + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "MultiplySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "AddSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SubtractSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ScreenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DarkenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LightenSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "OverlaySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HardLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HueSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HueSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "HueSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SaturationSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SaturationSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "SaturationSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "ColorSrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LuminositySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrc(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LuminositySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "LuminositySrc" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return source; + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlaySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorSrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminositySrcOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "MultiplyDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "AddDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SubtractDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ScreenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DarkenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LightenDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "OverlayDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HardLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SoftLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SoftLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SoftLightDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DifferenceDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DifferenceDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "DifferenceDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ExclusionDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ExclusionDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ExclusionDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HueDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HueDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "HueDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SaturationDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SaturationDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "SaturationDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "ColorDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LuminosityDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LuminosityDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "LuminosityDest" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return AtopNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestAtop" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return OverNormal(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "AddDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "HueDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOver" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestIn" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return In(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "MultiplyDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "AddDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SubtractDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ScreenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DarkenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LightenDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "OverlayDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HardLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SoftLightDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "DifferenceDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ExclusionDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "HueDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "SaturationDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "ColorDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "LuminosityDestOut" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Out(source, backdrop); + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "MultiplyClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "AddClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "SubtractClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ScreenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "DarkenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "LightenClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "OverlayClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "HardLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorBurnClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "SoftLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "SoftLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "SoftLightClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "DifferenceClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "DifferenceClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "DifferenceClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ExclusionClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ExclusionClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ExclusionClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "HueClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "HueClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "HueClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "SaturationClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "SaturationClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "SaturationClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "ColorClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "LuminosityClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityClear(Vector4 backdrop, Vector4 source, float opacity) + { + return Vector4.Zero; + } + + /// + /// Returns the associated-alpha result of the "LuminosityClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return Vector256.Zero; + } + + /// + /// Returns the associated-alpha result of the "LuminosityClear" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return Vector512.Zero; + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "AddXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SubtractXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ScreenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DarkenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LightenXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "OverlayXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HardLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgeXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SoftLightXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "DifferenceXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ExclusionXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "HueXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "SaturationXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "ColorXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminosityXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityXor(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminosityXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "LuminosityXor" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Xor(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return PlusNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return PlusNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "NormalPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return PlusNormal(backdrop, source); + } + + /// + /// Returns the associated-alpha result of the "MultiplyPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "MultiplyPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "AddPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SubtractPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ScreenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DarkenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LightenPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlayPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlayPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "OverlayPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HardLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgePlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorDodgePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorBurnPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SoftLightPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferencePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferencePlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferencePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferencePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "DifferencePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferencePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ExclusionPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HuePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HuePlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HuePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HuePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "HuePlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HuePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "SaturationPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "ColorPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityPlus(Vector4 backdrop, Vector4 source, float opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the associated-alpha result of the "LuminosityPlus" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt new file mode 100644 index 0000000000..8cc89620b0 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt @@ -0,0 +1,152 @@ +<# +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +internal static partial class AssociatedAlphaPorterDuffFunctions +{ +<# + foreach (string composer in Composers) + { + foreach (string blender in Blenders) + { + string function = blender + composer; + + foreach (string vectorType in VectorTypes) + { + string opacityType = vectorType == "Vector4" ? "float" : vectorType; + string zero = vectorType == "Vector4" ? "Vector4.Zero" : vectorType.Replace("", ".Zero"); +#> + /// + /// Returns the associated-alpha result of the "<#= function #>" compositing equation. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The source opacity in the range 0 through 1. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> <#= function #>(<#= vectorType #> backdrop, <#= vectorType #> source, <#= opacityType #> opacity) + { +<# + if (composer != "Dest" && composer != "Clear") + { +#> + // Associated RGB and alpha are scaled together so opacity cannot change the represented straight colour. + source *= opacity; + +<# + } +#> + return <#= GetComposition(blender, composer, zero) #>; + } + +<# + } + } + } +#> +} +<#+ + private static readonly string[] Composers = + { + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", + "Plus", + }; + + private static readonly string[] Blenders = + { + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight", + "ColorDodge", + "ColorBurn", + "SoftLight", + "Difference", + "Exclusion", + "Hue", + "Saturation", + "Color", + "Luminosity", + }; + + private static readonly string[] VectorTypes = + { + "Vector4", + "Vector256", + "Vector512", + }; + + private static string GetComposition(string blender, string composer, string zero) + { + bool normal = blender == "Normal"; + + switch (composer) + { + case "Src": + return "source"; + case "SrcAtop": + return normal + ? "AtopNormal(backdrop, source)" + : $"Atop(backdrop, source, {blender}(backdrop, source))"; + case "SrcOver": + return normal + ? "OverNormal(backdrop, source)" + : $"Over(backdrop, source, {blender}(backdrop, source))"; + case "SrcIn": + return "In(backdrop, source)"; + case "SrcOut": + return "Out(backdrop, source)"; + case "Dest": + return "backdrop"; + case "DestAtop": + return normal + ? "AtopNormal(source, backdrop)" + : $"Atop(source, backdrop, {blender}(source, backdrop))"; + case "DestOver": + return normal + ? "OverNormal(source, backdrop)" + : $"Over(source, backdrop, {blender}(source, backdrop))"; + case "DestIn": + return "In(source, backdrop)"; + case "DestOut": + return "Out(source, backdrop)"; + case "Clear": + return zero; + case "Plus": + return normal + ? "PlusNormal(backdrop, source)" + : $"Plus(backdrop, source, {blender}(backdrop, source))"; + default: + return "Xor(backdrop, source)"; + } + } +#> diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs new file mode 100644 index 0000000000..893e8e41a9 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs @@ -0,0 +1,999 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Provides Porter-Duff composition functions for associated-alpha vectors. +/// +internal static partial class AssociatedAlphaPorterDuffFunctions +{ + private const int BlendAlphaControl = 0b_10_00_10_00; + private const int ShuffleAlphaControl = 0b_11_11_11_11; + + /// + /// Calculates the associated overlap term for Multiply blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Multiply(Vector4 backdrop, Vector4 source) => backdrop * source; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Multiply(Vector256 backdrop, Vector256 source) => backdrop * source; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Multiply(Vector512 backdrop, Vector512 source) => backdrop * source; + + /// + /// Calculates the associated overlap term for Add blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Add(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Add(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Add(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha)); + } + + /// + /// Calculates the associated overlap term for Subtract blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Subtract(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Max(Vector4.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Subtract(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Max(Vector256.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Subtract(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Max(Vector512.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha)); + } + + /// + /// Calculates the associated overlap term for Screen blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Screen(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Screen(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Screen(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source); + } + + /// + /// Calculates the associated overlap term for Darken blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Darken(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Darken(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Darken(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Min(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + /// Calculates the associated overlap term for Lighten blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Lighten(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + return Vector4.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Lighten(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return Vector256.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Lighten(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return Vector512.Max(backdrop * sourceAlpha, source * backdropAlpha); + } + + /// + /// Calculates the associated overlap term for Overlay blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Overlay(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + + return new Vector4( + OverlayValue(backdrop.X, backdropAlpha.X, source.X, sourceAlpha.X), + OverlayValue(backdrop.Y, backdropAlpha.Y, source.Y, sourceAlpha.Y), + OverlayValue(backdrop.Z, backdropAlpha.Z, source.Z, sourceAlpha.Z), + 0F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Overlay(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 left = (backdrop + backdrop) * source; + Vector256 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); + Vector256 useRight = Avx.CompareGreaterThan(backdrop + backdrop, backdropAlpha); + return Avx.BlendVariable(left, right, useRight); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Overlay(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 left = (backdrop + backdrop) * source; + Vector512 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); + Vector512 useRight = Avx512F.CompareGreaterThan(backdrop + backdrop, backdropAlpha); + return Vector512.ConditionalSelect(useRight, right, left); + } + + /// + /// Calculates the associated overlap term for HardLight blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLight(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + + return new Vector4( + OverlayValue(source.X, sourceAlpha.X, backdrop.X, backdropAlpha.X), + OverlayValue(source.Y, sourceAlpha.Y, backdrop.Y, backdropAlpha.Y), + OverlayValue(source.Z, sourceAlpha.Z, backdrop.Z, backdropAlpha.Z), + 0F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLight(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 left = (backdrop + backdrop) * source; + Vector256 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F)); + Vector256 useRight = Avx.CompareGreaterThan(source + source, sourceAlpha); + return Avx.BlendVariable(left, right, useRight); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLight(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 left = (backdrop + backdrop) * source; + Vector512 right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F)); + Vector512 useRight = Avx512F.CompareGreaterThan(source + source, sourceAlpha); + return Vector512.ConditionalSelect(useRight, right, left); + } + + /// + /// Calculates the associated overlap term for ColorDodge blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodge(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodge(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodge(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.ColorDodge(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for ColorBurn blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurn(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurn(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurn(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.ColorBurn(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for SoftLight blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLight(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLight(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLight(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.SoftLight(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Difference blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Difference(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Difference(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Difference(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Difference(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Exclusion blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Exclusion(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exclusion(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Exclusion(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Exclusion(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Hue blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Hue(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Hue(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Hue(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Hue(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Saturation blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Saturation(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Saturation(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Saturation(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Saturation(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Color blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Color(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Color(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Color(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Color(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Calculates the associated overlap term for Luminosity blending. + /// + /// The associated backdrop vector. + /// The associated source vector. + /// The associated overlap term. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Luminosity(Vector4 backdrop, Vector4 source) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 unassociatedBackdrop = backdrop; + Vector4 unassociatedSource = source; + + // This blend equation is defined in straight RGB, so recover each color before scaling the result by the overlap alpha. + Numerics.UnPremultiply(ref unassociatedBackdrop); + Numerics.UnPremultiply(ref unassociatedSource); + return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Luminosity(Vector256 backdrop, Vector256 source) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector256 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Luminosity(Vector512 backdrop, Vector512 source) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 unassociatedBackdrop = Numerics.UnPremultiply(backdrop, backdropAlpha); + Vector512 unassociatedSource = Numerics.UnPremultiply(source, sourceAlpha); + return PorterDuffFunctions.Luminosity(unassociatedBackdrop, unassociatedSource) * backdropAlpha * sourceAlpha; + } + + /// + /// Composites an associated source over an associated destination without a color-blending function. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverNormal(Vector4 destination, Vector4 source) + { + // Associated source-over is Ps + Pb(1 - As); both color and alpha therefore use the same coefficient. + Vector4 sourceAlpha = Numerics.PermuteW(source); + return source + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverNormal(Vector256 destination, Vector256 source) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + return source + (destination * (Vector256.Create(1F) - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverNormal(Vector512 destination, Vector512 source) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + return source + (destination * (Vector512.Create(1F) - sourceAlpha)); + } + + /// + /// Adds associated source and destination vectors without a color-blending function. + /// + /// The associated destination vector. + /// The associated source vector. + /// The clamped associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 PlusNormal(Vector4 destination, Vector4 source) + { + Vector4 alpha = Vector4.Min(Vector4.One, Numerics.PermuteW(source) + Numerics.PermuteW(destination)); + return Numerics.WithW(Vector4.Min(Vector4.One, destination + source), alpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 PlusNormal(Vector256 destination, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 alpha = Vector256.Min(one, Avx.Permute(source, ShuffleAlphaControl) + Avx.Permute(destination, ShuffleAlphaControl)); + return Avx.Blend(Vector256.Min(one, destination + source), alpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 PlusNormal(Vector512 destination, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 alpha = Vector512.Min(one, Vector512_.ShuffleNative(source, ShuffleAlphaControl) + Vector512_.ShuffleNative(destination, ShuffleAlphaControl)); + return Vector512.ConditionalSelect(AlphaMask512(), alpha, Vector512.Min(one, destination + source)); + } + + /// + /// Adds associated source and destination vectors using a color-blended overlap term. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated overlap term produced by the color-blending function. + /// The clamped associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Plus(Vector4 destination, Vector4 source, Vector4 overlap) + { + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 alpha = Vector4.Min(Vector4.One, Numerics.PermuteW(source) + destinationAlpha); + + // The blended source contributes straight source color outside the destination and the blend result in the overlap. + Vector4 result = destination + (source * (Vector4.One - destinationAlpha)) + overlap; + return Numerics.WithW(Vector4.Min(Vector4.One, result), alpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Plus(Vector256 destination, Vector256 source, Vector256 overlap) + { + Vector256 one = Vector256.Create(1F); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 alpha = Vector256.Min(one, Avx.Permute(source, ShuffleAlphaControl) + destinationAlpha); + Vector256 result = destination + (source * (one - destinationAlpha)) + overlap; + return Avx.Blend(Vector256.Min(one, result), alpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Plus(Vector512 destination, Vector512 source, Vector512 overlap) + { + Vector512 one = Vector512.Create(1F); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 alpha = Vector512.Min(one, Vector512_.ShuffleNative(source, ShuffleAlphaControl) + destinationAlpha); + Vector512 result = destination + (source * (one - destinationAlpha)) + overlap; + return Vector512.ConditionalSelect(AlphaMask512(), alpha, Vector512.Min(one, result)); + } + + /// + /// Composites an associated source atop an associated destination without a color-blending function. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AtopNormal(Vector4 destination, Vector4 source) + { + // Source-atop retains the destination alpha while replacing its covered contribution with the source. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + return (source * destinationAlpha) + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AtopNormal(Vector256 destination, Vector256 source) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + return (source * destinationAlpha) + (destination * (Vector256.Create(1F) - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AtopNormal(Vector512 destination, Vector512 source) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + return (source * destinationAlpha) + (destination * (Vector512.Create(1F) - sourceAlpha)); + } + + /// + /// Composites an associated source over an associated destination using an unassociated blended color. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated overlap term produced by the color-blending function. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 overlap) + { + // The three terms cover destination-only, source-only, and overlapping color respectively. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 result = (destination * (Vector4.One - sourceAlpha)) + (source * (Vector4.One - destinationAlpha)) + overlap; + Vector4 alpha = source + (destination * (Vector4.One - sourceAlpha)); + return Numerics.WithW(result, alpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Over(Vector256 destination, Vector256 source, Vector256 overlap) + { + Vector256 one = Vector256.Create(1F); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; + Vector256 alpha = source + (destination * (one - sourceAlpha)); + return Avx.Blend(result, alpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Over(Vector512 destination, Vector512 source, Vector512 overlap) + { + Vector512 one = Vector512.Create(1F); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap; + Vector512 alpha = source + (destination * (one - sourceAlpha)); + return Vector512.ConditionalSelect(AlphaMask512(), alpha, result); + } + + /// + /// Composites an associated source atop an associated destination using an unassociated blended color. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated overlap term produced by the color-blending function. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 overlap) + { + // Atop discards source-only color and retains the destination alpha unchanged. + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 coefficient = Vector4.One - sourceAlpha; + Vector4 result = Vector128_.FusedMultiplyAdd(destination.AsVector128(), coefficient.AsVector128(), overlap.AsVector128()).AsVector4(); + + return Numerics.WithW(result, destinationAlpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Atop(Vector256 destination, Vector256 source, Vector256 overlap) + { + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 result = Vector256_.FusedMultiplyAdd(destination, Vector256.Create(1F) - sourceAlpha, overlap); + + return Avx.Blend(result, destinationAlpha, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Atop(Vector512 destination, Vector512 source, Vector512 overlap) + { + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 result = Vector512_.FusedMultiplyAdd(destination, Vector512.Create(1F) - sourceAlpha, overlap); + + return Vector512.ConditionalSelect(AlphaMask512(), destinationAlpha, result); + } + + /// + /// Retains the associated source within the destination coverage. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 In(Vector4 destination, Vector4 source) => source * Numerics.PermuteW(destination); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 In(Vector256 destination, Vector256 source) + => source * Avx.Permute(destination, ShuffleAlphaControl); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 In(Vector512 destination, Vector512 source) + => source * Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + + /// + /// Retains the associated source outside the destination coverage. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Out(Vector4 destination, Vector4 source) + => source * (Vector4.One - Numerics.PermuteW(destination)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Out(Vector256 destination, Vector256 source) + => source * (Vector256.Create(1F) - Avx.Permute(destination, ShuffleAlphaControl)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Out(Vector512 destination, Vector512 source) + => source * (Vector512.Create(1F) - Vector512_.ShuffleNative(destination, ShuffleAlphaControl)); + + /// + /// Retains only the non-overlapping parts of two associated vectors. + /// + /// The associated destination vector. + /// The associated source vector. + /// The associated composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Xor(Vector4 destination, Vector4 source) + { + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + return (source * (Vector4.One - destinationAlpha)) + (destination * (Vector4.One - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Xor(Vector256 destination, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Xor(Vector512 destination, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha)); + } + + /// + /// Applies raster coverage to an associated composition result. + /// + /// The associated backdrop vector. + /// The associated composition result. + /// The raster coverage in the range 0 through 1. + /// The covered associated result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 BlendWithCoverage(Vector4 backdrop, Vector4 source, float coverage) + { + // Use the same fused operation as the wider paths so exact midpoints cannot change across vector widths. + return Vector128_.FusedMultiplyAdd((source - backdrop).AsVector128(), Vector128.Create(coverage), backdrop.AsVector128()).AsVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 BlendWithCoverage(Vector256 backdrop, Vector256 source, Vector256 coverage) + => Vector256_.FusedMultiplyAdd(source - backdrop, coverage, backdrop); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 BlendWithCoverage(Vector512 backdrop, Vector512 source, Vector512 coverage) + => Vector512_.FusedMultiplyAdd(source - backdrop, coverage, backdrop); + + /// + /// Calculates one associated Overlay overlap component without recovering either straight component. + /// + /// The associated backdrop component. + /// The backdrop alpha. + /// The associated source component. + /// The source alpha. + /// The associated overlap component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float OverlayValue(float backdrop, float backdropAlpha, float source, float sourceAlpha) + { + // Comparing 2Pb with Ab is equivalent to comparing the straight backdrop component with one half. + return (backdrop + backdrop) <= backdropAlpha + ? (backdrop + backdrop) * source + : (backdropAlpha * sourceAlpha) - (2F * (backdropAlpha - backdrop) * (sourceAlpha - source)); + } + + /// + /// Creates a SIMD lane mask selecting the alpha component of each packed vector. + /// + /// The alpha-component mask. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 AlphaMask512() + => Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 0792306760..de2ccf3a9f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -3,11366 +3,6106 @@ // using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; /// -/// Collection of Porter Duff alpha blending functions applying different composition models. +/// Provides the straight-alpha equations consumed by the shared pixel-blending traversal. /// -/// -/// These functions are designed to be a general solution for all color cases, -/// that is, they take in account the alpha value of both the backdrop -/// and source, and there's no need to alpha-premultiply neither the backdrop -/// nor the source. -/// Note there are faster functions for when the backdrop color is known -/// to be opaque -/// -internal static class DefaultPixelBlenders - where TPixel : unmanaged, IPixel +internal static class DefaultPixelBlenderOperators { + /// + /// Applies the "NormalSrc" straight-alpha composition equation. + /// + public readonly struct NormalSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalSrc(background, source, amount); + } + + /// + /// Applies the "MultiplySrc" straight-alpha composition equation. + /// + public readonly struct MultiplySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplySrc(background, source, amount); + } + + /// + /// Applies the "AddSrc" straight-alpha composition equation. + /// + public readonly struct AddSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddSrc(background, source, amount); + } + + /// + /// Applies the "SubtractSrc" straight-alpha composition equation. + /// + public readonly struct SubtractSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractSrc(background, source, amount); + } + + /// + /// Applies the "ScreenSrc" straight-alpha composition equation. + /// + public readonly struct ScreenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenSrc(background, source, amount); + } + + /// + /// Applies the "DarkenSrc" straight-alpha composition equation. + /// + public readonly struct DarkenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenSrc(background, source, amount); + } + + /// + /// Applies the "LightenSrc" straight-alpha composition equation. + /// + public readonly struct LightenSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenSrc(background, source, amount); + } + + /// + /// Applies the "OverlaySrc" straight-alpha composition equation. + /// + public readonly struct OverlaySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlaySrc(background, source, amount); + } + + /// + /// Applies the "HardLightSrc" straight-alpha composition equation. + /// + public readonly struct HardLightSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightSrc(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrc" straight-alpha composition equation. + /// + public readonly struct ColorDodgeSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeSrc(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrc" straight-alpha composition equation. + /// + public readonly struct ColorBurnSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnSrc(background, source, amount); + } + + /// + /// Applies the "SoftLightSrc" straight-alpha composition equation. + /// + public readonly struct SoftLightSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightSrc(background, source, amount); + } + + /// + /// Applies the "DifferenceSrc" straight-alpha composition equation. + /// + public readonly struct DifferenceSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceSrc(background, source, amount); + } + + /// + /// Applies the "ExclusionSrc" straight-alpha composition equation. + /// + public readonly struct ExclusionSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionSrc(background, source, amount); + } + + /// + /// Applies the "HueSrc" straight-alpha composition equation. + /// + public readonly struct HueSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueSrc(background, source, amount); + } + + /// + /// Applies the "SaturationSrc" straight-alpha composition equation. + /// + public readonly struct SaturationSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationSrc(background, source, amount); + } + + /// + /// Applies the "ColorSrc" straight-alpha composition equation. + /// + public readonly struct ColorSrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorSrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorSrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorSrc(background, source, amount); + } + + /// + /// Applies the "LuminositySrc" straight-alpha composition equation. + /// + public readonly struct LuminositySrc : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminositySrc(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminositySrc(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminositySrc(background, source, amount); + } + + /// + /// Applies the "NormalSrcAtop" straight-alpha composition equation. + /// + public readonly struct NormalSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalSrcAtop(background, source, amount); + } + + /// + /// Applies the "MultiplySrcAtop" straight-alpha composition equation. + /// + public readonly struct MultiplySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplySrcAtop(background, source, amount); + } /// - /// A pixel blender that implements the "NormalSrc" composition equation. - /// - public class NormalSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrc Instance { get; } = new NormalSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrc" composition equation. - /// - public class MultiplySrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrc Instance { get; } = new MultiplySrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrc" composition equation. - /// - public class AddSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrc Instance { get; } = new AddSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrc" composition equation. - /// - public class SubtractSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrc Instance { get; } = new SubtractSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrc" composition equation. - /// - public class ScreenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrc Instance { get; } = new ScreenSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrc" composition equation. - /// - public class DarkenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrc Instance { get; } = new DarkenSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrc" composition equation. - /// - public class LightenSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrc Instance { get; } = new LightenSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrc" composition equation. - /// - public class OverlaySrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrc Instance { get; } = new OverlaySrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrc" composition equation. - /// - public class HardLightSrc : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrc Instance { get; } = new HardLightSrc(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcAtop" composition equation. - /// - public class NormalSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcAtop" composition equation. - /// - public class MultiplySrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcAtop" composition equation. - /// - public class AddSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcAtop Instance { get; } = new AddSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcAtop" composition equation. - /// - public class SubtractSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcAtop" composition equation. - /// - public class ScreenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcAtop" composition equation. - /// - public class DarkenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcAtop" composition equation. - /// - public class LightenSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcAtop" composition equation. - /// - public class OverlaySrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcAtop" composition equation. - /// - public class HardLightSrcAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOver" composition equation. - /// - public class NormalSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOver Instance { get; } = new NormalSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOver" composition equation. - /// - public class MultiplySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOver" composition equation. - /// - public class AddSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOver Instance { get; } = new AddSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOver" composition equation. - /// - public class SubtractSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOver" composition equation. - /// - public class ScreenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOver" composition equation. - /// - public class DarkenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOver" composition equation. - /// - public class LightenSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOver Instance { get; } = new LightenSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOver" composition equation. - /// - public class OverlaySrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOver" composition equation. - /// - public class HardLightSrcOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcIn" composition equation. - /// - public class NormalSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcIn Instance { get; } = new NormalSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcIn" composition equation. - /// - public class MultiplySrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcIn" composition equation. - /// - public class AddSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcIn Instance { get; } = new AddSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcIn" composition equation. - /// - public class SubtractSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcIn" composition equation. - /// - public class ScreenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcIn" composition equation. - /// - public class DarkenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcIn" composition equation. - /// - public class LightenSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcIn Instance { get; } = new LightenSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcIn" composition equation. - /// - public class OverlaySrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcIn" composition equation. - /// - public class HardLightSrcIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalSrcOut" composition equation. - /// - public class NormalSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalSrcOut Instance { get; } = new NormalSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplySrcOut" composition equation. - /// - public class MultiplySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddSrcOut" composition equation. - /// - public class AddSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddSrcOut Instance { get; } = new AddSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractSrcOut" composition equation. - /// - public class SubtractSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenSrcOut" composition equation. - /// - public class ScreenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenSrcOut" composition equation. - /// - public class DarkenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenSrcOut" composition equation. - /// - public class LightenSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenSrcOut Instance { get; } = new LightenSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlaySrcOut" composition equation. - /// - public class OverlaySrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightSrcOut" composition equation. - /// - public class HardLightSrcOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDest" composition equation. - /// - public class NormalDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDest Instance { get; } = new NormalDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDest" composition equation. - /// - public class MultiplyDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDest Instance { get; } = new MultiplyDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDest" composition equation. - /// - public class AddDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDest Instance { get; } = new AddDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDest" composition equation. - /// - public class SubtractDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDest Instance { get; } = new SubtractDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDest" composition equation. - /// - public class ScreenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDest Instance { get; } = new ScreenDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDest" composition equation. - /// - public class DarkenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDest Instance { get; } = new DarkenDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDest" composition equation. - /// - public class LightenDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDest Instance { get; } = new LightenDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDest" composition equation. - /// - public class OverlayDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDest Instance { get; } = new OverlayDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDest" composition equation. - /// - public class HardLightDest : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDest Instance { get; } = new HardLightDest(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestAtop" composition equation. - /// - public class NormalDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestAtop Instance { get; } = new NormalDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestAtop" composition equation. - /// - public class MultiplyDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestAtop" composition equation. - /// - public class AddDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestAtop Instance { get; } = new AddDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestAtop" composition equation. - /// - public class SubtractDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestAtop" composition equation. - /// - public class ScreenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestAtop" composition equation. - /// - public class DarkenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestAtop" composition equation. - /// - public class LightenDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestAtop Instance { get; } = new LightenDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestAtop" composition equation. - /// - public class OverlayDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestAtop" composition equation. - /// - public class HardLightDestAtop : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOver" composition equation. - /// - public class NormalDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOver Instance { get; } = new NormalDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOver" composition equation. - /// - public class MultiplyDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOver" composition equation. - /// - public class AddDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOver Instance { get; } = new AddDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOver" composition equation. - /// - public class SubtractDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOver Instance { get; } = new SubtractDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOver" composition equation. - /// - public class ScreenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOver Instance { get; } = new ScreenDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOver" composition equation. - /// - public class DarkenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOver Instance { get; } = new DarkenDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOver" composition equation. - /// - public class LightenDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOver Instance { get; } = new LightenDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOver" composition equation. - /// - public class OverlayDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOver Instance { get; } = new OverlayDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOver" composition equation. - /// - public class HardLightDestOver : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOver Instance { get; } = new HardLightDestOver(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestIn" composition equation. - /// - public class NormalDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestIn Instance { get; } = new NormalDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestIn" composition equation. - /// - public class MultiplyDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestIn" composition equation. - /// - public class AddDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestIn Instance { get; } = new AddDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestIn" composition equation. - /// - public class SubtractDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestIn Instance { get; } = new SubtractDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestIn" composition equation. - /// - public class ScreenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestIn Instance { get; } = new ScreenDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestIn" composition equation. - /// - public class DarkenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestIn Instance { get; } = new DarkenDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestIn" composition equation. - /// - public class LightenDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestIn Instance { get; } = new LightenDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestIn" composition equation. - /// - public class OverlayDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestIn Instance { get; } = new OverlayDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestIn" composition equation. - /// - public class HardLightDestIn : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestIn Instance { get; } = new HardLightDestIn(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalDestOut" composition equation. - /// - public class NormalDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalDestOut Instance { get; } = new NormalDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyDestOut" composition equation. - /// - public class MultiplyDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddDestOut" composition equation. - /// - public class AddDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddDestOut Instance { get; } = new AddDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractDestOut" composition equation. - /// - public class SubtractDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractDestOut Instance { get; } = new SubtractDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenDestOut" composition equation. - /// - public class ScreenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenDestOut Instance { get; } = new ScreenDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenDestOut" composition equation. - /// - public class DarkenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenDestOut Instance { get; } = new DarkenDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenDestOut" composition equation. - /// - public class LightenDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenDestOut Instance { get; } = new LightenDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayDestOut" composition equation. - /// - public class OverlayDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayDestOut Instance { get; } = new OverlayDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightDestOut" composition equation. - /// - public class HardLightDestOut : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightDestOut Instance { get; } = new HardLightDestOut(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalClear" composition equation. - /// - public class NormalClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalClear Instance { get; } = new NormalClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyClear" composition equation. - /// - public class MultiplyClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyClear Instance { get; } = new MultiplyClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddClear" composition equation. - /// - public class AddClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddClear Instance { get; } = new AddClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractClear" composition equation. - /// - public class SubtractClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractClear Instance { get; } = new SubtractClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenClear" composition equation. - /// - public class ScreenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenClear Instance { get; } = new ScreenClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenClear" composition equation. - /// - public class DarkenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenClear Instance { get; } = new DarkenClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenClear" composition equation. - /// - public class LightenClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenClear Instance { get; } = new LightenClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayClear" composition equation. - /// - public class OverlayClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayClear Instance { get; } = new OverlayClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightClear" composition equation. - /// - public class HardLightClear : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightClear Instance { get; } = new HardLightClear(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "NormalXor" composition equation. - /// - public class NormalXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static NormalXor Instance { get; } = new NormalXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "MultiplyXor" composition equation. - /// - public class MultiplyXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static MultiplyXor Instance { get; } = new MultiplyXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "AddXor" composition equation. - /// - public class AddXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static AddXor Instance { get; } = new AddXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "SubtractXor" composition equation. - /// - public class SubtractXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static SubtractXor Instance { get; } = new SubtractXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "ScreenXor" composition equation. - /// - public class ScreenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static ScreenXor Instance { get; } = new ScreenXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "DarkenXor" composition equation. - /// - public class DarkenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static DarkenXor Instance { get; } = new DarkenXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "LightenXor" composition equation. - /// - public class LightenXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static LightenXor Instance { get; } = new LightenXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "OverlayXor" composition equation. - /// - public class OverlayXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static OverlayXor Instance { get; } = new OverlayXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } - } - - /// - /// A pixel blender that implements the "HardLightXor" composition equation. - /// - public class HardLightXor : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static HardLightXor Instance { get; } = new HardLightXor(); - - /// - 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; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); - } - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// Applies the "AddSrcAtop" straight-alpha composition equation. + /// + public readonly struct AddSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddSrcAtop(background, source, amount); } + /// + /// Applies the "SubtractSrcAtop" straight-alpha composition equation. + /// + public readonly struct SubtractSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractSrcAtop(background, source, amount); + } + + /// + /// Applies the "ScreenSrcAtop" straight-alpha composition equation. + /// + public readonly struct ScreenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenSrcAtop(background, source, amount); + } + + /// + /// Applies the "DarkenSrcAtop" straight-alpha composition equation. + /// + public readonly struct DarkenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenSrcAtop(background, source, amount); + } + + /// + /// Applies the "LightenSrcAtop" straight-alpha composition equation. + /// + public readonly struct LightenSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenSrcAtop(background, source, amount); + } + + /// + /// Applies the "OverlaySrcAtop" straight-alpha composition equation. + /// + public readonly struct OverlaySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlaySrcAtop(background, source, amount); + } + + /// + /// Applies the "HardLightSrcAtop" straight-alpha composition equation. + /// + public readonly struct HardLightSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcAtop" straight-alpha composition equation. + /// + public readonly struct ColorDodgeSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcAtop" straight-alpha composition equation. + /// + public readonly struct ColorBurnSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnSrcAtop(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcAtop" straight-alpha composition equation. + /// + public readonly struct SoftLightSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightSrcAtop(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcAtop" straight-alpha composition equation. + /// + public readonly struct DifferenceSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceSrcAtop(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcAtop" straight-alpha composition equation. + /// + public readonly struct ExclusionSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionSrcAtop(background, source, amount); + } + + /// + /// Applies the "HueSrcAtop" straight-alpha composition equation. + /// + public readonly struct HueSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueSrcAtop(background, source, amount); + } + + /// + /// Applies the "SaturationSrcAtop" straight-alpha composition equation. + /// + public readonly struct SaturationSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationSrcAtop(background, source, amount); + } + + /// + /// Applies the "ColorSrcAtop" straight-alpha composition equation. + /// + public readonly struct ColorSrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorSrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorSrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorSrcAtop(background, source, amount); + } + + /// + /// Applies the "LuminositySrcAtop" straight-alpha composition equation. + /// + public readonly struct LuminositySrcAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminositySrcAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminositySrcAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminositySrcAtop(background, source, amount); + } + + /// + /// Applies the "NormalSrcOver" straight-alpha composition equation. + /// + public readonly struct NormalSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalSrcOver(background, source, amount); + } + + /// + /// Applies the "MultiplySrcOver" straight-alpha composition equation. + /// + public readonly struct MultiplySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplySrcOver(background, source, amount); + } + + /// + /// Applies the "AddSrcOver" straight-alpha composition equation. + /// + public readonly struct AddSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddSrcOver(background, source, amount); + } + + /// + /// Applies the "SubtractSrcOver" straight-alpha composition equation. + /// + public readonly struct SubtractSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractSrcOver(background, source, amount); + } + + /// + /// Applies the "ScreenSrcOver" straight-alpha composition equation. + /// + public readonly struct ScreenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenSrcOver(background, source, amount); + } + + /// + /// Applies the "DarkenSrcOver" straight-alpha composition equation. + /// + public readonly struct DarkenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenSrcOver(background, source, amount); + } + + /// + /// Applies the "LightenSrcOver" straight-alpha composition equation. + /// + public readonly struct LightenSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenSrcOver(background, source, amount); + } + + /// + /// Applies the "OverlaySrcOver" straight-alpha composition equation. + /// + public readonly struct OverlaySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlaySrcOver(background, source, amount); + } + + /// + /// Applies the "HardLightSrcOver" straight-alpha composition equation. + /// + public readonly struct HardLightSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcOver" straight-alpha composition equation. + /// + public readonly struct ColorDodgeSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcOver" straight-alpha composition equation. + /// + public readonly struct ColorBurnSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnSrcOver(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcOver" straight-alpha composition equation. + /// + public readonly struct SoftLightSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightSrcOver(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcOver" straight-alpha composition equation. + /// + public readonly struct DifferenceSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceSrcOver(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcOver" straight-alpha composition equation. + /// + public readonly struct ExclusionSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionSrcOver(background, source, amount); + } + + /// + /// Applies the "HueSrcOver" straight-alpha composition equation. + /// + public readonly struct HueSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueSrcOver(background, source, amount); + } + + /// + /// Applies the "SaturationSrcOver" straight-alpha composition equation. + /// + public readonly struct SaturationSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationSrcOver(background, source, amount); + } + + /// + /// Applies the "ColorSrcOver" straight-alpha composition equation. + /// + public readonly struct ColorSrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorSrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorSrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorSrcOver(background, source, amount); + } + + /// + /// Applies the "LuminositySrcOver" straight-alpha composition equation. + /// + public readonly struct LuminositySrcOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminositySrcOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminositySrcOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminositySrcOver(background, source, amount); + } + + /// + /// Applies the "NormalSrcIn" straight-alpha composition equation. + /// + public readonly struct NormalSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalSrcIn(background, source, amount); + } + + /// + /// Applies the "MultiplySrcIn" straight-alpha composition equation. + /// + public readonly struct MultiplySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplySrcIn(background, source, amount); + } + + /// + /// Applies the "AddSrcIn" straight-alpha composition equation. + /// + public readonly struct AddSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddSrcIn(background, source, amount); + } + + /// + /// Applies the "SubtractSrcIn" straight-alpha composition equation. + /// + public readonly struct SubtractSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractSrcIn(background, source, amount); + } + + /// + /// Applies the "ScreenSrcIn" straight-alpha composition equation. + /// + public readonly struct ScreenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenSrcIn(background, source, amount); + } + + /// + /// Applies the "DarkenSrcIn" straight-alpha composition equation. + /// + public readonly struct DarkenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenSrcIn(background, source, amount); + } + + /// + /// Applies the "LightenSrcIn" straight-alpha composition equation. + /// + public readonly struct LightenSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenSrcIn(background, source, amount); + } + + /// + /// Applies the "OverlaySrcIn" straight-alpha composition equation. + /// + public readonly struct OverlaySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlaySrcIn(background, source, amount); + } + + /// + /// Applies the "HardLightSrcIn" straight-alpha composition equation. + /// + public readonly struct HardLightSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcIn" straight-alpha composition equation. + /// + public readonly struct ColorDodgeSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcIn" straight-alpha composition equation. + /// + public readonly struct ColorBurnSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnSrcIn(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcIn" straight-alpha composition equation. + /// + public readonly struct SoftLightSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightSrcIn(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcIn" straight-alpha composition equation. + /// + public readonly struct DifferenceSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceSrcIn(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcIn" straight-alpha composition equation. + /// + public readonly struct ExclusionSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionSrcIn(background, source, amount); + } + + /// + /// Applies the "HueSrcIn" straight-alpha composition equation. + /// + public readonly struct HueSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueSrcIn(background, source, amount); + } + + /// + /// Applies the "SaturationSrcIn" straight-alpha composition equation. + /// + public readonly struct SaturationSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationSrcIn(background, source, amount); + } + + /// + /// Applies the "ColorSrcIn" straight-alpha composition equation. + /// + public readonly struct ColorSrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorSrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorSrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorSrcIn(background, source, amount); + } + + /// + /// Applies the "LuminositySrcIn" straight-alpha composition equation. + /// + public readonly struct LuminositySrcIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminositySrcIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminositySrcIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminositySrcIn(background, source, amount); + } + + /// + /// Applies the "NormalSrcOut" straight-alpha composition equation. + /// + public readonly struct NormalSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalSrcOut(background, source, amount); + } + + /// + /// Applies the "MultiplySrcOut" straight-alpha composition equation. + /// + public readonly struct MultiplySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplySrcOut(background, source, amount); + } + + /// + /// Applies the "AddSrcOut" straight-alpha composition equation. + /// + public readonly struct AddSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddSrcOut(background, source, amount); + } + + /// + /// Applies the "SubtractSrcOut" straight-alpha composition equation. + /// + public readonly struct SubtractSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractSrcOut(background, source, amount); + } + + /// + /// Applies the "ScreenSrcOut" straight-alpha composition equation. + /// + public readonly struct ScreenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenSrcOut(background, source, amount); + } + + /// + /// Applies the "DarkenSrcOut" straight-alpha composition equation. + /// + public readonly struct DarkenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenSrcOut(background, source, amount); + } + + /// + /// Applies the "LightenSrcOut" straight-alpha composition equation. + /// + public readonly struct LightenSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenSrcOut(background, source, amount); + } + + /// + /// Applies the "OverlaySrcOut" straight-alpha composition equation. + /// + public readonly struct OverlaySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlaySrcOut(background, source, amount); + } + + /// + /// Applies the "HardLightSrcOut" straight-alpha composition equation. + /// + public readonly struct HardLightSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorDodgeSrcOut" straight-alpha composition equation. + /// + public readonly struct ColorDodgeSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorBurnSrcOut" straight-alpha composition equation. + /// + public readonly struct ColorBurnSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnSrcOut(background, source, amount); + } + + /// + /// Applies the "SoftLightSrcOut" straight-alpha composition equation. + /// + public readonly struct SoftLightSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightSrcOut(background, source, amount); + } + + /// + /// Applies the "DifferenceSrcOut" straight-alpha composition equation. + /// + public readonly struct DifferenceSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceSrcOut(background, source, amount); + } + + /// + /// Applies the "ExclusionSrcOut" straight-alpha composition equation. + /// + public readonly struct ExclusionSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionSrcOut(background, source, amount); + } + + /// + /// Applies the "HueSrcOut" straight-alpha composition equation. + /// + public readonly struct HueSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueSrcOut(background, source, amount); + } + + /// + /// Applies the "SaturationSrcOut" straight-alpha composition equation. + /// + public readonly struct SaturationSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationSrcOut(background, source, amount); + } + + /// + /// Applies the "ColorSrcOut" straight-alpha composition equation. + /// + public readonly struct ColorSrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorSrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorSrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorSrcOut(background, source, amount); + } + + /// + /// Applies the "LuminositySrcOut" straight-alpha composition equation. + /// + public readonly struct LuminositySrcOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminositySrcOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminositySrcOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminositySrcOut(background, source, amount); + } + + /// + /// Applies the "NormalDest" straight-alpha composition equation. + /// + public readonly struct NormalDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalDest(background, source, amount); + } + + /// + /// Applies the "MultiplyDest" straight-alpha composition equation. + /// + public readonly struct MultiplyDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyDest(background, source, amount); + } + + /// + /// Applies the "AddDest" straight-alpha composition equation. + /// + public readonly struct AddDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddDest(background, source, amount); + } + + /// + /// Applies the "SubtractDest" straight-alpha composition equation. + /// + public readonly struct SubtractDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractDest(background, source, amount); + } + + /// + /// Applies the "ScreenDest" straight-alpha composition equation. + /// + public readonly struct ScreenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenDest(background, source, amount); + } + + /// + /// Applies the "DarkenDest" straight-alpha composition equation. + /// + public readonly struct DarkenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenDest(background, source, amount); + } + + /// + /// Applies the "LightenDest" straight-alpha composition equation. + /// + public readonly struct LightenDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenDest(background, source, amount); + } + + /// + /// Applies the "OverlayDest" straight-alpha composition equation. + /// + public readonly struct OverlayDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayDest(background, source, amount); + } + + /// + /// Applies the "HardLightDest" straight-alpha composition equation. + /// + public readonly struct HardLightDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightDest(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDest" straight-alpha composition equation. + /// + public readonly struct ColorDodgeDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeDest(background, source, amount); + } + + /// + /// Applies the "ColorBurnDest" straight-alpha composition equation. + /// + public readonly struct ColorBurnDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnDest(background, source, amount); + } + + /// + /// Applies the "SoftLightDest" straight-alpha composition equation. + /// + public readonly struct SoftLightDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightDest(background, source, amount); + } + + /// + /// Applies the "DifferenceDest" straight-alpha composition equation. + /// + public readonly struct DifferenceDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceDest(background, source, amount); + } + + /// + /// Applies the "ExclusionDest" straight-alpha composition equation. + /// + public readonly struct ExclusionDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionDest(background, source, amount); + } + + /// + /// Applies the "HueDest" straight-alpha composition equation. + /// + public readonly struct HueDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueDest(background, source, amount); + } + + /// + /// Applies the "SaturationDest" straight-alpha composition equation. + /// + public readonly struct SaturationDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationDest(background, source, amount); + } + + /// + /// Applies the "ColorDest" straight-alpha composition equation. + /// + public readonly struct ColorDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDest(background, source, amount); + } + + /// + /// Applies the "LuminosityDest" straight-alpha composition equation. + /// + public readonly struct LuminosityDest : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityDest(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityDest(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityDest(background, source, amount); + } + + /// + /// Applies the "NormalDestAtop" straight-alpha composition equation. + /// + public readonly struct NormalDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalDestAtop(background, source, amount); + } + + /// + /// Applies the "MultiplyDestAtop" straight-alpha composition equation. + /// + public readonly struct MultiplyDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyDestAtop(background, source, amount); + } + + /// + /// Applies the "AddDestAtop" straight-alpha composition equation. + /// + public readonly struct AddDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddDestAtop(background, source, amount); + } + + /// + /// Applies the "SubtractDestAtop" straight-alpha composition equation. + /// + public readonly struct SubtractDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractDestAtop(background, source, amount); + } + + /// + /// Applies the "ScreenDestAtop" straight-alpha composition equation. + /// + public readonly struct ScreenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenDestAtop(background, source, amount); + } + + /// + /// Applies the "DarkenDestAtop" straight-alpha composition equation. + /// + public readonly struct DarkenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenDestAtop(background, source, amount); + } + + /// + /// Applies the "LightenDestAtop" straight-alpha composition equation. + /// + public readonly struct LightenDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenDestAtop(background, source, amount); + } + + /// + /// Applies the "OverlayDestAtop" straight-alpha composition equation. + /// + public readonly struct OverlayDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayDestAtop(background, source, amount); + } + + /// + /// Applies the "HardLightDestAtop" straight-alpha composition equation. + /// + public readonly struct HardLightDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestAtop" straight-alpha composition equation. + /// + public readonly struct ColorDodgeDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestAtop" straight-alpha composition equation. + /// + public readonly struct ColorBurnDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnDestAtop(background, source, amount); + } + + /// + /// Applies the "SoftLightDestAtop" straight-alpha composition equation. + /// + public readonly struct SoftLightDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightDestAtop(background, source, amount); + } + + /// + /// Applies the "DifferenceDestAtop" straight-alpha composition equation. + /// + public readonly struct DifferenceDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceDestAtop(background, source, amount); + } + + /// + /// Applies the "ExclusionDestAtop" straight-alpha composition equation. + /// + public readonly struct ExclusionDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionDestAtop(background, source, amount); + } + + /// + /// Applies the "HueDestAtop" straight-alpha composition equation. + /// + public readonly struct HueDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueDestAtop(background, source, amount); + } + + /// + /// Applies the "SaturationDestAtop" straight-alpha composition equation. + /// + public readonly struct SaturationDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationDestAtop(background, source, amount); + } + + /// + /// Applies the "ColorDestAtop" straight-alpha composition equation. + /// + public readonly struct ColorDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDestAtop(background, source, amount); + } + + /// + /// Applies the "LuminosityDestAtop" straight-alpha composition equation. + /// + public readonly struct LuminosityDestAtop : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityDestAtop(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityDestAtop(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityDestAtop(background, source, amount); + } + + /// + /// Applies the "NormalDestOver" straight-alpha composition equation. + /// + public readonly struct NormalDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalDestOver(background, source, amount); + } + + /// + /// Applies the "MultiplyDestOver" straight-alpha composition equation. + /// + public readonly struct MultiplyDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyDestOver(background, source, amount); + } + + /// + /// Applies the "AddDestOver" straight-alpha composition equation. + /// + public readonly struct AddDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddDestOver(background, source, amount); + } + + /// + /// Applies the "SubtractDestOver" straight-alpha composition equation. + /// + public readonly struct SubtractDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractDestOver(background, source, amount); + } + + /// + /// Applies the "ScreenDestOver" straight-alpha composition equation. + /// + public readonly struct ScreenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenDestOver(background, source, amount); + } + + /// + /// Applies the "DarkenDestOver" straight-alpha composition equation. + /// + public readonly struct DarkenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenDestOver(background, source, amount); + } + + /// + /// Applies the "LightenDestOver" straight-alpha composition equation. + /// + public readonly struct LightenDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenDestOver(background, source, amount); + } + + /// + /// Applies the "OverlayDestOver" straight-alpha composition equation. + /// + public readonly struct OverlayDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayDestOver(background, source, amount); + } + + /// + /// Applies the "HardLightDestOver" straight-alpha composition equation. + /// + public readonly struct HardLightDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightDestOver(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestOver" straight-alpha composition equation. + /// + public readonly struct ColorDodgeDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeDestOver(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestOver" straight-alpha composition equation. + /// + public readonly struct ColorBurnDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnDestOver(background, source, amount); + } + + /// + /// Applies the "SoftLightDestOver" straight-alpha composition equation. + /// + public readonly struct SoftLightDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightDestOver(background, source, amount); + } + + /// + /// Applies the "DifferenceDestOver" straight-alpha composition equation. + /// + public readonly struct DifferenceDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceDestOver(background, source, amount); + } + + /// + /// Applies the "ExclusionDestOver" straight-alpha composition equation. + /// + public readonly struct ExclusionDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionDestOver(background, source, amount); + } + + /// + /// Applies the "HueDestOver" straight-alpha composition equation. + /// + public readonly struct HueDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueDestOver(background, source, amount); + } + + /// + /// Applies the "SaturationDestOver" straight-alpha composition equation. + /// + public readonly struct SaturationDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationDestOver(background, source, amount); + } + + /// + /// Applies the "ColorDestOver" straight-alpha composition equation. + /// + public readonly struct ColorDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDestOver(background, source, amount); + } + + /// + /// Applies the "LuminosityDestOver" straight-alpha composition equation. + /// + public readonly struct LuminosityDestOver : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityDestOver(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityDestOver(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityDestOver(background, source, amount); + } + + /// + /// Applies the "NormalDestIn" straight-alpha composition equation. + /// + public readonly struct NormalDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalDestIn(background, source, amount); + } + + /// + /// Applies the "MultiplyDestIn" straight-alpha composition equation. + /// + public readonly struct MultiplyDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyDestIn(background, source, amount); + } + + /// + /// Applies the "AddDestIn" straight-alpha composition equation. + /// + public readonly struct AddDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddDestIn(background, source, amount); + } + + /// + /// Applies the "SubtractDestIn" straight-alpha composition equation. + /// + public readonly struct SubtractDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractDestIn(background, source, amount); + } + + /// + /// Applies the "ScreenDestIn" straight-alpha composition equation. + /// + public readonly struct ScreenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenDestIn(background, source, amount); + } + + /// + /// Applies the "DarkenDestIn" straight-alpha composition equation. + /// + public readonly struct DarkenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenDestIn(background, source, amount); + } + + /// + /// Applies the "LightenDestIn" straight-alpha composition equation. + /// + public readonly struct LightenDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenDestIn(background, source, amount); + } + + /// + /// Applies the "OverlayDestIn" straight-alpha composition equation. + /// + public readonly struct OverlayDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayDestIn(background, source, amount); + } + + /// + /// Applies the "HardLightDestIn" straight-alpha composition equation. + /// + public readonly struct HardLightDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightDestIn(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestIn" straight-alpha composition equation. + /// + public readonly struct ColorDodgeDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeDestIn(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestIn" straight-alpha composition equation. + /// + public readonly struct ColorBurnDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnDestIn(background, source, amount); + } + + /// + /// Applies the "SoftLightDestIn" straight-alpha composition equation. + /// + public readonly struct SoftLightDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightDestIn(background, source, amount); + } + + /// + /// Applies the "DifferenceDestIn" straight-alpha composition equation. + /// + public readonly struct DifferenceDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceDestIn(background, source, amount); + } + + /// + /// Applies the "ExclusionDestIn" straight-alpha composition equation. + /// + public readonly struct ExclusionDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionDestIn(background, source, amount); + } + + /// + /// Applies the "HueDestIn" straight-alpha composition equation. + /// + public readonly struct HueDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueDestIn(background, source, amount); + } + + /// + /// Applies the "SaturationDestIn" straight-alpha composition equation. + /// + public readonly struct SaturationDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationDestIn(background, source, amount); + } + + /// + /// Applies the "ColorDestIn" straight-alpha composition equation. + /// + public readonly struct ColorDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDestIn(background, source, amount); + } + + /// + /// Applies the "LuminosityDestIn" straight-alpha composition equation. + /// + public readonly struct LuminosityDestIn : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityDestIn(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityDestIn(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityDestIn(background, source, amount); + } + + /// + /// Applies the "NormalDestOut" straight-alpha composition equation. + /// + public readonly struct NormalDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalDestOut(background, source, amount); + } + + /// + /// Applies the "MultiplyDestOut" straight-alpha composition equation. + /// + public readonly struct MultiplyDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyDestOut(background, source, amount); + } + + /// + /// Applies the "AddDestOut" straight-alpha composition equation. + /// + public readonly struct AddDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddDestOut(background, source, amount); + } + + /// + /// Applies the "SubtractDestOut" straight-alpha composition equation. + /// + public readonly struct SubtractDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractDestOut(background, source, amount); + } + + /// + /// Applies the "ScreenDestOut" straight-alpha composition equation. + /// + public readonly struct ScreenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenDestOut(background, source, amount); + } + + /// + /// Applies the "DarkenDestOut" straight-alpha composition equation. + /// + public readonly struct DarkenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenDestOut(background, source, amount); + } + + /// + /// Applies the "LightenDestOut" straight-alpha composition equation. + /// + public readonly struct LightenDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenDestOut(background, source, amount); + } + + /// + /// Applies the "OverlayDestOut" straight-alpha composition equation. + /// + public readonly struct OverlayDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayDestOut(background, source, amount); + } + + /// + /// Applies the "HardLightDestOut" straight-alpha composition equation. + /// + public readonly struct HardLightDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightDestOut(background, source, amount); + } + + /// + /// Applies the "ColorDodgeDestOut" straight-alpha composition equation. + /// + public readonly struct ColorDodgeDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeDestOut(background, source, amount); + } + + /// + /// Applies the "ColorBurnDestOut" straight-alpha composition equation. + /// + public readonly struct ColorBurnDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnDestOut(background, source, amount); + } + + /// + /// Applies the "SoftLightDestOut" straight-alpha composition equation. + /// + public readonly struct SoftLightDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightDestOut(background, source, amount); + } + + /// + /// Applies the "DifferenceDestOut" straight-alpha composition equation. + /// + public readonly struct DifferenceDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceDestOut(background, source, amount); + } + + /// + /// Applies the "ExclusionDestOut" straight-alpha composition equation. + /// + public readonly struct ExclusionDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionDestOut(background, source, amount); + } + + /// + /// Applies the "HueDestOut" straight-alpha composition equation. + /// + public readonly struct HueDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueDestOut(background, source, amount); + } + + /// + /// Applies the "SaturationDestOut" straight-alpha composition equation. + /// + public readonly struct SaturationDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationDestOut(background, source, amount); + } + + /// + /// Applies the "ColorDestOut" straight-alpha composition equation. + /// + public readonly struct ColorDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDestOut(background, source, amount); + } + + /// + /// Applies the "LuminosityDestOut" straight-alpha composition equation. + /// + public readonly struct LuminosityDestOut : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityDestOut(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityDestOut(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityDestOut(background, source, amount); + } + + /// + /// Applies the "NormalClear" straight-alpha composition equation. + /// + public readonly struct NormalClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalClear(background, source, amount); + } + + /// + /// Applies the "MultiplyClear" straight-alpha composition equation. + /// + public readonly struct MultiplyClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyClear(background, source, amount); + } + + /// + /// Applies the "AddClear" straight-alpha composition equation. + /// + public readonly struct AddClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddClear(background, source, amount); + } + + /// + /// Applies the "SubtractClear" straight-alpha composition equation. + /// + public readonly struct SubtractClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractClear(background, source, amount); + } + + /// + /// Applies the "ScreenClear" straight-alpha composition equation. + /// + public readonly struct ScreenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenClear(background, source, amount); + } + + /// + /// Applies the "DarkenClear" straight-alpha composition equation. + /// + public readonly struct DarkenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenClear(background, source, amount); + } + + /// + /// Applies the "LightenClear" straight-alpha composition equation. + /// + public readonly struct LightenClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenClear(background, source, amount); + } + + /// + /// Applies the "OverlayClear" straight-alpha composition equation. + /// + public readonly struct OverlayClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayClear(background, source, amount); + } + + /// + /// Applies the "HardLightClear" straight-alpha composition equation. + /// + public readonly struct HardLightClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightClear(background, source, amount); + } + + /// + /// Applies the "ColorDodgeClear" straight-alpha composition equation. + /// + public readonly struct ColorDodgeClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeClear(background, source, amount); + } + + /// + /// Applies the "ColorBurnClear" straight-alpha composition equation. + /// + public readonly struct ColorBurnClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnClear(background, source, amount); + } + + /// + /// Applies the "SoftLightClear" straight-alpha composition equation. + /// + public readonly struct SoftLightClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightClear(background, source, amount); + } + + /// + /// Applies the "DifferenceClear" straight-alpha composition equation. + /// + public readonly struct DifferenceClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceClear(background, source, amount); + } + + /// + /// Applies the "ExclusionClear" straight-alpha composition equation. + /// + public readonly struct ExclusionClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionClear(background, source, amount); + } + + /// + /// Applies the "HueClear" straight-alpha composition equation. + /// + public readonly struct HueClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueClear(background, source, amount); + } + + /// + /// Applies the "SaturationClear" straight-alpha composition equation. + /// + public readonly struct SaturationClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationClear(background, source, amount); + } + + /// + /// Applies the "ColorClear" straight-alpha composition equation. + /// + public readonly struct ColorClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorClear(background, source, amount); + } + + /// + /// Applies the "LuminosityClear" straight-alpha composition equation. + /// + public readonly struct LuminosityClear : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityClear(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityClear(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityClear(background, source, amount); + } + + /// + /// Applies the "NormalXor" straight-alpha composition equation. + /// + public readonly struct NormalXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalXor(background, source, amount); + } + + /// + /// Applies the "MultiplyXor" straight-alpha composition equation. + /// + public readonly struct MultiplyXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyXor(background, source, amount); + } + + /// + /// Applies the "AddXor" straight-alpha composition equation. + /// + public readonly struct AddXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddXor(background, source, amount); + } + + /// + /// Applies the "SubtractXor" straight-alpha composition equation. + /// + public readonly struct SubtractXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractXor(background, source, amount); + } + + /// + /// Applies the "ScreenXor" straight-alpha composition equation. + /// + public readonly struct ScreenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenXor(background, source, amount); + } + + /// + /// Applies the "DarkenXor" straight-alpha composition equation. + /// + public readonly struct DarkenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenXor(background, source, amount); + } + + /// + /// Applies the "LightenXor" straight-alpha composition equation. + /// + public readonly struct LightenXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenXor(background, source, amount); + } + + /// + /// Applies the "OverlayXor" straight-alpha composition equation. + /// + public readonly struct OverlayXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayXor(background, source, amount); + } + + /// + /// Applies the "HardLightXor" straight-alpha composition equation. + /// + public readonly struct HardLightXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightXor(background, source, amount); + } + + /// + /// Applies the "ColorDodgeXor" straight-alpha composition equation. + /// + public readonly struct ColorDodgeXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgeXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgeXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgeXor(background, source, amount); + } + + /// + /// Applies the "ColorBurnXor" straight-alpha composition equation. + /// + public readonly struct ColorBurnXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnXor(background, source, amount); + } + + /// + /// Applies the "SoftLightXor" straight-alpha composition equation. + /// + public readonly struct SoftLightXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightXor(background, source, amount); + } + + /// + /// Applies the "DifferenceXor" straight-alpha composition equation. + /// + public readonly struct DifferenceXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferenceXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferenceXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferenceXor(background, source, amount); + } + + /// + /// Applies the "ExclusionXor" straight-alpha composition equation. + /// + public readonly struct ExclusionXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionXor(background, source, amount); + } + + /// + /// Applies the "HueXor" straight-alpha composition equation. + /// + public readonly struct HueXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HueXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HueXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HueXor(background, source, amount); + } + + /// + /// Applies the "SaturationXor" straight-alpha composition equation. + /// + public readonly struct SaturationXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationXor(background, source, amount); + } + + /// + /// Applies the "ColorXor" straight-alpha composition equation. + /// + public readonly struct ColorXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorXor(background, source, amount); + } + + /// + /// Applies the "LuminosityXor" straight-alpha composition equation. + /// + public readonly struct LuminosityXor : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityXor(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityXor(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityXor(background, source, amount); + } + + /// + /// Applies the "NormalPlus" straight-alpha composition equation. + /// + public readonly struct NormalPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.NormalPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.NormalPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.NormalPlus(background, source, amount); + } + + /// + /// Applies the "MultiplyPlus" straight-alpha composition equation. + /// + public readonly struct MultiplyPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.MultiplyPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.MultiplyPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.MultiplyPlus(background, source, amount); + } + + /// + /// Applies the "AddPlus" straight-alpha composition equation. + /// + public readonly struct AddPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.AddPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.AddPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.AddPlus(background, source, amount); + } + + /// + /// Applies the "SubtractPlus" straight-alpha composition equation. + /// + public readonly struct SubtractPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SubtractPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SubtractPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SubtractPlus(background, source, amount); + } + + /// + /// Applies the "ScreenPlus" straight-alpha composition equation. + /// + public readonly struct ScreenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ScreenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ScreenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ScreenPlus(background, source, amount); + } + + /// + /// Applies the "DarkenPlus" straight-alpha composition equation. + /// + public readonly struct DarkenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DarkenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DarkenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DarkenPlus(background, source, amount); + } + + /// + /// Applies the "LightenPlus" straight-alpha composition equation. + /// + public readonly struct LightenPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LightenPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LightenPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LightenPlus(background, source, amount); + } + + /// + /// Applies the "OverlayPlus" straight-alpha composition equation. + /// + public readonly struct OverlayPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.OverlayPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.OverlayPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.OverlayPlus(background, source, amount); + } + + /// + /// Applies the "HardLightPlus" straight-alpha composition equation. + /// + public readonly struct HardLightPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HardLightPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HardLightPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HardLightPlus(background, source, amount); + } + + /// + /// Applies the "ColorDodgePlus" straight-alpha composition equation. + /// + public readonly struct ColorDodgePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorDodgePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorDodgePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorDodgePlus(background, source, amount); + } + + /// + /// Applies the "ColorBurnPlus" straight-alpha composition equation. + /// + public readonly struct ColorBurnPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorBurnPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorBurnPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorBurnPlus(background, source, amount); + } + + /// + /// Applies the "SoftLightPlus" straight-alpha composition equation. + /// + public readonly struct SoftLightPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SoftLightPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SoftLightPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SoftLightPlus(background, source, amount); + } + + /// + /// Applies the "DifferencePlus" straight-alpha composition equation. + /// + public readonly struct DifferencePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.DifferencePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.DifferencePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.DifferencePlus(background, source, amount); + } + + /// + /// Applies the "ExclusionPlus" straight-alpha composition equation. + /// + public readonly struct ExclusionPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ExclusionPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ExclusionPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ExclusionPlus(background, source, amount); + } + + /// + /// Applies the "HuePlus" straight-alpha composition equation. + /// + public readonly struct HuePlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.HuePlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.HuePlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.HuePlus(background, source, amount); + } + + /// + /// Applies the "SaturationPlus" straight-alpha composition equation. + /// + public readonly struct SaturationPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.SaturationPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.SaturationPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.SaturationPlus(background, source, amount); + } + + /// + /// Applies the "ColorPlus" straight-alpha composition equation. + /// + public readonly struct ColorPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.ColorPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.ColorPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.ColorPlus(background, source, amount); + } + + /// + /// Applies the "LuminosityPlus" straight-alpha composition equation. + /// + public readonly struct LuminosityPlus : IPixelBlenderOperator + { + /// + public static bool IsAssociatedAlpha => false; + + /// + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.LuminosityPlus(background, source, amount); + + /// + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.LuminosityPlus(background, source, amount); + + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.LuminosityPlus(background, source, amount); + } + +} + +/// +/// Provides straight-alpha pixel blenders for each color and alpha composition pair. +/// +/// The destination pixel format. +internal static class DefaultPixelBlenders + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the shared blender for the "NormalSrc" composition equation. + /// + public static PixelBlender NormalSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrc" composition equation. + /// + public static PixelBlender MultiplySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrc" composition equation. + /// + public static PixelBlender AddSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrc" composition equation. + /// + public static PixelBlender SubtractSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrc" composition equation. + /// + public static PixelBlender ScreenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrc" composition equation. + /// + public static PixelBlender DarkenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrc" composition equation. + /// + public static PixelBlender LightenSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrc" composition equation. + /// + public static PixelBlender OverlaySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrc" composition equation. + /// + public static PixelBlender HardLightSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrc" composition equation. + /// + public static PixelBlender ColorDodgeSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrc" composition equation. + /// + public static PixelBlender ColorBurnSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrc" composition equation. + /// + public static PixelBlender SoftLightSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrc" composition equation. + /// + public static PixelBlender DifferenceSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrc" composition equation. + /// + public static PixelBlender ExclusionSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrc" composition equation. + /// + public static PixelBlender HueSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrc" composition equation. + /// + public static PixelBlender SaturationSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrc" composition equation. + /// + public static PixelBlender ColorSrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrc" composition equation. + /// + public static PixelBlender LuminositySrc => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcAtop" composition equation. + /// + public static PixelBlender NormalSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcAtop" composition equation. + /// + public static PixelBlender MultiplySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcAtop" composition equation. + /// + public static PixelBlender AddSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcAtop" composition equation. + /// + public static PixelBlender SubtractSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcAtop" composition equation. + /// + public static PixelBlender ScreenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcAtop" composition equation. + /// + public static PixelBlender DarkenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcAtop" composition equation. + /// + public static PixelBlender LightenSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcAtop" composition equation. + /// + public static PixelBlender OverlaySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcAtop" composition equation. + /// + public static PixelBlender HardLightSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcAtop" composition equation. + /// + public static PixelBlender ColorDodgeSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcAtop" composition equation. + /// + public static PixelBlender ColorBurnSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcAtop" composition equation. + /// + public static PixelBlender SoftLightSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcAtop" composition equation. + /// + public static PixelBlender DifferenceSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcAtop" composition equation. + /// + public static PixelBlender ExclusionSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcAtop" composition equation. + /// + public static PixelBlender HueSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcAtop" composition equation. + /// + public static PixelBlender SaturationSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcAtop" composition equation. + /// + public static PixelBlender ColorSrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcAtop" composition equation. + /// + public static PixelBlender LuminositySrcAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcOver" composition equation. + /// + public static PixelBlender NormalSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcOver" composition equation. + /// + public static PixelBlender MultiplySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcOver" composition equation. + /// + public static PixelBlender AddSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcOver" composition equation. + /// + public static PixelBlender SubtractSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcOver" composition equation. + /// + public static PixelBlender ScreenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcOver" composition equation. + /// + public static PixelBlender DarkenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcOver" composition equation. + /// + public static PixelBlender LightenSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcOver" composition equation. + /// + public static PixelBlender OverlaySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcOver" composition equation. + /// + public static PixelBlender HardLightSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcOver" composition equation. + /// + public static PixelBlender ColorDodgeSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcOver" composition equation. + /// + public static PixelBlender ColorBurnSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcOver" composition equation. + /// + public static PixelBlender SoftLightSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcOver" composition equation. + /// + public static PixelBlender DifferenceSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcOver" composition equation. + /// + public static PixelBlender ExclusionSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcOver" composition equation. + /// + public static PixelBlender HueSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcOver" composition equation. + /// + public static PixelBlender SaturationSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcOver" composition equation. + /// + public static PixelBlender ColorSrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcOver" composition equation. + /// + public static PixelBlender LuminositySrcOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcIn" composition equation. + /// + public static PixelBlender NormalSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcIn" composition equation. + /// + public static PixelBlender MultiplySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcIn" composition equation. + /// + public static PixelBlender AddSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcIn" composition equation. + /// + public static PixelBlender SubtractSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcIn" composition equation. + /// + public static PixelBlender ScreenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcIn" composition equation. + /// + public static PixelBlender DarkenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcIn" composition equation. + /// + public static PixelBlender LightenSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcIn" composition equation. + /// + public static PixelBlender OverlaySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcIn" composition equation. + /// + public static PixelBlender HardLightSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcIn" composition equation. + /// + public static PixelBlender ColorDodgeSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcIn" composition equation. + /// + public static PixelBlender ColorBurnSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcIn" composition equation. + /// + public static PixelBlender SoftLightSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcIn" composition equation. + /// + public static PixelBlender DifferenceSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcIn" composition equation. + /// + public static PixelBlender ExclusionSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcIn" composition equation. + /// + public static PixelBlender HueSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcIn" composition equation. + /// + public static PixelBlender SaturationSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcIn" composition equation. + /// + public static PixelBlender ColorSrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcIn" composition equation. + /// + public static PixelBlender LuminositySrcIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalSrcOut" composition equation. + /// + public static PixelBlender NormalSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplySrcOut" composition equation. + /// + public static PixelBlender MultiplySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddSrcOut" composition equation. + /// + public static PixelBlender AddSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractSrcOut" composition equation. + /// + public static PixelBlender SubtractSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenSrcOut" composition equation. + /// + public static PixelBlender ScreenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenSrcOut" composition equation. + /// + public static PixelBlender DarkenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenSrcOut" composition equation. + /// + public static PixelBlender LightenSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlaySrcOut" composition equation. + /// + public static PixelBlender OverlaySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightSrcOut" composition equation. + /// + public static PixelBlender HardLightSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeSrcOut" composition equation. + /// + public static PixelBlender ColorDodgeSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnSrcOut" composition equation. + /// + public static PixelBlender ColorBurnSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightSrcOut" composition equation. + /// + public static PixelBlender SoftLightSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceSrcOut" composition equation. + /// + public static PixelBlender DifferenceSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionSrcOut" composition equation. + /// + public static PixelBlender ExclusionSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueSrcOut" composition equation. + /// + public static PixelBlender HueSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationSrcOut" composition equation. + /// + public static PixelBlender SaturationSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorSrcOut" composition equation. + /// + public static PixelBlender ColorSrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminositySrcOut" composition equation. + /// + public static PixelBlender LuminositySrcOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDest" composition equation. + /// + public static PixelBlender NormalDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDest" composition equation. + /// + public static PixelBlender MultiplyDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDest" composition equation. + /// + public static PixelBlender AddDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDest" composition equation. + /// + public static PixelBlender SubtractDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDest" composition equation. + /// + public static PixelBlender ScreenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDest" composition equation. + /// + public static PixelBlender DarkenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDest" composition equation. + /// + public static PixelBlender LightenDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDest" composition equation. + /// + public static PixelBlender OverlayDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDest" composition equation. + /// + public static PixelBlender HardLightDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDest" composition equation. + /// + public static PixelBlender ColorDodgeDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDest" composition equation. + /// + public static PixelBlender ColorBurnDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDest" composition equation. + /// + public static PixelBlender SoftLightDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDest" composition equation. + /// + public static PixelBlender DifferenceDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDest" composition equation. + /// + public static PixelBlender ExclusionDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDest" composition equation. + /// + public static PixelBlender HueDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDest" composition equation. + /// + public static PixelBlender SaturationDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDest" composition equation. + /// + public static PixelBlender ColorDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDest" composition equation. + /// + public static PixelBlender LuminosityDest => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestAtop" composition equation. + /// + public static PixelBlender NormalDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestAtop" composition equation. + /// + public static PixelBlender MultiplyDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestAtop" composition equation. + /// + public static PixelBlender AddDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestAtop" composition equation. + /// + public static PixelBlender SubtractDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestAtop" composition equation. + /// + public static PixelBlender ScreenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestAtop" composition equation. + /// + public static PixelBlender DarkenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestAtop" composition equation. + /// + public static PixelBlender LightenDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestAtop" composition equation. + /// + public static PixelBlender OverlayDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestAtop" composition equation. + /// + public static PixelBlender HardLightDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestAtop" composition equation. + /// + public static PixelBlender ColorDodgeDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestAtop" composition equation. + /// + public static PixelBlender ColorBurnDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestAtop" composition equation. + /// + public static PixelBlender SoftLightDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestAtop" composition equation. + /// + public static PixelBlender DifferenceDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestAtop" composition equation. + /// + public static PixelBlender ExclusionDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestAtop" composition equation. + /// + public static PixelBlender HueDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestAtop" composition equation. + /// + public static PixelBlender SaturationDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestAtop" composition equation. + /// + public static PixelBlender ColorDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestAtop" composition equation. + /// + public static PixelBlender LuminosityDestAtop => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestOver" composition equation. + /// + public static PixelBlender NormalDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestOver" composition equation. + /// + public static PixelBlender MultiplyDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestOver" composition equation. + /// + public static PixelBlender AddDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestOver" composition equation. + /// + public static PixelBlender SubtractDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestOver" composition equation. + /// + public static PixelBlender ScreenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestOver" composition equation. + /// + public static PixelBlender DarkenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestOver" composition equation. + /// + public static PixelBlender LightenDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestOver" composition equation. + /// + public static PixelBlender OverlayDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestOver" composition equation. + /// + public static PixelBlender HardLightDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestOver" composition equation. + /// + public static PixelBlender ColorDodgeDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestOver" composition equation. + /// + public static PixelBlender ColorBurnDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestOver" composition equation. + /// + public static PixelBlender SoftLightDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestOver" composition equation. + /// + public static PixelBlender DifferenceDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestOver" composition equation. + /// + public static PixelBlender ExclusionDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestOver" composition equation. + /// + public static PixelBlender HueDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestOver" composition equation. + /// + public static PixelBlender SaturationDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestOver" composition equation. + /// + public static PixelBlender ColorDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestOver" composition equation. + /// + public static PixelBlender LuminosityDestOver => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestIn" composition equation. + /// + public static PixelBlender NormalDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestIn" composition equation. + /// + public static PixelBlender MultiplyDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestIn" composition equation. + /// + public static PixelBlender AddDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestIn" composition equation. + /// + public static PixelBlender SubtractDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestIn" composition equation. + /// + public static PixelBlender ScreenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestIn" composition equation. + /// + public static PixelBlender DarkenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestIn" composition equation. + /// + public static PixelBlender LightenDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestIn" composition equation. + /// + public static PixelBlender OverlayDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestIn" composition equation. + /// + public static PixelBlender HardLightDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestIn" composition equation. + /// + public static PixelBlender ColorDodgeDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestIn" composition equation. + /// + public static PixelBlender ColorBurnDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestIn" composition equation. + /// + public static PixelBlender SoftLightDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestIn" composition equation. + /// + public static PixelBlender DifferenceDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestIn" composition equation. + /// + public static PixelBlender ExclusionDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestIn" composition equation. + /// + public static PixelBlender HueDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestIn" composition equation. + /// + public static PixelBlender SaturationDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestIn" composition equation. + /// + public static PixelBlender ColorDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestIn" composition equation. + /// + public static PixelBlender LuminosityDestIn => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalDestOut" composition equation. + /// + public static PixelBlender NormalDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyDestOut" composition equation. + /// + public static PixelBlender MultiplyDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddDestOut" composition equation. + /// + public static PixelBlender AddDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractDestOut" composition equation. + /// + public static PixelBlender SubtractDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenDestOut" composition equation. + /// + public static PixelBlender ScreenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenDestOut" composition equation. + /// + public static PixelBlender DarkenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenDestOut" composition equation. + /// + public static PixelBlender LightenDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayDestOut" composition equation. + /// + public static PixelBlender OverlayDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightDestOut" composition equation. + /// + public static PixelBlender HardLightDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeDestOut" composition equation. + /// + public static PixelBlender ColorDodgeDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnDestOut" composition equation. + /// + public static PixelBlender ColorBurnDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightDestOut" composition equation. + /// + public static PixelBlender SoftLightDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceDestOut" composition equation. + /// + public static PixelBlender DifferenceDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionDestOut" composition equation. + /// + public static PixelBlender ExclusionDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueDestOut" composition equation. + /// + public static PixelBlender HueDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationDestOut" composition equation. + /// + public static PixelBlender SaturationDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDestOut" composition equation. + /// + public static PixelBlender ColorDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityDestOut" composition equation. + /// + public static PixelBlender LuminosityDestOut => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalClear" composition equation. + /// + public static PixelBlender NormalClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyClear" composition equation. + /// + public static PixelBlender MultiplyClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddClear" composition equation. + /// + public static PixelBlender AddClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractClear" composition equation. + /// + public static PixelBlender SubtractClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenClear" composition equation. + /// + public static PixelBlender ScreenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenClear" composition equation. + /// + public static PixelBlender DarkenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenClear" composition equation. + /// + public static PixelBlender LightenClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayClear" composition equation. + /// + public static PixelBlender OverlayClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightClear" composition equation. + /// + public static PixelBlender HardLightClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeClear" composition equation. + /// + public static PixelBlender ColorDodgeClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnClear" composition equation. + /// + public static PixelBlender ColorBurnClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightClear" composition equation. + /// + public static PixelBlender SoftLightClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceClear" composition equation. + /// + public static PixelBlender DifferenceClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionClear" composition equation. + /// + public static PixelBlender ExclusionClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueClear" composition equation. + /// + public static PixelBlender HueClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationClear" composition equation. + /// + public static PixelBlender SaturationClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorClear" composition equation. + /// + public static PixelBlender ColorClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityClear" composition equation. + /// + public static PixelBlender LuminosityClear => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalXor" composition equation. + /// + public static PixelBlender NormalXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyXor" composition equation. + /// + public static PixelBlender MultiplyXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddXor" composition equation. + /// + public static PixelBlender AddXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractXor" composition equation. + /// + public static PixelBlender SubtractXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenXor" composition equation. + /// + public static PixelBlender ScreenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenXor" composition equation. + /// + public static PixelBlender DarkenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenXor" composition equation. + /// + public static PixelBlender LightenXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayXor" composition equation. + /// + public static PixelBlender OverlayXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightXor" composition equation. + /// + public static PixelBlender HardLightXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgeXor" composition equation. + /// + public static PixelBlender ColorDodgeXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnXor" composition equation. + /// + public static PixelBlender ColorBurnXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightXor" composition equation. + /// + public static PixelBlender SoftLightXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferenceXor" composition equation. + /// + public static PixelBlender DifferenceXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionXor" composition equation. + /// + public static PixelBlender ExclusionXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HueXor" composition equation. + /// + public static PixelBlender HueXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationXor" composition equation. + /// + public static PixelBlender SaturationXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorXor" composition equation. + /// + public static PixelBlender ColorXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityXor" composition equation. + /// + public static PixelBlender LuminosityXor => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "NormalPlus" composition equation. + /// + public static PixelBlender NormalPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "MultiplyPlus" composition equation. + /// + public static PixelBlender MultiplyPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "AddPlus" composition equation. + /// + public static PixelBlender AddPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SubtractPlus" composition equation. + /// + public static PixelBlender SubtractPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ScreenPlus" composition equation. + /// + public static PixelBlender ScreenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DarkenPlus" composition equation. + /// + public static PixelBlender DarkenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LightenPlus" composition equation. + /// + public static PixelBlender LightenPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "OverlayPlus" composition equation. + /// + public static PixelBlender OverlayPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HardLightPlus" composition equation. + /// + public static PixelBlender HardLightPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorDodgePlus" composition equation. + /// + public static PixelBlender ColorDodgePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorBurnPlus" composition equation. + /// + public static PixelBlender ColorBurnPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SoftLightPlus" composition equation. + /// + public static PixelBlender SoftLightPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "DifferencePlus" composition equation. + /// + public static PixelBlender DifferencePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ExclusionPlus" composition equation. + /// + public static PixelBlender ExclusionPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "HuePlus" composition equation. + /// + public static PixelBlender HuePlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "SaturationPlus" composition equation. + /// + public static PixelBlender SaturationPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "ColorPlus" composition equation. + /// + public static PixelBlender ColorPlus => PixelBlender.Instance; + + /// + /// Gets the shared blender for the "LuminosityPlus" composition equation. + /// + public static PixelBlender LuminosityPlus => PixelBlender.Instance; + } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index da6208eaa2..8dc9fe5648 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -4,39 +4,19 @@ #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> // 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.PixelFormats.PixelBlenders; -/// -/// Collection of Porter Duff alpha blending functions applying different composition models. -/// -/// -/// These functions are designed to be a general solution for all color cases, -/// that is, they take in account the alpha value of both the backdrop -/// and source, and there's no need to alpha-premultiply neither the backdrop -/// nor the source. -/// Note there are faster functions for when the backdrop color is known -/// to be opaque -/// -internal static class DefaultPixelBlenders - where TPixel : unmanaged, IPixel -{ - <# -var composers = new []{ +var composers = new [] +{ "Src", "SrcAtop", "SrcOver", @@ -49,9 +29,11 @@ var composers = new []{ "DestOut", "Clear", "Xor", + "Plus", }; -var blenders = new []{ +var blenders = new [] +{ "Normal", "Multiply", "Add", @@ -60,122 +42,78 @@ var blenders = new []{ "Darken", "Lighten", "Overlay", - "HardLight" + "HardLight", + "ColorDodge", + "ColorBurn", + "SoftLight", + "Difference", + "Exclusion", + "Hue", + "Saturation", + "Color", + "Luminosity", }; - - foreach(var composer in composers) { - foreach(var blender in blenders) { - - var blender_composer= $"{blender}{composer}"; +#> +/// +/// Provides the straight-alpha equations consumed by the shared pixel-blending traversal. +/// +internal static class DefaultPixelBlenderOperators +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; #> /// - /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// Applies the "<#= blenderComposer #>" straight-alpha composition equation. /// - public class <#= blender_composer#> : PixelBlender + public readonly struct <#= blenderComposer #> : IPixelBlenderOperator { - /// - /// Gets the static instance of this blender. - /// - public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); - /// - 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; - } + public static bool IsAssociatedAlpha => false; /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = Numerics.Clamp(amount, 0, 1); - - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); - - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - Vector256 opacity = Vector256.Create(amount); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - } + public static Vector4 Invoke(Vector4 background, Vector4 source, float amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - if (Avx2.IsSupported && destination.Length >= 2) - { - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + public static Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); - ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); - ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref float amountBase = ref MemoryMarshal.GetReference(amount); - - Vector256 vOne = Vector256.Create(1F); - - while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) - { - // We need to create a Vector256 containing the current and next amount values - // taking up each half of the Vector256 and then clamp them. - Vector256 opacity = Vector256.Create( - Vector128.Create(amountBase), - Vector128.Create(Unsafe.Add(ref amountBase, 1))); - opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); - - destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); - destinationBase = ref Unsafe.Add(ref destinationBase, 1); - backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); - sourceBase = ref Unsafe.Add(ref sourceBase, 1); - amountBase = ref Unsafe.Add(ref amountBase, 2); - } - - if (Numerics.Modulo2(destination.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - int i = destination.Length - 1; - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - else - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); - } - } - } + /// + public static Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount) + => PorterDuffFunctions.<#= blenderComposer #>(background, source, amount); } <# } } +#> +} + +/// +/// Provides straight-alpha pixel blenders for each color and alpha composition pair. +/// +/// The destination pixel format. +internal static class DefaultPixelBlenders + where TPixel : unmanaged, IPixel +{ +<# +foreach (var composer in composers) +{ + foreach (var blender in blenders) + { + var blenderComposer = $"{blender}{composer}"; +#> + /// + /// Gets the shared blender for the "<#= blenderComposer #>" composition equation. + /// + public static PixelBlender <#= blenderComposer #> => PixelBlender>.Instance; +<# + } +} #> } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs b/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs new file mode 100644 index 0000000000..4b06e95233 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/IPixelBlenderOperator.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Defines a Porter-Duff equation that can be applied by the shared pixel-blending traversal. +/// +internal interface IPixelBlenderOperator +{ + /// + /// Gets a value indicating whether pixel conversion uses associated-alpha vectors. + /// + public static abstract bool IsAssociatedAlpha { get; } + + /// + /// Blends one pixel represented by four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity in the range 0 through 1. + /// The blended RGBA lanes. + public static abstract Vector4 Invoke(Vector4 background, Vector4 source, float amount); + + /// + /// Blends two pixels represented by two consecutive groups of four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity repeated across each pixel's four lanes. + /// The blended RGBA lanes. + public static abstract Vector256 Invoke(Vector256 background, Vector256 source, Vector256 amount); + + /// + /// Blends four pixels represented by four consecutive groups of four RGBA lanes. + /// + /// The background RGBA lanes. + /// The source RGBA lanes. + /// The source opacity repeated across each pixel's four lanes. + /// The blended RGBA lanes. + public static abstract Vector512 Invoke(Vector512 background, Vector512 source, Vector512 amount); +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs new file mode 100644 index 0000000000..d2b26db802 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PixelBlender{TPixel,TOperator}.cs @@ -0,0 +1,614 @@ +// 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; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +/// +/// Applies a statically selected Porter-Duff equation across pixel-vector rows. +/// +/// The destination pixel format. +/// The Porter-Duff equation. +internal sealed class PixelBlender : PixelBlender + where TPixel : unmanaged, IPixel + where TOperator : struct, IPixelBlenderOperator +{ + /// + /// Initializes a new instance of the class. + /// + private PixelBlender() + { + } + + /// + /// Gets the shared blender instance for this exact pixel and equation combination. + /// + // Keeping the singleton on the already-required closed blender type preserves per-mode lazy initialization + // without introducing another NativeAOT holder type or eagerly allocating every blender for a pixel format. + public static PixelBlender Instance { get; } = new PixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + // The operator type uniquely determines the alpha representation. NativeAOT therefore needs only the + // pixel/operator closure, while the JIT can remove this static choice from the generated hot path. + Vector4 backgroundVector = TOperator.IsAssociatedAlpha + ? background.ToAssociatedScaledVector4() + : background.ToUnassociatedScaledVector4(); + Vector4 sourceVector = TOperator.IsAssociatedAlpha + ? source.ToAssociatedScaledVector4() + : source.ToUnassociatedScaledVector4(); + + Vector4 result = TOperator.Invoke(backgroundVector, sourceVector, Numerics.Clamp(amount, 0, 1F)); + + return TOperator.IsAssociatedAlpha + ? TPixel.FromAssociatedScaledVector4(result) + : TPixel.FromUnassociatedScaledVector4(result); + } + + /// + protected sealed override void ToBlendVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + { + PixelConversionModifiers modifiers = TOperator.IsAssociatedAlpha + ? PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply + : PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply; + + PixelOperations.Instance.ToVector4(configuration, source, destination, modifiers); + } + + /// + protected sealed override Vector4 ToBlendVector4(TPixel source) + => TOperator.IsAssociatedAlpha + ? source.ToAssociatedScaledVector4() + : source.ToUnassociatedScaledVector4(); + + /// + protected sealed override void FromBlendVector4( + Configuration configuration, + Span source, + Span destination) + { + PixelConversionModifiers modifiers = TOperator.IsAssociatedAlpha + ? PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply + : PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply; + + PixelOperations.Instance.FromVector4Destructive(configuration, source, destination, modifiers); + } + + /// + protected sealed override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + // Public entry points validate the row lengths, so all three references can advance in lockstep. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + // A 512-bit register holds four complete RGBA pixels in [R,G,B,A] groups. + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), Unsafe.Add(ref sourceBase, i), amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + // A 256-bit register holds two complete RGBA pixels in [R,G,B,A] groups. + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), Unsafe.Add(ref sourceBase, i), amountVector); + } + + scalarStart = vectorCount * 2; + } + + // Vector4 is both the scalar pixel representation and the portable SIMD fallback. + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke(Unsafe.Add(ref backgroundRef, (uint)i), Unsafe.Add(ref sourceRef, (uint)i), amount); + } + } + + /// + protected sealed override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount) + { + // Public entry points validate the row lengths, so the destination and background advance together. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + // Repeat one [R,G,B,A] source group four times to match the four background pixels. + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), sourceVector, amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + // Repeat one [R,G,B,A] source group twice to match the two background pixels. + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), sourceVector, amountVector); + } + + scalarStart = vectorCount * 2; + } + + // The remaining pixel count is at most three after AVX-512 or one after AVX2. + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke(Unsafe.Add(ref backgroundRef, (uint)i), source, amount); + } + } + + /// + protected sealed override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + // Each amount belongs to one pixel and must be repeated across that pixel's four RGBA lanes. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + // Four scalar amounts become four contiguous [a,a,a,a] groups before clamping. + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), Unsafe.Add(ref sourceBase, i), amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + // Two scalar amounts become two contiguous [a,a,a,a] groups before clamping. + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), Unsafe.Add(ref sourceBase, i), amountVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke(Unsafe.Add(ref backgroundRef, (uint)i), Unsafe.Add(ref sourceRef, (uint)i), Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount) + { + // The source is invariant, while each background pixel has its own independently clamped amount. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), sourceVector, amountVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + + Unsafe.Add(ref destinationBase, i) = TOperator.Invoke(Unsafe.Add(ref backgroundBase, i), sourceVector, amountVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.Invoke(Unsafe.Add(ref backgroundRef, (uint)i), source, Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + } + } + + /// + protected sealed override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount, ReadOnlySpan coverage) + { + // Coverage mixes the composed result back toward the original background, so it is fused into this pass. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, Unsafe.Add(ref sourceBase, i), amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, Unsafe.Add(ref sourceBase, i), amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke(backgroundPixel, Unsafe.Add(ref sourceRef, (uint)i), amount); + + float clampedCoverage = Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F); + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage) + : PorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage); + } + } + + /// + protected sealed override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, float amount, ReadOnlySpan coverage) + { + // The constant source is expanded once per selected width and reused for the complete row. + int scalarStart = 0; + amount = Numerics.Clamp(amount, 0, 1F); + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + Vector512 amountVector = Vector512.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + Vector256 amountVector = Vector256.Create(amount); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke(backgroundPixel, source, amount); + + float clampedCoverage = Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F); + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage) + : PorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage); + } + } + + /// + protected sealed override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + // Amount controls composition while coverage controls the final mix with the untouched background. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(source); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector512 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 4; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, Unsafe.Add(ref sourceBase, i), amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + ref Vector256 sourceBase = ref Unsafe.As>(ref sourceRef); + int vectorCount = destination.Length / 2; + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, Unsafe.Add(ref sourceBase, i), amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke(backgroundPixel, Unsafe.Add(ref sourceRef, (uint)i), Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + + float clampedCoverage = Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F); + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage) + : PorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage); + } + } + + /// + protected sealed override void BlendWithCoverageFunction(Span destination, ReadOnlySpan background, Vector4 source, ReadOnlySpan amount, ReadOnlySpan coverage) + { + // The invariant source is expanded once; only amount and coverage are gathered for each vector batch. + int scalarStart = 0; + + ref Vector4 destinationRef = ref MemoryMarshal.GetReference(destination); + ref Vector4 backgroundRef = ref MemoryMarshal.GetReference(background); + ref float amountRef = ref MemoryMarshal.GetReference(amount); + ref float coverageRef = ref MemoryMarshal.GetReference(coverage); + + if (Vector512.IsHardwareAccelerated && destination.Length >= 4) + { + ref Vector512 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector512 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 4; + Vector512 sourceVector = CreateVector512(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector512 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 4); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 4); + Vector512 amountVector = CreateClampedVector512(ref amountBase); + Vector512 coverageVector = CreateClampedVector512(ref coverageBase); + Vector512 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 4; + } + else if (Vector256.IsHardwareAccelerated && destination.Length >= 2) + { + ref Vector256 destinationBase = ref Unsafe.As>(ref destinationRef); + ref Vector256 backgroundBase = ref Unsafe.As>(ref backgroundRef); + int vectorCount = destination.Length / 2; + Vector256 sourceVector = CreateVector256(source); + + for (nuint i = 0; i < (uint)vectorCount; i++) + { + ref Vector256 backgroundVector = ref Unsafe.Add(ref backgroundBase, i); + ref float amountBase = ref Unsafe.Add(ref amountRef, i * 2); + ref float coverageBase = ref Unsafe.Add(ref coverageRef, i * 2); + Vector256 amountVector = CreateClampedVector256(ref amountBase); + Vector256 coverageVector = CreateClampedVector256(ref coverageBase); + Vector256 blended = TOperator.Invoke(backgroundVector, sourceVector, amountVector); + + Unsafe.Add(ref destinationBase, i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector) + : PorterDuffFunctions.BlendWithCoverage(backgroundVector, blended, coverageVector); + } + + scalarStart = vectorCount * 2; + } + + for (int i = scalarStart; i < destination.Length; i++) + { + Vector4 backgroundPixel = Unsafe.Add(ref backgroundRef, (uint)i); + Vector4 blended = TOperator.Invoke(backgroundPixel, source, Numerics.Clamp(Unsafe.Add(ref amountRef, (uint)i), 0, 1F)); + + float clampedCoverage = Numerics.Clamp(Unsafe.Add(ref coverageRef, (uint)i), 0, 1F); + Unsafe.Add(ref destinationRef, (uint)i) = TOperator.IsAssociatedAlpha + ? AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage) + : PorterDuffFunctions.BlendWithCoverage(backgroundPixel, blended, clampedCoverage); + } + } + + /// + /// Repeats one RGBA pixel twice to fill a 256-bit register. + /// + /// The pixel represented by ordered RGBA lanes. + /// Two consecutive copies of the pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 CreateVector256(Vector4 pixel) + => Vector256.Create(pixel.X, pixel.Y, pixel.Z, pixel.W, pixel.X, pixel.Y, pixel.Z, pixel.W); + + /// + /// Repeats one RGBA pixel four times to fill a 512-bit register. + /// + /// The pixel represented by ordered RGBA lanes. + /// Four consecutive copies of the pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 CreateVector512(Vector4 pixel) + => Vector512.Create(pixel.X, pixel.Y, pixel.Z, pixel.W, pixel.X, pixel.Y, pixel.Z, pixel.W, pixel.X, pixel.Y, pixel.Z, pixel.W, pixel.X, pixel.Y, pixel.Z, pixel.W); + + /// + /// Expands and clamps two per-pixel scalar values for two packed RGBA pixels. + /// + /// The first of two contiguous scalar values. + /// Two consecutive groups containing one clamped value in each group's four lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 CreateClampedVector256(ref float values) + { + Vector256 result = Vector256.Create(Vector128.Create(values), Vector128.Create(Unsafe.Add(ref values, 1))); + + // Amount and coverage share the same public 0..1 contract and therefore the same packed clamp. + return Vector256.Min(Vector256.Max(Vector256.Zero, result), Vector256.Create(1F)); + } + + /// + /// Expands and clamps four per-pixel scalar values for four packed RGBA pixels. + /// + /// The first of four contiguous scalar values. + /// Four consecutive groups containing one clamped value in each group's four lanes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 CreateClampedVector512(ref float values) + { + Vector512 result = Vector512.Create(values, values, values, values, Unsafe.Add(ref values, 1), Unsafe.Add(ref values, 1), Unsafe.Add(ref values, 1), Unsafe.Add(ref values, 1), Unsafe.Add(ref values, 2), Unsafe.Add(ref values, 2), Unsafe.Add(ref values, 2), Unsafe.Add(ref values, 2), Unsafe.Add(ref values, 3), Unsafe.Add(ref values, 3), Unsafe.Add(ref values, 3), Unsafe.Add(ref values, 3)); + + // Amount and coverage share the same public 0..1 contract and therefore the same packed clamp. + return Vector512.Min(Vector512.Max(Vector512.Zero, result), Vector512.Create(1F)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index e7cf3b2921..138249fbda 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -37,7 +37,18 @@ public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "NormalSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "NormalSrcAtop" compositing equation. @@ -64,7 +75,22 @@ public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Normal(backdrop, source)); } @@ -94,7 +120,22 @@ public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Normal(backdrop, source)); } @@ -123,7 +164,18 @@ public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "NormalSrcOut" compositing equation. @@ -149,7 +201,18 @@ public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "NormalDest" compositing equation. @@ -177,6 +240,19 @@ public static Vector256 NormalDest(Vector256 backdrop, Vector256 + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "NormalDestAtop" compositing equation. /// @@ -202,7 +278,22 @@ public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Normal(source, backdrop)); + } + + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Normal(source, backdrop)); } @@ -232,7 +323,22 @@ public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Normal(source, backdrop)); + } + + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Normal(source, backdrop)); } @@ -261,7 +367,18 @@ public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "NormalDestOut" compositing equation. @@ -287,7 +404,18 @@ public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "NormalXor" compositing equation. @@ -313,7 +441,63 @@ public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "NormalPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Normal(backdrop, source)); + } /// /// Returns the result of the "NormalClear" compositing equation. @@ -339,7 +523,18 @@ public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 NormalClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 NormalClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -355,9 +550,7 @@ public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(NormalSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -373,9 +566,7 @@ public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(NormalSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -391,9 +582,7 @@ public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(NormalSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -409,9 +598,7 @@ public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(NormalSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -427,9 +614,7 @@ public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(NormalSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -445,9 +630,7 @@ public static TPixel NormalDest(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(NormalDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -463,9 +646,7 @@ public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(NormalDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -481,9 +662,7 @@ public static TPixel NormalDestOver(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(NormalDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -499,9 +678,7 @@ public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(NormalDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -517,9 +694,7 @@ public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(NormalDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -535,9 +710,7 @@ public static TPixel NormalClear(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(NormalClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -553,9 +726,23 @@ public static TPixel NormalXor(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(NormalXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "NormalPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(NormalPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -582,7 +769,18 @@ public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "MultiplySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "MultiplySrcAtop" compositing equation. @@ -609,7 +807,22 @@ public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Multiply(backdrop, source)); } @@ -639,7 +852,22 @@ public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Multiply(backdrop, source)); } @@ -668,7 +896,18 @@ public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "MultiplySrcOut" compositing equation. @@ -694,7 +933,18 @@ public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "MultiplyDest" compositing equation. @@ -722,6 +972,19 @@ public static Vector256 MultiplyDest(Vector256 backdrop, Vector256 return backdrop; } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "MultiplyDestAtop" compositing equation. /// @@ -747,7 +1010,22 @@ public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Multiply(source, backdrop)); } @@ -777,7 +1055,22 @@ public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Multiply(source, backdrop)); } @@ -806,7 +1099,18 @@ public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "MultiplyDestOut" compositing equation. @@ -832,17 +1136,28 @@ public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float op /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); /// - /// Returns the result of the "MultiplyXor" compositing equation. + /// Returns the result of the "MultiplyDestOut" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) + public static Vector512 MultiplyDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) { source = Numerics.WithW(source, source * opacity); @@ -858,7 +1173,63 @@ public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "MultiplyPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplyPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplyPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Multiply(backdrop, source)); + } /// /// Returns the result of the "MultiplyClear" compositing equation. @@ -884,7 +1255,18 @@ public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 MultiplyClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -900,9 +1282,7 @@ public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(MultiplySrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -918,9 +1298,7 @@ public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(MultiplySrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -936,9 +1314,7 @@ public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(MultiplySrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -954,9 +1330,7 @@ public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(MultiplySrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -972,9 +1346,7 @@ public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(MultiplySrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -990,9 +1362,7 @@ public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(MultiplyDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1008,9 +1378,7 @@ public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(MultiplyDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1026,9 +1394,7 @@ public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(MultiplyDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1044,9 +1410,7 @@ public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(MultiplyDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1062,9 +1426,7 @@ public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(MultiplyDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1080,9 +1442,7 @@ public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(MultiplyClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1098,9 +1458,23 @@ public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(MultiplyXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "MultiplyPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(MultiplyPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1127,7 +1501,18 @@ public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "AddSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "AddSrcAtop" compositing equation. @@ -1154,7 +1539,22 @@ public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the result of the "AddSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Add(backdrop, source)); } @@ -1184,7 +1584,22 @@ public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the result of the "AddSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Add(backdrop, source)); } @@ -1213,7 +1628,18 @@ public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "AddSrcOut" compositing equation. @@ -1239,7 +1665,18 @@ public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "AddDest" compositing equation. @@ -1267,6 +1704,19 @@ public static Vector256 AddDest(Vector256 backdrop, Vector256 + /// Returns the result of the "AddDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "AddDestAtop" compositing equation. /// @@ -1292,7 +1742,22 @@ public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Add(source, backdrop)); } @@ -1322,7 +1787,22 @@ public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Add(source, backdrop)); } @@ -1351,7 +1831,18 @@ public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "AddDestOut" compositing equation. @@ -1377,7 +1868,18 @@ public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "AddXor" compositing equation. @@ -1403,7 +1905,63 @@ public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "AddPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the result of the "AddPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Add(backdrop, source)); + } + + /// + /// Returns the result of the "AddPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Add(backdrop, source)); + } /// /// Returns the result of the "AddClear" compositing equation. @@ -1429,7 +1987,18 @@ public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 AddClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 AddClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -1445,9 +2014,7 @@ public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacit 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.FromUnassociatedScaledVector4(AddSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1463,9 +2030,7 @@ public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(AddSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1481,9 +2046,7 @@ public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(AddSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1499,9 +2062,7 @@ public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opac 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.FromUnassociatedScaledVector4(AddSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1517,9 +2078,7 @@ public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(AddSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1535,9 +2094,7 @@ public static TPixel AddDest(TPixel backdrop, TPixel source, float opaci 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.FromUnassociatedScaledVector4(AddDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1553,9 +2110,7 @@ public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(AddDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1571,9 +2126,7 @@ public static TPixel AddDestOver(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(AddDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1589,9 +2142,7 @@ public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(AddDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1607,9 +2158,7 @@ public static TPixel AddDestOut(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(AddDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1625,9 +2174,7 @@ public static TPixel AddClear(TPixel backdrop, TPixel source, float opac 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.FromUnassociatedScaledVector4(AddClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1643,9 +2190,23 @@ public static TPixel AddXor(TPixel backdrop, TPixel source, float opacit 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.FromUnassociatedScaledVector4(AddXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "AddPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(AddPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -1672,7 +2233,18 @@ public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "SubtractSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "SubtractSrcAtop" compositing equation. @@ -1699,7 +2271,22 @@ public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Subtract(backdrop, source)); } @@ -1729,18 +2316,33 @@ public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); return Over(backdrop, source, Subtract(backdrop, source)); } /// - /// Returns the result of the "SubtractSrcIn" compositing equation. + /// Returns the result of the "SubtractSrcOver" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) { @@ -1758,7 +2360,18 @@ public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "SubtractSrcOut" compositing equation. @@ -1784,7 +2397,18 @@ public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "SubtractDest" compositing equation. @@ -1812,6 +2436,19 @@ public static Vector256 SubtractDest(Vector256 backdrop, Vector256 return backdrop; } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "SubtractDestAtop" compositing equation. /// @@ -1837,7 +2474,22 @@ public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Subtract(source, backdrop)); } @@ -1867,7 +2519,22 @@ public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Subtract(source, backdrop)); } @@ -1896,7 +2563,18 @@ public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "SubtractDestOut" compositing equation. @@ -1922,7 +2600,18 @@ public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float op /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "SubtractXor" compositing equation. @@ -1948,7 +2637,63 @@ public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SubtractPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubtractPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Subtract(backdrop, source)); + } /// /// Returns the result of the "SubtractClear" compositing equation. @@ -1974,7 +2719,18 @@ public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 SubtractClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SubtractClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -1990,9 +2746,7 @@ public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(SubtractSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2008,9 +2762,7 @@ public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(SubtractSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2026,9 +2778,7 @@ public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(SubtractSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2044,9 +2794,7 @@ public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(SubtractSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2062,9 +2810,7 @@ public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(SubtractSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2080,9 +2826,7 @@ public static TPixel SubtractDest(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(SubtractDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2098,9 +2842,7 @@ public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(SubtractDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2116,9 +2858,7 @@ public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(SubtractDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2134,9 +2874,7 @@ public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(SubtractDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2152,9 +2890,7 @@ public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(SubtractDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2170,9 +2906,7 @@ public static TPixel SubtractClear(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(SubtractClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2188,9 +2922,23 @@ public static TPixel SubtractXor(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(SubtractXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SubtractPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubtractPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SubtractPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2217,7 +2965,18 @@ public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "ScreenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "ScreenSrcAtop" compositing equation. @@ -2244,7 +3003,22 @@ public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Screen(backdrop, source)); } @@ -2274,7 +3048,22 @@ public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Screen(backdrop, source)); } @@ -2303,7 +3092,18 @@ public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "ScreenSrcOut" compositing equation. @@ -2329,7 +3129,18 @@ public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "ScreenDest" compositing equation. @@ -2357,6 +3168,19 @@ public static Vector256 ScreenDest(Vector256 backdrop, Vector256 + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "ScreenDestAtop" compositing equation. /// @@ -2382,7 +3206,22 @@ public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Screen(source, backdrop)); } @@ -2412,7 +3251,22 @@ public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Screen(source, backdrop)); } @@ -2441,7 +3295,18 @@ public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "ScreenDestOut" compositing equation. @@ -2467,7 +3332,18 @@ public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "ScreenXor" compositing equation. @@ -2493,33 +3369,100 @@ public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ScreenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); /// - /// Returns the result of the "ScreenClear" compositing equation. + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ScreenPlus" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 ScreenPlus(Vector4 backdrop, Vector4 source, float opacity) { source = Numerics.WithW(source, source * opacity); - return Clear(backdrop, source); + return Plus(backdrop, source, Screen(backdrop, source)); } /// - /// Returns the result of the "ScreenClear" compositing equation. + /// Returns the result of the "ScreenPlus" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 ScreenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + public static Vector256 ScreenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ScreenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -2535,9 +3478,7 @@ public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(ScreenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2553,9 +3494,7 @@ public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(ScreenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2571,9 +3510,7 @@ public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(ScreenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2589,9 +3526,7 @@ public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(ScreenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2607,9 +3542,7 @@ public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(ScreenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2625,9 +3558,7 @@ public static TPixel ScreenDest(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(ScreenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2643,9 +3574,7 @@ public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(ScreenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2661,9 +3590,7 @@ public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(ScreenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2679,9 +3606,7 @@ public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(ScreenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2697,9 +3622,7 @@ public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(ScreenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2715,9 +3638,7 @@ public static TPixel ScreenClear(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(ScreenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2733,9 +3654,23 @@ public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(ScreenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ScreenPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ScreenPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -2762,7 +3697,18 @@ public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "DarkenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "DarkenSrcAtop" compositing equation. @@ -2789,7 +3735,22 @@ public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Darken(backdrop, source)); } @@ -2819,7 +3780,22 @@ public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opac [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Darken(backdrop, source)); } @@ -2848,7 +3824,18 @@ public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "DarkenSrcOut" compositing equation. @@ -2874,7 +3861,18 @@ public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "DarkenDest" compositing equation. @@ -2902,6 +3900,19 @@ public static Vector256 DarkenDest(Vector256 backdrop, Vector256 + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "DarkenDestAtop" compositing equation. /// @@ -2927,7 +3938,22 @@ public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Darken(source, backdrop)); } @@ -2957,7 +3983,22 @@ public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Darken(source, backdrop)); } @@ -2986,7 +4027,18 @@ public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "DarkenDestOut" compositing equation. @@ -3012,7 +4064,18 @@ public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "DarkenXor" compositing equation. @@ -3038,7 +4101,63 @@ public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "DarkenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Darken(backdrop, source)); + } /// /// Returns the result of the "DarkenClear" compositing equation. @@ -3064,7 +4183,18 @@ public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacit /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 DarkenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DarkenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -3080,9 +4210,7 @@ public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(DarkenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3098,9 +4226,7 @@ public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(DarkenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3116,9 +4242,7 @@ public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(DarkenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3134,9 +4258,7 @@ public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(DarkenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3152,9 +4274,7 @@ public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(DarkenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3170,9 +4290,7 @@ public static TPixel DarkenDest(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(DarkenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3188,9 +4306,7 @@ public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(DarkenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3206,9 +4322,7 @@ public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(DarkenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3224,9 +4338,7 @@ public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(DarkenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3242,9 +4354,7 @@ public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(DarkenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3260,9 +4370,7 @@ public static TPixel DarkenClear(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(DarkenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3278,9 +4386,23 @@ public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opa 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.FromUnassociatedScaledVector4(DarkenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DarkenPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DarkenPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3307,7 +4429,18 @@ public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "LightenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "LightenSrcAtop" compositing equation. @@ -3334,7 +4467,22 @@ public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Lighten(backdrop, source)); } @@ -3364,7 +4512,22 @@ public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Lighten(backdrop, source)); } @@ -3393,19 +4556,30 @@ public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); /// - /// Returns the result of the "LightenSrcOut" compositing equation. + /// Returns the result of the "LightenSrcIn" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) - { - source = Numerics.WithW(source, source * opacity); + public static Vector512 LightenSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } @@ -3419,7 +4593,18 @@ public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "LightenDest" compositing equation. @@ -3447,6 +4632,19 @@ public static Vector256 LightenDest(Vector256 backdrop, Vector256< return backdrop; } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "LightenDestAtop" compositing equation. /// @@ -3472,7 +4670,22 @@ public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Lighten(source, backdrop)); } @@ -3502,7 +4715,22 @@ public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Lighten(source, backdrop)); } @@ -3531,7 +4759,18 @@ public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "LightenDestOut" compositing equation. @@ -3557,7 +4796,18 @@ public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "LightenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "LightenXor" compositing equation. @@ -3583,7 +4833,63 @@ public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "LightenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Lighten(backdrop, source)); + } /// /// Returns the result of the "LightenClear" compositing equation. @@ -3609,7 +4915,18 @@ public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 LightenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LightenClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -3625,9 +4942,7 @@ public static TPixel LightenSrc(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(LightenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3643,9 +4958,7 @@ public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(LightenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3661,9 +4974,7 @@ public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(LightenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3679,9 +4990,7 @@ public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(LightenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3697,9 +5006,7 @@ public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(LightenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3715,9 +5022,7 @@ public static TPixel LightenDest(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(LightenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3733,9 +5038,7 @@ public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(LightenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3751,9 +5054,7 @@ public static TPixel LightenDestOver(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(LightenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3769,9 +5070,7 @@ public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(LightenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3787,9 +5086,7 @@ public static TPixel LightenDestOut(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(LightenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3805,9 +5102,7 @@ public static TPixel LightenClear(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(LightenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3823,9 +5118,23 @@ public static TPixel LightenXor(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(LightenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LightenPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LightenPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -3852,7 +5161,18 @@ public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlaySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "OverlaySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "OverlaySrcAtop" compositing equation. @@ -3879,7 +5199,22 @@ public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlaySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, Overlay(backdrop, source)); } @@ -3909,7 +5244,22 @@ public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlaySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, Overlay(backdrop, source)); } @@ -3938,7 +5288,18 @@ public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlaySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "OverlaySrcOut" compositing equation. @@ -3964,7 +5325,18 @@ public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlaySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlaySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "OverlayDest" compositing equation. @@ -3992,6 +5364,19 @@ public static Vector256 OverlayDest(Vector256 backdrop, Vector256< return backdrop; } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "OverlayDestAtop" compositing equation. /// @@ -4017,7 +5402,22 @@ public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlayDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, Overlay(source, backdrop)); } @@ -4047,7 +5447,22 @@ public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float op [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlayDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, Overlay(source, backdrop)); } @@ -4076,7 +5491,18 @@ public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opac /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlayDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "OverlayDestOut" compositing equation. @@ -4102,7 +5528,18 @@ public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlayDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "OverlayXor" compositing equation. @@ -4128,55 +5565,120 @@ public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 OverlayXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); /// - /// Returns the result of the "OverlayClear" compositing equation. + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "OverlayPlus" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 OverlayPlus(Vector4 backdrop, Vector4 source, float opacity) { source = Numerics.WithW(source, source * opacity); - return Clear(backdrop, source); + return Plus(backdrop, source, Overlay(backdrop, source)); } /// - /// Returns the result of the "OverlayClear" compositing equation. + /// Returns the result of the "OverlayPlus" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 OverlayClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + public static Vector256 OverlayPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + return Plus(backdrop, source, Overlay(backdrop, source)); + } /// - /// Returns the result of the "OverlaySrc" compositing equation. + /// Returns the result of the "OverlayPlus" compositing equation. /// - /// The pixel format. /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) - where TPixel : unmanaged, IPixel + public static Vector512 OverlayPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) { - opacity = Numerics.Clamp(opacity, 0, 1); - TPixel dest = default; - dest.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); - return dest; + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Overlay(backdrop, source)); } /// - /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "OverlaySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(OverlaySrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. /// /// The pixel format. /// The backdrop vector. @@ -4188,9 +5690,7 @@ public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(OverlaySrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4206,9 +5706,7 @@ public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(OverlaySrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4224,9 +5722,7 @@ public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(OverlaySrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4242,9 +5738,7 @@ public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(OverlaySrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4260,9 +5754,7 @@ public static TPixel OverlayDest(TPixel backdrop, TPixel source, float o 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.FromUnassociatedScaledVector4(OverlayDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4278,9 +5770,7 @@ public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(OverlayDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4296,9 +5786,7 @@ public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(OverlayDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4314,9 +5802,7 @@ public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(OverlayDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4332,9 +5818,7 @@ public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(OverlayDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4350,9 +5834,7 @@ public static TPixel OverlayClear(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(OverlayClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4368,9 +5850,23 @@ public static TPixel OverlayXor(TPixel backdrop, TPixel source, float op 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.FromUnassociatedScaledVector4(OverlayXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "OverlayPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(OverlayPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4397,7 +5893,18 @@ public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "HardLightSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "HardLightSrcAtop" compositing equation. @@ -4424,7 +5931,22 @@ public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, HardLight(backdrop, source)); } @@ -4454,7 +5976,22 @@ public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float o [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, HardLight(backdrop, source)); } @@ -4483,7 +6020,18 @@ public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "HardLightSrcOut" compositing equation. @@ -4509,7 +6057,18 @@ public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float op /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "HardLightDest" compositing equation. @@ -4537,6 +6096,19 @@ public static Vector256 HardLightDest(Vector256 backdrop, Vector25 return backdrop; } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "HardLightDestAtop" compositing equation. /// @@ -4562,7 +6134,22 @@ public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, HardLight(source, backdrop)); } @@ -4592,7 +6179,22 @@ public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, HardLight(source, backdrop)); } @@ -4621,7 +6223,18 @@ public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float op /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "HardLightDestOut" compositing equation. @@ -4647,7 +6260,18 @@ public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float o /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "HardLightXor" compositing equation. @@ -4673,7 +6297,63 @@ public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opaci /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "HardLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, HardLight(backdrop, source)); + } /// /// Returns the result of the "HardLightClear" compositing equation. @@ -4699,7 +6379,18 @@ public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opa /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 HardLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLightClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// @@ -4715,9 +6406,7 @@ public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(HardLightSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4733,9 +6422,7 @@ public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(HardLightSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4751,9 +6438,7 @@ public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(HardLightSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4769,9 +6454,7 @@ public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(HardLightSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4787,9 +6470,7 @@ public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(HardLightSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4805,9 +6486,7 @@ public static TPixel HardLightDest(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(HardLightDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4823,9 +6502,7 @@ public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, f 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.FromUnassociatedScaledVector4(HardLightDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4841,9 +6518,7 @@ public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, f 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.FromUnassociatedScaledVector4(HardLightDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4859,9 +6534,7 @@ public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, flo 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.FromUnassociatedScaledVector4(HardLightDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4877,9 +6550,7 @@ public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, fl 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.FromUnassociatedScaledVector4(HardLightDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4895,9 +6566,7 @@ public static TPixel HardLightClear(TPixel backdrop, TPixel source, floa 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.FromUnassociatedScaledVector4(HardLightClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } /// @@ -4913,8 +6582,6610 @@ public static TPixel HardLightXor(TPixel backdrop, TPixel source, float 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.FromUnassociatedScaledVector4(HardLightXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HardLightPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HardLightPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "ColorDodgeSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "ColorDodgeSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorDodgeDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDodgeDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDodgeDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, ColorDodge(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDodgeDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "ColorDodgeDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorDodgeDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorDodgeDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "ColorDodgeDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorDodgeDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorDodgeXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "ColorDodgeXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorDodgeXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorDodgePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgePlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, ColorDodge(backdrop, source)); + } + + /// + /// Returns the result of the "ColorDodgeClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodgeClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "ColorDodgeClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodgeClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorDodgeClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodgeClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "ColorDodgeSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgeXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgeXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgeXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDodgePlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDodgePlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDodgePlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "ColorBurnSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "ColorBurnSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "ColorBurnSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorBurnSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorBurnSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "ColorBurnSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorBurnSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorBurnDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorBurnDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorBurnDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorBurnDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, ColorBurn(source, backdrop)); + } + + /// + /// Returns the result of the "ColorBurnDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "ColorBurnDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorBurnDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorBurnDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "ColorBurnDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorBurnDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorBurnXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "ColorBurnXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorBurnXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorBurnPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, ColorBurn(backdrop, source)); + } + + /// + /// Returns the result of the "ColorBurnClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurnClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "ColorBurnClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurnClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorBurnClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurnClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "ColorBurnSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorBurnPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorBurnPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorBurnPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "SoftLightSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "SoftLightSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "SoftLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "SoftLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SoftLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SoftLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "SoftLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SoftLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SoftLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SoftLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SoftLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SoftLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, SoftLight(source, backdrop)); + } + + /// + /// Returns the result of the "SoftLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "SoftLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SoftLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "SoftLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "SoftLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SoftLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "SoftLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "SoftLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SoftLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SoftLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, SoftLight(backdrop, source)); + } + + /// + /// Returns the result of the "SoftLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLightClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "SoftLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SoftLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLightClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "SoftLightSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SoftLightPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SoftLightPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SoftLightPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "DifferenceSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "DifferenceSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "DifferenceSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "DifferenceSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DifferenceSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "DifferenceSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "DifferenceSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DifferenceSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "DifferenceDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "DifferenceDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "DifferenceDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "DifferenceDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Difference(source, backdrop)); + } + + /// + /// Returns the result of the "DifferenceDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "DifferenceDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "DifferenceDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "DifferenceDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "DifferenceDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "DifferenceDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "DifferenceXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "DifferenceXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DifferenceXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "DifferencePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferencePlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferencePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferencePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferencePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferencePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Difference(backdrop, source)); + } + + /// + /// Returns the result of the "DifferenceClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DifferenceClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "DifferenceClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DifferenceClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "DifferenceClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 DifferenceClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "DifferenceSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferenceXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferenceXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferenceXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "DifferencePlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DifferencePlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(DifferencePlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "ExclusionSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "ExclusionSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "ExclusionSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "ExclusionSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ExclusionSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ExclusionSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "ExclusionSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ExclusionSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ExclusionDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ExclusionDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ExclusionDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ExclusionDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Exclusion(source, backdrop)); + } + + /// + /// Returns the result of the "ExclusionDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "ExclusionDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ExclusionDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ExclusionDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "ExclusionDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ExclusionDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ExclusionXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "ExclusionXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ExclusionXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ExclusionPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Exclusion(backdrop, source)); + } + + /// + /// Returns the result of the "ExclusionClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ExclusionClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "ExclusionClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ExclusionClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ExclusionClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ExclusionClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "ExclusionSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ExclusionPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ExclusionPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ExclusionPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "HueSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "HueSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "HueSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "HueSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HueSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "HueSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "HueSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HueSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "HueDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "HueDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "HueDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "HueDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Hue(source, backdrop)); + } + + /// + /// Returns the result of the "HueDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "HueDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "HueDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "HueDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "HueDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "HueDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "HueXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "HueXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HueXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "HuePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HuePlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HuePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HuePlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HuePlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HuePlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Hue(backdrop, source)); + } + + /// + /// Returns the result of the "HueClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HueClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "HueClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HueClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "HueClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HueClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "HueSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HueXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HueXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HueXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "HuePlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HuePlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(HuePlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "SaturationSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "SaturationSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "SaturationSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "SaturationSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SaturationSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SaturationSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "SaturationSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SaturationSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SaturationDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SaturationDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SaturationDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "SaturationDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Saturation(source, backdrop)); + } + + /// + /// Returns the result of the "SaturationDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "SaturationDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SaturationDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "SaturationDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "SaturationDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "SaturationDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "SaturationXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "SaturationXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SaturationXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "SaturationPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Saturation(backdrop, source)); + } + + /// + /// Returns the result of the "SaturationClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SaturationClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "SaturationClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SaturationClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "SaturationClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SaturationClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "SaturationSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "SaturationPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SaturationPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(SaturationPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "ColorSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "ColorSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "ColorSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "ColorSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "ColorSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorSrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ColorDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Color(source, backdrop)); + } + + /// + /// Returns the result of the "ColorDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "ColorDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "ColorDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "ColorDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "ColorXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "ColorXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "ColorPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Color(backdrop, source)); + } + + /// + /// Returns the result of the "ColorClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "ColorClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "ColorClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "ColorSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorSrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorSrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorSrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorSrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorSrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "ColorPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ColorPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(ColorPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminositySrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrc(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return source; + } + + /// + /// Returns the result of the "LuminositySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "LuminositySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrc(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + /// + /// Returns the result of the "LuminositySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminositySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "LuminositySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LuminositySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "LuminositySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminositySrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "LuminositySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminositySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LuminositySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminositySrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "LuminosityDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "LuminosityDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "LuminosityDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "LuminosityDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Atop(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestOver(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Over(source, backdrop, Luminosity(source, backdrop)); + } + + /// + /// Returns the result of the "LuminosityDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "LuminosityDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "LuminosityDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "LuminosityDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityDestOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(source, backdrop); + } + + /// + /// Returns the result of the "LuminosityDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "LuminosityDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityDestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); + + /// + /// Returns the result of the "LuminosityXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityXor(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Xor(backdrop, source); + } + + /// + /// Returns the result of the "LuminosityXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LuminosityXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityXor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "LuminosityPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityPlus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminosityPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityPlus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminosityPlus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityPlus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, Luminosity(backdrop, source)); + } + + /// + /// Returns the result of the "LuminosityClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LuminosityClear(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Clear(backdrop, source); + } + + /// + /// Returns the result of the "LuminosityClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LuminosityClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "LuminosityClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 LuminosityClear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + + /// + /// Returns the result of the "LuminositySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminositySrc(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminositySrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminositySrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminositySrcAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminositySrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminositySrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminositySrcOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminositySrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminositySrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminositySrcIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminositySrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminositySrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminositySrcOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminositySrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityDest(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityDestAtop(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityDestOver(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityDestIn(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityDestOut(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityClear(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityXor(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); + } + + /// + /// Returns the result of the "LuminosityPlus" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LuminosityPlus(TPixel backdrop, TPixel source, float opacity) + where TPixel : unmanaged, IPixel + { + opacity = Numerics.Clamp(opacity, 0, 1); + return TPixel.FromUnassociatedScaledVector4(LuminosityPlus(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 64eee502bb..3ee0e133c0 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -47,7 +47,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>Src(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + => Avx.Blend(source, source * opacity, BlendAlphaControl); + + /// + /// Returns the result of the "<#=blender#>Src compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>Src(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); /// /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. @@ -74,7 +85,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>SrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>SrcAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(backdrop, source, <#=blender#>(backdrop, source)); } @@ -104,7 +130,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>SrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>SrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>SrcOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(backdrop, source, <#=blender#>(backdrop, source)); } @@ -133,7 +174,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>SrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => In(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "<#=blender#>SrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>SrcIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "<#=blender#>SrcOut" compositing equation. @@ -159,7 +211,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>SrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Out(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "<#=blender#>SrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>SrcOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); /// /// Returns the result of the "<#=blender#>Dest" compositing equation. @@ -187,6 +250,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "<#=blender#>Dest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>Dest(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + return backdrop; + } + /// /// Returns the result of the "<#=blender#>DestAtop" compositing equation. /// @@ -212,7 +288,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>DestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Atop(source, backdrop, <#=blender#>(source, backdrop)); + } + + /// + /// Returns the result of the "<#=blender#>DestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>DestAtop(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Atop(source, backdrop, <#=blender#>(source, backdrop)); } @@ -242,7 +333,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>DestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Over(source, backdrop, <#=blender#>(source, backdrop)); + } + + /// + /// Returns the result of the "<#=blender#>DestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>DestOver(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); return Over(source, backdrop, <#=blender#>(source, backdrop)); } @@ -271,7 +377,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>DestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) - => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => In(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "<#=blender#>DestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>DestIn(Vector512 backdrop, Vector512 source, Vector512 opacity) + => In(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "<#=blender#>DestOut" compositing equation. @@ -297,7 +414,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>DestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + => Out(Avx.Blend(source, source * opacity, BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "<#=blender#>DestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>DestOut(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Out(Avx512F.BlendVariable(source, source * opacity, AlphaMask512()), backdrop); /// /// Returns the result of the "<#=blender#>Xor" compositing equation. @@ -323,7 +451,63 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>Xor(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Xor(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "<#=blender#>Xor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>Xor(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Xor(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); + + /// + /// Returns the result of the "<#=blender#>Plus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 <#=blender#>Plus(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Plus(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>Plus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>Plus(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, source * opacity, BlendAlphaControl); + + return Plus(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>Plus" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>Plus(Vector512 backdrop, Vector512 source, Vector512 opacity) + { + source = Avx512F.BlendVariable(source, source * opacity, AlphaMask512()); + + return Plus(backdrop, source, <#=blender#>(backdrop, source)); + } /// /// Returns the result of the "<#=blender#>Clear" compositing equation. @@ -349,7 +533,18 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 <#=blender#>Clear(Vector256 backdrop, Vector256 source, Vector256 opacity) - => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + => Clear(backdrop, Avx.Blend(source, source * opacity, BlendAlphaControl)); + + /// + /// Returns the result of the "<#=blender#>Clear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 <#=blender#>Clear(Vector512 backdrop, Vector512 source, Vector512 opacity) + => Clear(backdrop, Avx512F.BlendVariable(source, source * opacity, AlphaMask512())); <#} #> @@ -368,9 +563,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.FromUnassociatedScaledVector4(<#=blender#><#=composer#>(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity)); } <# } #> <# @@ -387,6 +580,7 @@ var composers = new []{ "DestOut", "Clear", "Xor", + "Plus", }; var blenders = new []{ @@ -398,7 +592,16 @@ var blenders = new []{ "Darken", "Lighten", "Overlay", - "HardLight" + "HardLight", + "ColorDodge", + "ColorBurn", + "SoftLight", + "Difference", + "Exclusion", + "Hue", + "Saturation", + "Color", + "Luminosity", }; foreach(var blender in blenders) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index ca358be31c..4b048a6f47 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -44,6 +45,16 @@ public static Vector4 Normal(Vector4 backdrop, Vector4 source) public static Vector256 Normal(Vector256 backdrop, Vector256 source) => source; + /// + /// Returns the result of the "Normal" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Normal(Vector512 backdrop, Vector512 source) + => source; + /// /// Returns the result of the "Multiply" compositing equation. /// @@ -62,7 +73,17 @@ public static Vector4 Multiply(Vector4 backdrop, Vector4 source) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Multiply(Vector256 backdrop, Vector256 source) - => Avx.Multiply(backdrop, source); + => backdrop * source; + + /// + /// Returns the result of the "Multiply" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Multiply(Vector512 backdrop, Vector512 source) + => backdrop * source; /// /// Returns the result of the "Add" compositing equation. @@ -82,7 +103,17 @@ public static Vector4 Add(Vector4 backdrop, Vector4 source) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Add(Vector256 backdrop, Vector256 source) - => Avx.Min(Vector256.Create(1F), Avx.Add(backdrop, source)); + => Vector256.Min(Vector256.Create(1F), backdrop + source); + + /// + /// Returns the result of the "Add" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Add(Vector512 backdrop, Vector512 source) + => Vector512.Min(Vector512.Create(1F), backdrop + source); /// /// Returns the result of the "Subtract" compositing equation. @@ -102,7 +133,17 @@ public static Vector4 Subtract(Vector4 backdrop, Vector4 source) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Subtract(Vector256 backdrop, Vector256 source) - => Avx.Max(Vector256.Zero, Avx.Subtract(backdrop, source)); + => Vector256.Max(Vector256.Zero, backdrop - source); + + /// + /// Returns the result of the "Subtract" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Subtract(Vector512 backdrop, Vector512 source) + => Vector512.Max(Vector512.Zero, backdrop - source); /// /// Returns the result of the "Screen" compositing equation. @@ -124,7 +165,20 @@ public static Vector4 Screen(Vector4 backdrop, Vector4 source) public static Vector256 Screen(Vector256 backdrop, Vector256 source) { Vector256 vOne = Vector256.Create(1F); - return SimdUtils.HwIntrinsics.MultiplyAddNegated(Avx.Subtract(vOne, backdrop), Avx.Subtract(vOne, source), vOne); + return Vector256_.MultiplyAddNegated(vOne, vOne - backdrop, vOne - source); + } + + /// + /// Returns the result of the "Screen" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Screen(Vector512 backdrop, Vector512 source) + { + Vector512 vOne = Vector512.Create(1F); + return Vector512_.MultiplyAddNegated(vOne, vOne - backdrop, vOne - source); } /// @@ -145,7 +199,17 @@ public static Vector4 Darken(Vector4 backdrop, Vector4 source) /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Darken(Vector256 backdrop, Vector256 source) - => Avx.Min(backdrop, source); + => Vector256.Min(backdrop, source); + + /// + /// Returns the result of the "Darken" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Darken(Vector512 backdrop, Vector512 source) + => Vector512.Min(backdrop, source); /// /// Returns the result of the "Lighten" compositing equation. @@ -164,7 +228,17 @@ public static Vector256 Darken(Vector256 backdrop, Vector256The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Lighten(Vector256 backdrop, Vector256 source) - => Avx.Max(backdrop, source); + => Vector256.Max(backdrop, source); + + /// + /// Returns the result of the "Lighten" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Lighten(Vector512 backdrop, Vector512 source) + => Vector512.Max(backdrop, source); /// /// Returns the result of the "Overlay" compositing equation. @@ -192,7 +266,20 @@ public static Vector4 Overlay(Vector4 backdrop, Vector4 source) public static Vector256 Overlay(Vector256 backdrop, Vector256 source) { Vector256 color = OverlayValueFunction(backdrop, source); - return Avx.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + return Vector256.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + } + + /// + /// Returns the result of the "Overlay" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Overlay(Vector512 backdrop, Vector512 source) + { + Vector512 color = OverlayValueFunction(backdrop, source); + return Vector512.Min(Vector512.Create(1F), Vector512.ConditionalSelect(AlphaMask512(), Vector512.Zero, color)); } /// @@ -221,7 +308,434 @@ public static Vector4 HardLight(Vector4 backdrop, Vector4 source) public static Vector256 HardLight(Vector256 backdrop, Vector256 source) { Vector256 color = OverlayValueFunction(source, backdrop); - return Avx.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + return Vector256.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + } + + /// + /// Returns the result of the "HardLight" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 HardLight(Vector512 backdrop, Vector512 source) + { + Vector512 color = OverlayValueFunction(source, backdrop); + return Vector512.Min(Vector512.Create(1F), Vector512.ConditionalSelect(AlphaMask512(), Vector512.Zero, color)); + } + + /// + /// Returns the result of the "ColorDodge" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorDodge(Vector4 backdrop, Vector4 source) + => new( + ColorDodgeValue(backdrop.X, source.X), + ColorDodgeValue(backdrop.Y, source.Y), + ColorDodgeValue(backdrop.Z, source.Z), + 0F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorDodge(Vector256 backdrop, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 result = Vector256.Min(one, backdrop / (one - source)); + + // The explicit singular cases avoid the undefined 0 / 0 result while preserving the order required by the specification. + result = Avx.BlendVariable(result, one, Avx.CompareEqual(source, one)); + result = Avx.BlendVariable(result, Vector256.Zero, Avx.CompareEqual(backdrop, Vector256.Zero)); + return Avx.Blend(result, Vector256.Zero, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorDodge(Vector512 backdrop, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 result = Vector512.Min(one, backdrop / (one - source)); + result = Vector512.ConditionalSelect(Vector512.Equals(source, one), one, result); + result = Vector512.ConditionalSelect(Vector512.Equals(backdrop, Vector512.Zero), Vector512.Zero, result); + return Vector512.ConditionalSelect(AlphaMask512(), Vector512.Zero, result); + } + + /// + /// Returns the result of the "ColorBurn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ColorBurn(Vector4 backdrop, Vector4 source) + => new( + ColorBurnValue(backdrop.X, source.X), + ColorBurnValue(backdrop.Y, source.Y), + ColorBurnValue(backdrop.Z, source.Z), + 0F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ColorBurn(Vector256 backdrop, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 result = one - Vector256.Min(one, (one - backdrop) / source); + + // As with ColorDodge, resolve the branch singularities explicitly so transparent lanes never produce NaN. + result = Avx.BlendVariable(result, Vector256.Zero, Avx.CompareEqual(source, Vector256.Zero)); + result = Avx.BlendVariable(result, one, Avx.CompareEqual(backdrop, one)); + return Avx.Blend(result, Vector256.Zero, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 ColorBurn(Vector512 backdrop, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 result = one - Vector512.Min(one, (one - backdrop) / source); + result = Vector512.ConditionalSelect(Vector512.Equals(source, Vector512.Zero), Vector512.Zero, result); + result = Vector512.ConditionalSelect(Vector512.Equals(backdrop, one), one, result); + return Vector512.ConditionalSelect(AlphaMask512(), Vector512.Zero, result); + } + + /// + /// Returns the result of the "SoftLight" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SoftLight(Vector4 backdrop, Vector4 source) + => new( + SoftLightValue(backdrop.X, source.X), + SoftLightValue(backdrop.Y, source.Y), + SoftLightValue(backdrop.Z, source.Z), + 0F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SoftLight(Vector256 backdrop, Vector256 source) + { + Vector256 one = Vector256.Create(1F); + Vector256 two = Vector256.Create(2F); + Vector256 dark = backdrop - ((one - (two * source)) * backdrop * (one - backdrop)); + Vector256 polynomial = ((((Vector256.Create(16F) * backdrop) - Vector256.Create(12F)) * backdrop) + Vector256.Create(4F)) * backdrop; + Vector256 d = Avx.BlendVariable(polynomial, Avx.Sqrt(backdrop), Avx.CompareGreaterThan(backdrop, Vector256.Create(.25F))); + Vector256 light = backdrop + (((two * source) - one) * (d - backdrop)); + Vector256 result = Avx.BlendVariable(dark, light, Avx.CompareGreaterThan(source, Vector256.Create(.5F))); + return Avx.Blend(result, Vector256.Zero, BlendAlphaControl); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 SoftLight(Vector512 backdrop, Vector512 source) + { + Vector512 one = Vector512.Create(1F); + Vector512 two = Vector512.Create(2F); + Vector512 dark = backdrop - ((one - (two * source)) * backdrop * (one - backdrop)); + Vector512 polynomial = ((((Vector512.Create(16F) * backdrop) - Vector512.Create(12F)) * backdrop) + Vector512.Create(4F)) * backdrop; + Vector512 d = Vector512.ConditionalSelect(Avx512F.CompareGreaterThan(backdrop, Vector512.Create(.25F)), Vector512.Sqrt(backdrop), polynomial); + Vector512 light = backdrop + (((two * source) - one) * (d - backdrop)); + Vector512 result = Vector512.ConditionalSelect(Avx512F.CompareGreaterThan(source, Vector512.Create(.5F)), light, dark); + return Vector512.ConditionalSelect(AlphaMask512(), Vector512.Zero, result); + } + + /// + /// Returns the result of the "Difference" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Difference(Vector4 backdrop, Vector4 source) + => Vector4.Abs(backdrop - source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Difference(Vector256 backdrop, Vector256 source) + => Vector256.Abs(backdrop - source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Difference(Vector512 backdrop, Vector512 source) + => Vector512.Abs(backdrop - source); + + /// + /// Returns the result of the "Exclusion" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Exclusion(Vector4 backdrop, Vector4 source) + => backdrop + source - (2F * backdrop * source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exclusion(Vector256 backdrop, Vector256 source) + => backdrop + source - (Vector256.Create(2F) * backdrop * source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Exclusion(Vector512 backdrop, Vector512 source) + => backdrop + source - (Vector512.Create(2F) * backdrop * source); + + /// + /// Returns the result of the "Hue" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Hue(Vector4 backdrop, Vector4 source) + => SetLuminosity(SetSaturation(source, Saturation(backdrop)), Luminosity(backdrop)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Hue(Vector256 backdrop, Vector256 source) + => Vector256.Create( + Hue(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(), + Hue(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Hue(Vector512 backdrop, Vector512 source) + => Vector512.Create(Hue(backdrop.GetLower(), source.GetLower()), Hue(backdrop.GetUpper(), source.GetUpper())); + + /// + /// Returns the result of the "Saturation" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Saturation(Vector4 backdrop, Vector4 source) + => SetLuminosity(SetSaturation(backdrop, Saturation(source)), Luminosity(backdrop)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Saturation(Vector256 backdrop, Vector256 source) + => Vector256.Create( + Saturation(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(), + Saturation(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Saturation(Vector512 backdrop, Vector512 source) + => Vector512.Create(Saturation(backdrop.GetLower(), source.GetLower()), Saturation(backdrop.GetUpper(), source.GetUpper())); + + /// + /// Returns the result of the "Color" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Color(Vector4 backdrop, Vector4 source) + => SetLuminosity(source, Luminosity(backdrop)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Color(Vector256 backdrop, Vector256 source) + => Vector256.Create( + Color(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(), + Color(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Color(Vector512 backdrop, Vector512 source) + => Vector512.Create(Color(backdrop.GetLower(), source.GetLower()), Color(backdrop.GetUpper(), source.GetUpper())); + + /// + /// Returns the result of the "Luminosity" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The blended color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Luminosity(Vector4 backdrop, Vector4 source) + => SetLuminosity(backdrop, Luminosity(source)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Luminosity(Vector256 backdrop, Vector256 source) + => Vector256.Create( + Luminosity(backdrop.GetLower().AsVector4(), source.GetLower().AsVector4()).AsVector128(), + Luminosity(backdrop.GetUpper().AsVector4(), source.GetUpper().AsVector4()).AsVector128()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Luminosity(Vector512 backdrop, Vector512 source) + => Vector512.Create(Luminosity(backdrop.GetLower(), source.GetLower()), Luminosity(backdrop.GetUpper(), source.GetUpper())); + + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 BlendWithCoverage(Vector4 backdrop, Vector4 source, float coverage) + { + Vector4 backdropAlpha = Numerics.PermuteW(backdrop); + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 backdropPremultiplied = Numerics.WithW(backdrop * backdropAlpha, backdropAlpha); + Vector4 sourcePremultiplied = Numerics.WithW(source * sourceAlpha, sourceAlpha); + + // Use the same fused operation as the wider paths so exact midpoints cannot change across vector widths. + Vector4 result = Vector128_.MultiplyAddEstimate((sourcePremultiplied - backdropPremultiplied).AsVector128(), Vector128.Create(coverage), backdropPremultiplied.AsVector128()).AsVector4(); + + Numerics.UnPremultiply(ref result); + return result; + } + + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 BlendWithCoverage(Vector256 backdrop, Vector256 source, Vector256 coverage) + { + Vector256 backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 backdropPremultiplied = Avx.Blend(backdrop * backdropAlpha, backdropAlpha, BlendAlphaControl); + Vector256 sourcePremultiplied = Avx.Blend(source * sourceAlpha, sourceAlpha, BlendAlphaControl); + Vector256 result = Vector256_.MultiplyAddEstimate(sourcePremultiplied - backdropPremultiplied, coverage, backdropPremultiplied); + + return Numerics.UnPremultiply(result, Avx.Permute(result, ShuffleAlphaControl)); + } + + /// + /// Applies raster coverage to a Porter-Duff composition result. + /// + /// The backdrop vector. + /// The Porter-Duff composition result. + /// The coverage. Range 0..1. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 BlendWithCoverage(Vector512 backdrop, Vector512 source, Vector512 coverage) + { + Vector512 backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 alphaMask = AlphaMask512(); + Vector512 backdropPremultiplied = Vector512.ConditionalSelect(alphaMask, backdropAlpha, backdrop * backdropAlpha); + Vector512 sourcePremultiplied = Vector512.ConditionalSelect(alphaMask, sourceAlpha, source * sourceAlpha); + Vector512 result = Vector512_.MultiplyAddEstimate(sourcePremultiplied - backdropPremultiplied, coverage, backdropPremultiplied); + + return Numerics.UnPremultiply(result, Vector512_.ShuffleNative(result, ShuffleAlphaControl)); + } + + /// + /// Calculates one ColorDodge component, including the singular cases defined by the blending model. + /// + /// The backdrop component. + /// The source component. + /// The blended component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ColorDodgeValue(float backdrop, float source) + => backdrop == 0F ? 0F : source == 1F ? 1F : MathF.Min(1F, backdrop / (1F - source)); + + /// + /// Calculates one ColorBurn component, including the singular cases defined by the blending model. + /// + /// The backdrop component. + /// The source component. + /// The blended component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ColorBurnValue(float backdrop, float source) + => backdrop == 1F ? 1F : source == 0F ? 0F : 1F - MathF.Min(1F, (1F - backdrop) / source); + + /// + /// Calculates one SoftLight component. + /// + /// The backdrop component. + /// The source component. + /// The blended component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float SoftLightValue(float backdrop, float source) + { + if (source <= .5F) + { + return backdrop - ((1F - (2F * source)) * backdrop * (1F - backdrop)); + } + + // The cubic segment joins the square-root segment smoothly while avoiding its steep slope near zero. + float d = backdrop <= .25F + ? (((((16F * backdrop) - 12F) * backdrop) + 4F) * backdrop) + : MathF.Sqrt(backdrop); + + return backdrop + (((2F * source) - 1F) * (d - backdrop)); + } + + /// + /// Calculates the saturation of an RGB color. + /// + /// The color. + /// The difference between the maximum and minimum RGB components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Saturation(Vector4 color) + => MathF.Max(color.X, MathF.Max(color.Y, color.Z)) - MathF.Min(color.X, MathF.Min(color.Y, color.Z)); + + /// + /// Calculates the luminosity of an RGB color. + /// + /// The color. + /// The weighted RGB luminosity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Luminosity(Vector4 color) + => (.3F * color.X) + (.59F * color.Y) + (.11F * color.Z); + + /// + /// Replaces the saturation of an RGB color while retaining its channel ordering. + /// + /// The color. + /// The required saturation. + /// The adjusted color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 SetSaturation(Vector4 color, float saturation) + { + float minimum = MathF.Min(color.X, MathF.Min(color.Y, color.Z)); + float range = MathF.Max(color.X, MathF.Max(color.Y, color.Z)) - minimum; + if (range == 0F) + { + return Vector4.Zero; + } + + // Translating the minimum to zero and scaling the range is equivalent to the specification's ordered-channel construction. + return (color - new Vector4(minimum)) * (saturation / range); + } + + /// + /// Replaces the luminosity of an RGB color and clips the result to the representable gamut. + /// + /// The color. + /// The required luminosity. + /// The adjusted color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 SetLuminosity(Vector4 color, float luminosity) + { + color += new Vector4(luminosity - Luminosity(color)); + + float minimum = MathF.Min(color.X, MathF.Min(color.Y, color.Z)); + if (minimum < 0F) + { + // Pull every component toward the requested luminosity by the same ratio so hue is preserved. + color = new Vector4(luminosity) + ((color - new Vector4(luminosity)) * (luminosity / (luminosity - minimum))); + } + + float maximum = MathF.Max(color.X, MathF.Max(color.Y, color.Z)); + if (maximum > 1F) + { + color = new Vector4(luminosity) + ((color - new Vector4(luminosity)) * ((1F - luminosity) / (maximum - luminosity))); + } + + return Numerics.WithW(color, Vector4.Zero); } /// @@ -244,14 +758,32 @@ private static float OverlayValueFunction(float backdrop, float source) public static Vector256 OverlayValueFunction(Vector256 backdrop, Vector256 source) { Vector256 vOne = Vector256.Create(1F); - Vector256 left = Avx.Multiply(Avx.Add(backdrop, backdrop), source); + Vector256 left = (backdrop + backdrop) * source; Vector256 vOneMinusSource = Avx.Subtract(vOne, source); - Vector256 right = SimdUtils.HwIntrinsics.MultiplyAddNegated(Avx.Add(vOneMinusSource, vOneMinusSource), Avx.Subtract(vOne, backdrop), vOne); + Vector256 right = Vector256_.MultiplyAddNegated(vOne, vOneMinusSource + vOneMinusSource, vOne - backdrop); Vector256 cmp = Avx.CompareGreaterThan(backdrop, Vector256.Create(.5F)); return Avx.BlendVariable(left, right, cmp); } + /// + /// Helper function for Overlay and HardLight modes + /// + /// Backdrop color element + /// Source color element + /// Overlay value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 OverlayValueFunction(Vector512 backdrop, Vector512 source) + { + Vector512 vOne = Vector512.Create(1F); + Vector512 left = (backdrop + backdrop) * source; + + Vector512 vOneMinusSource = vOne - source; + Vector512 right = Vector512_.MultiplyAddNegated(vOne, vOneMinusSource + vOneMinusSource, vOne - backdrop); + Vector512 cmp = Avx512F.CompareGreaterThan(backdrop, Vector512.Create(.5F)); + return Vector512.ConditionalSelect(cmp, right, left); + } + /// /// Returns the result of the "Over" compositing equation. /// @@ -295,17 +827,47 @@ public static Vector256 Over(Vector256 destination, Vector256 sW = Avx.Permute(source, ShuffleAlphaControl); Vector256 dW = Avx.Permute(destination, ShuffleAlphaControl); - Vector256 blendW = Avx.Multiply(sW, dW); - Vector256 dstW = Avx.Subtract(dW, blendW); - Vector256 srcW = Avx.Subtract(sW, blendW); + Vector256 blendW = sW * dW; + Vector256 dstW = dW - blendW; + Vector256 srcW = sW - blendW; + + // calculate final alpha + Vector256 alpha = dstW + sW; + + // calculate final color + Vector256 color = destination * dstW; + color = Vector256_.MultiplyAddEstimate(source, srcW, color); + color = Vector256_.MultiplyAddEstimate(blend, blendW, color); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + + /// + /// Returns the result of the "Over" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Over(Vector512 destination, Vector512 source, Vector512 blend) + { + // calculate weights + Vector512 sW = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 dW = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + + Vector512 blendW = sW * dW; + Vector512 dstW = dW - blendW; + Vector512 srcW = sW - blendW; // calculate final alpha - Vector256 alpha = Avx.Add(dstW, sW); + Vector512 alpha = dstW + sW; // calculate final color - Vector256 color = Avx.Multiply(destination, dstW); - color = SimdUtils.HwIntrinsics.MultiplyAdd(color, source, srcW); - color = SimdUtils.HwIntrinsics.MultiplyAdd(color, blend, blendW); + Vector512 color = destination * dstW; + color = Vector512_.MultiplyAddEstimate(source, srcW, color); + color = Vector512_.MultiplyAddEstimate(blend, blendW, color); // unpremultiply return Numerics.UnPremultiply(color, alpha); @@ -354,11 +916,36 @@ public static Vector256 Atop(Vector256 destination, Vector256 sW = Avx.Permute(source, ShuffleAlphaControl); - Vector256 blendW = Avx.Multiply(sW, alpha); - Vector256 dstW = Avx.Subtract(alpha, blendW); + Vector256 blendW = sW * alpha; + Vector256 dstW = alpha - blendW; // calculate final color - Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(blend, blendW), destination, dstW); + Vector256 color = Vector256_.MultiplyAddEstimate(destination, dstW, Avx.Multiply(blend, blendW)); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + + /// + /// Returns the result of the "Atop" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Atop(Vector512 destination, Vector512 source, Vector512 blend) + { + // calculate final alpha + Vector512 alpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + + // calculate weights + Vector512 sW = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 blendW = sW * alpha; + Vector512 dstW = alpha - blendW; + + // calculate final color + Vector512 color = Vector512_.MultiplyAddEstimate(destination, dstW, blend * blendW); // unpremultiply return Numerics.UnPremultiply(color, alpha); @@ -392,10 +979,29 @@ public static Vector4 In(Vector4 destination, Vector4 source) public static Vector256 In(Vector256 destination, Vector256 source) { // calculate alpha - Vector256 alpha = Avx.Permute(Avx.Multiply(source, destination), ShuffleAlphaControl); + Vector256 alpha = Avx.Permute(source * destination, ShuffleAlphaControl); + + // premultiply + Vector256 color = source * alpha; + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 In(Vector512 destination, Vector512 source) + { + // calculate alpha + Vector512 alpha = Vector512_.ShuffleNative(source * destination, ShuffleAlphaControl); // premultiply - Vector256 color = Avx.Multiply(source, alpha); + Vector512 color = source * alpha; // unpremultiply return Numerics.UnPremultiply(color, alpha); @@ -429,10 +1035,29 @@ public static Vector4 Out(Vector4 destination, Vector4 source) public static Vector256 Out(Vector256 destination, Vector256 source) { // calculate alpha - Vector256 alpha = Avx.Permute(Avx.Multiply(source, Avx.Subtract(Vector256.Create(1F), destination)), ShuffleAlphaControl); + Vector256 alpha = Avx.Permute(source * (Vector256.Create(1F) - destination), ShuffleAlphaControl); + + // premultiply + Vector256 color = source * alpha; + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + + /// + /// Returns the result of the "Out" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Out(Vector512 destination, Vector512 source) + { + // calculate alpha + Vector512 alpha = Vector512_.ShuffleNative(source * (Vector512.Create(1F) - destination), ShuffleAlphaControl); // premultiply - Vector256 color = Avx.Multiply(source, alpha); + Vector512 color = source * alpha; // unpremultiply return Numerics.UnPremultiply(color, alpha); @@ -475,20 +1100,103 @@ public static Vector256 Xor(Vector256 destination, Vector256 dW = Avx.Shuffle(destination, destination, ShuffleAlphaControl); Vector256 vOne = Vector256.Create(1F); - Vector256 srcW = Avx.Subtract(vOne, dW); - Vector256 dstW = Avx.Subtract(vOne, sW); + Vector256 srcW = vOne - dW; + Vector256 dstW = vOne - sW; + + // calculate alpha + Vector256 alpha = Vector256_.MultiplyAddEstimate(sW, srcW, Avx.Multiply(dW, dstW)); + Vector256 color = Vector256_.MultiplyAddEstimate(Avx.Multiply(sW, source), srcW, Avx.Multiply(Avx.Multiply(dW, destination), dstW)); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + + /// + /// Returns the result of the "XOr" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Xor(Vector512 destination, Vector512 source) + { + // calculate weights + Vector512 sW = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 dW = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + + Vector512 vOne = Vector512.Create(1F); + Vector512 srcW = vOne - dW; + Vector512 dstW = vOne - sW; // calculate alpha - Vector256 alpha = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(dW, dstW), sW, srcW); - Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(Avx.Multiply(dW, destination), dstW), Avx.Multiply(sW, source), srcW); + Vector512 alpha = Vector512_.MultiplyAddEstimate(sW, srcW, dW * dstW); + Vector512 color = Vector512_.MultiplyAddEstimate(sW * source, srcW, (dW * destination) * dstW); // unpremultiply return Numerics.UnPremultiply(color, alpha); } + /// + /// Returns the result of the "Plus" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The blended source and destination color. + /// The composition result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Plus(Vector4 destination, Vector4 source, Vector4 blend) + { + Vector4 sourceAlpha = Numerics.PermuteW(source); + Vector4 destinationAlpha = Numerics.PermuteW(destination); + Vector4 overlap = sourceAlpha * destinationAlpha; + + // Plus retains both non-overlapping regions and uses the selected blend mode in their overlap. + Vector4 color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap); + Vector4 alpha = Vector4.Min(Vector4.One, sourceAlpha + destinationAlpha); + color = Vector4.Min(Vector4.One, color); + + Numerics.UnPremultiply(ref color, alpha); + return color; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Plus(Vector256 destination, Vector256 source, Vector256 blend) + { + Vector256 one = Vector256.Create(1F); + Vector256 sourceAlpha = Avx.Permute(source, ShuffleAlphaControl); + Vector256 destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl); + Vector256 overlap = sourceAlpha * destinationAlpha; + Vector256 color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap); + Vector256 alpha = Vector256.Min(one, sourceAlpha + destinationAlpha); + + return Numerics.UnPremultiply(Vector256.Min(one, color), alpha); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Plus(Vector512 destination, Vector512 source, Vector512 blend) + { + Vector512 one = Vector512.Create(1F); + Vector512 sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl); + Vector512 destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl); + Vector512 overlap = sourceAlpha * destinationAlpha; + Vector512 color = (destination * destinationAlpha) + (source * (sourceAlpha - overlap)) + (blend * overlap); + Vector512 alpha = Vector512.Min(one, sourceAlpha + destinationAlpha); + + return Numerics.UnPremultiply(Vector512.Min(one, color), alpha); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 Clear(Vector4 backdrop, Vector4 source) => Vector4.Zero; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector256 Clear(Vector256 backdrop, Vector256 source) => Vector256.Zero; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Clear(Vector512 backdrop, Vector512 source) => Vector512.Zero; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 AlphaMask512() + => Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); } diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 4ed6174386..bb519c1741 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -3,7 +3,7 @@ using System.Buffers; using System.Numerics; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; namespace SixLabors.ImageSharp.PixelFormats; @@ -52,16 +52,276 @@ public void Blend( Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); - Span destinationVectors = buffer.Slice(0, maxLength); - Span backgroundVectors = buffer.Slice(maxLength, maxLength); - Span sourceVectors = buffer.Slice(maxLength * 2, maxLength); + this.Blend( + configuration, + destination, + background, + source, + amount, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends 2 rows together using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Blends a row against a constant source color. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.Blend( + configuration, + destination, + background, + source, + amount, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends a row against a constant source color using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + + this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount); + + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); + + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount, + ReadOnlySpan coverage) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + float amount, + ReadOnlySpan coverage, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage); + + this.FromBlendVector4(configuration, destinationVectors, destination); } /// @@ -83,6 +343,27 @@ public void Blend( ReadOnlySpan amount) => this.Blend(configuration, destination, background, source, amount); + /// + /// Blends 2 rows together using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + Span workingBuffer) + => this.Blend(configuration, destination, background, source, amount, workingBuffer); + /// /// Blends 2 rows together /// @@ -109,18 +390,356 @@ public void Blend( Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); - Span destinationVectors = buffer.Slice(0, maxLength); - Span backgroundVectors = buffer.Slice(maxLength, maxLength); - Span sourceVectors = buffer.Slice(maxLength * 2, maxLength); + this.Blend( + configuration, + destination, + background, + source, + amount, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends a row against a constant source color. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.Blend( + configuration, + destination, + background, + source, + amount, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends 2 rows together using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); - PixelOperations.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale); - PixelOperations.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale); + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); this.BlendFunction(destinationVectors, backgroundVectors, sourceVectors, amount); - PixelOperations.Instance.FromVector4Destructive(configuration, destinationVectors[..maxLength], destination, PixelConversionModifiers.Scale); + this.FromBlendVector4(configuration, destinationVectors, destination); } + /// + /// Blends a row against a constant source color using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + + this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount); + + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + => this.BlendWithCoverage(configuration, destination, background, source, amount, coverage); + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + => this.BlendWithCoverage(configuration, destination, background, source, amount, coverage, workingBuffer); + + /// + /// Blends 2 rows together with per-pixel coverage. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 3); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 3)]); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + + using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(maxLength * 2); + this.BlendWithCoverage( + configuration, + destination, + background, + source, + amount, + coverage, + buffer.Memory.Span[..(maxLength * 2)]); + } + + /// + /// Blends 2 rows together with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// the pixel format of the source span + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 3 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + where TPixelSrc : unmanaged, IPixel + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, maxLength, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 3, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + Span sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + this.ToBlendVector4(configuration, source[..maxLength], sourceVectors); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage); + + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Blends a row against a constant source color with per-pixel coverage using caller-provided temporary vector scratch. + /// + /// to use internally + /// the destination span + /// the background span + /// the source color + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + /// Reusable temporary vector scratch with capacity for at least 2 rows. + public void BlendWithCoverage( + Configuration configuration, + Span destination, + ReadOnlySpan background, + TPixel source, + ReadOnlySpan amount, + ReadOnlySpan coverage, + Span workingBuffer) + { + int maxLength = destination.Length; + Guard.MustBeGreaterThanOrEqualTo(background.Length, maxLength, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, maxLength, nameof(amount.Length)); + Guard.MustBeGreaterThanOrEqualTo(coverage.Length, maxLength, nameof(coverage.Length)); + Guard.MustBeGreaterThanOrEqualTo(workingBuffer.Length, maxLength * 2, nameof(workingBuffer.Length)); + + Span destinationVectors = workingBuffer[..maxLength]; + Span backgroundVectors = workingBuffer.Slice(maxLength, maxLength); + + this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors); + + this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage); + + this.FromBlendVector4(configuration, destinationVectors, destination); + } + + /// + /// Converts source pixels to the scaled-vector representation consumed by this blender. + /// + /// The source pixel format. + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToBlendVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TPixelSource : unmanaged, IPixel + => PixelOperations.Instance.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + /// + /// Converts a source pixel to the vector representation consumed by the blend functions. + /// + /// The source pixel. + /// The source vector. + protected virtual Vector4 ToBlendVector4(TPixel source) + => source.ToUnassociatedScaledVector4(); + + /// + /// Converts blend results from this blender's scaled-vector representation to destination pixels. + /// + /// The configuration. + /// The source vectors. + /// The destination pixels. + protected virtual void FromBlendVector4( + Configuration configuration, + Span source, + Span destination) + => PixelOperations.Instance.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + /// /// Blend 2 rows together. /// @@ -137,6 +756,22 @@ protected abstract void BlendFunction( ReadOnlySpan source, float amount); + /// + /// Blend a row against a constant source color. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount); + /// /// Blend 2 rows together. /// @@ -152,4 +787,124 @@ protected abstract void BlendFunction( ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount); + + /// + /// Blend a row against a constant source color. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount); + + /// + /// Blend 2 rows together with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend a row against a constant source color with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + float amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend 2 rows together with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } + + /// + /// Blend a row against a constant source color with per-pixel coverage. + /// + /// destination span + /// the background span + /// the source color vector + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + /// A span with coverage values between 0 and 1. + protected virtual void BlendWithCoverageFunction( + Span destination, + ReadOnlySpan background, + Vector4 source, + ReadOnlySpan amount, + ReadOnlySpan coverage) + { + this.BlendFunction(destination, background, source, amount); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.BlendWithCoverage(background[i], destination[i], Numerics.Clamp(coverage[i], 0, 1F)); + } + } } diff --git a/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs b/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs index 7aa07cf51a..aa74f61729 100644 --- a/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs +++ b/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs @@ -52,4 +52,49 @@ public enum PixelColorBlendingMode /// Multiplies or screens the colors, depending on the source value. /// HardLight, + + /// + /// Brightens the backdrop color to reflect the source color. + /// + ColorDodge, + + /// + /// Darkens the backdrop color to reflect the source color. + /// + ColorBurn, + + /// + /// Darkens or lightens the colors, depending on the source color. + /// + SoftLight, + + /// + /// Subtracts the darker color from the lighter color. + /// + Difference, + + /// + /// Produces an effect similar to Difference with lower contrast. + /// + Exclusion, + + /// + /// Uses the hue of the source color with the saturation and luminosity of the backdrop color. + /// + Hue, + + /// + /// Uses the saturation of the source color with the hue and luminosity of the backdrop color. + /// + Saturation, + + /// + /// Uses the hue and saturation of the source color with the luminosity of the backdrop color. + /// + Color, + + /// + /// Uses the luminosity of the source color with the hue and saturation of the backdrop color. + /// + Luminosity, } diff --git a/src/ImageSharp/PixelFormats/PixelColorType.cs b/src/ImageSharp/PixelFormats/PixelColorType.cs new file mode 100644 index 0000000000..315c0f6584 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelColorType.cs @@ -0,0 +1,116 @@ +// 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 +{ + /// + /// No color type. + /// + None = 0, + + /// + /// 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, + + /// + /// Represents the Exponent component used in formats like R9G9B9E5. + /// + Exponent = 1 << 4, + + /// + /// Indicates that the color is in luminance (grayscale) format. + /// + 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 << 7, + + /// + /// Indicates that the color is in RGB (Red, Green, Blue) format. + /// + RGB = Red | Green | Blue | (1 << 8), + + /// + /// Indicates that the color is in BGR (Blue, Green, Red) format. + /// + BGR = Blue | Green | Red | (1 << 9), + + /// + /// Represents the Chrominance Blue component. + /// + ChrominanceBlue = 1 << 10, + + /// + /// Represents the Chrominance Red component. + /// + ChrominanceRed = 1 << 11, + + /// + /// Indicates that the color is in YCbCr (Luminance, Chrominance Blue, Chrominance Red) format. + /// + YCbCr = Luminance | ChrominanceBlue | ChrominanceRed | (1 << 12), + + /// + /// Represents the Cyan component in CMYK. + /// + Cyan = 1 << 13, + + /// + /// Represents the Magenta component in CMYK. + /// + Magenta = 1 << 14, + + /// + /// Represents the Yellow component in CMYK. + /// + Yellow = 1 << 15, + + /// + /// Represents the Key (black) component in CMYK and YCCK. + /// + Key = 1 << 16, + + /// + /// 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 << 17 +} diff --git a/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs b/src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs new file mode 100644 index 0000000000..674c9363b5 --- /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/PixelComponentInfo.cs b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs new file mode 100644 index 0000000000..1444b344b6 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelComponentInfo.cs @@ -0,0 +1,122 @@ +// 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 + => 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) + { + throw new ArgumentOutOfRangeException(nameof(count), $"Count {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 ArgumentOutOfRangeException(nameof(precision), $"Precision {precision.Length} 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, bitsPerPixel - 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/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 9db3d30045..8cee6057da 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -1,37 +1,43 @@ -// Copyright (c) Six Labors. +// 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; /// -/// Flags responsible to select additional operations which could be efficiently applied in -/// -/// or -/// -/// knowing the pixel type. +/// Specifies vector representation transformations applied by +/// +/// and +/// +/// during bulk pixel conversion. /// [Flags] public enum PixelConversionModifiers { /// - /// No special operation is selected + /// Preserves the pixel format's native numeric range and alpha representation. /// None = 0, /// - /// Select and instead the standard (non scaled) variants. + /// Requests the scaled numeric range represented by and . /// Scale = 1 << 0, /// - /// Enable alpha premultiplication / unpremultiplication + /// Requests associated alpha for the vector representation. + /// When combined with , associated alpha takes precedence. /// Premultiply = 1 << 1, /// - /// Enable SRGB companding (defined in ). + /// Expands sRGB color components when converting pixels to vectors and compresses linear RGB color components when converting vectors to pixels. /// SRgbCompand = 1 << 2, + + /// + /// Requests unassociated alpha for the vector representation unless is also specified. + /// + UnPremultiply = 1 << 3, } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 0256907121..8a17831a1c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -8,10 +8,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// /// Packed pixel type containing a single 8-bit normalized alpha value. -/// -/// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form. -/// /// +/// +/// and scaled vector conversions return alpha in [0, 1] with zero color components. +/// The storage layout matches DXGI_FORMAT_A8_UNORM. +/// public partial struct A8 : IPixel, IPackedVector { /// @@ -41,7 +42,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,87 +53,122 @@ 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new() { A = this.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(0, 0, 0, this.PackedValue / 255f); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 8), + PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(vector.W); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(0, 0, 0, this.PackedValue / 255F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = source.A; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromAssociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromAssociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromVector4(Vector4 source) => new(Pack(source.W)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromAbgr32(Abgr32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromArgb32(Argb32 source) => new(source.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgr24(Bgr24 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromBgra32(Bgra32 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromL8(L8 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.PackedValue = source.A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromL16(L16 source) => new(byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromLa16(La16 source) => new(source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.PackedValue = byte.MaxValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static A8 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.A)); - /// - [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. @@ -146,7 +182,6 @@ public void ToRgba32(ref Rgba32 dest) /// /// 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); /// @@ -156,7 +191,6 @@ public void ToRgba32(ref Rgba32 dest) public override readonly string ToString() => $"A8({this.PackedValue})"; /// - [MethodImpl(InliningOptions.ShortMethod)] public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); /// @@ -164,6 +198,6 @@ public void ToRgba32(ref Rgba32 dest) /// /// 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 4891abba8c..acd354b7f2 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 Abgr32(byte r, byte g, byte b) /// 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,11 @@ public Abgr32(byte r, byte g, byte b, byte a) /// 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(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -99,9 +102,11 @@ public Abgr32(float r, float g, float b, float a = 1) /// /// 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(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -109,9 +114,9 @@ public Abgr32(Vector3 vector) /// /// 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() => this = Pack(vector); /// /// Initializes a new instance of the struct. @@ -119,48 +124,32 @@ public Abgr32(Vector4 vector) /// /// 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)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [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; } - /// - /// 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. /// @@ -169,7 +158,7 @@ public uint PackedValue /// /// 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); /// @@ -180,164 +169,158 @@ public uint PackedValue /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromAbgr32(this); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.BGR, + PixelAlphaRepresentation.Unassociated); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this = source; + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - // 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; + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromAssociatedScaledVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + /// - [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 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [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 Abgr32 FromVector4(Vector4 source) => Pack(source); /// - [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 FromAbgr32(Abgr32 source) => new() { PackedValue = source.PackedValue }; /// - [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 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(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 FromBgr24(Bgr24 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(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, 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 Abgr32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromL16(L16 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new Abgr32(rgb, rgb, rgb); } /// - [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 Abgr32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromLa32(La32 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); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new Abgr32(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, 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(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; /// @@ -347,7 +330,6 @@ public void FromRgba64(Rgba64 source) 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(); /// @@ -357,38 +339,28 @@ public void FromRgba64(Rgba64 source) /// 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 Abgr32(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs new file mode 100644 index 0000000000..9105a99f7f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs @@ -0,0 +1,273 @@ +// 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 SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing alpha and associated blue, green, and red components as 8-bit unsigned normalized values. +/// Components are stored in alpha, blue, green, and red order from least to most significant byte. +/// +/// +/// The native component, packed, and vector representations use associated alpha. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Abgr32P : IPixel, IPackedVector +{ + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Abgr32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Abgr32P(byte r, byte g, byte b, byte a) + { + this.A = a; + this.B = b; + this.G = g; + this.R = r; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Abgr32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Abgr32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Abgr32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Abgr32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Abgr32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Abgr32P left, Abgr32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Abgr32P left, Abgr32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + // Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint. + return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.BGR, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static Abgr32P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromUnassociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToAbgr32P(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Abgr32P FromAssociatedScaledVector4(Vector4 source) + { + // Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written. + return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToAbgr32P(source); + } + + /// + public static Abgr32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Abgr32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Abgr32P other && this.Equals(other); + + /// + public readonly bool Equals(Abgr32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Abgr32P({this.R}, {this.G}, {this.B}, {this.A})"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Abgr32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + // Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12. + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Abgr32P(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 0c99adb52d..90ae882631 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 Argb32(byte r, byte g, byte b) /// 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,11 @@ public Argb32(byte r, byte g, byte b, byte a) /// 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(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -99,9 +95,11 @@ public Argb32(float r, float g, float b, float a = 1) /// /// 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(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -109,9 +107,9 @@ public Argb32(Vector3 vector) /// /// 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() => this = Pack(vector); /// /// Initializes a new instance of the struct. @@ -119,7 +117,7 @@ public Argb32(Vector4 vector) /// /// The packed value. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(uint packed) : this() => this.Argb = packed; @@ -128,39 +126,23 @@ public Argb32(uint packed) /// public uint Argb { - [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [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; } - /// - /// 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. /// @@ -169,7 +151,7 @@ public uint PackedValue /// /// 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); /// @@ -180,163 +162,158 @@ public uint PackedValue /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromArgb32(this); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.RGB, + PixelAlphaRepresentation.Unassociated); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.PackedValue = source.PackedValue; + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = byte.MaxValue; + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromAssociatedScaledVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(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 Argb32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [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 FromVector4(Vector4 source) => Pack(source); /// - [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 Argb32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// - [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 FromArgb32(Argb32 source) => new() { PackedValue = source.PackedValue }; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [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 Argb32 FromBgr24(Bgr24 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(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B, 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 Argb32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromL16(L16 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new Argb32(rgb, rgb, rgb); } /// - [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 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromLa32(La32 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); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new Argb32(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(source.A)); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, source.A); + + /// + [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(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; /// @@ -346,48 +323,20 @@ public void FromRgba64(Rgba64 source) 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(); - /// - /// Packs the four floats into a color. - /// - /// The x-component - /// 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); - } - - /// - /// 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); - } - /// /// 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 Argb32(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs new file mode 100644 index 0000000000..7c866c8fe3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs @@ -0,0 +1,273 @@ +// 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 SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing alpha and associated red, green, and blue components as 8-bit unsigned normalized values. +/// Components are stored in alpha, red, green, and blue order from least to most significant byte. +/// +/// +/// The native component, packed, and vector representations use associated alpha. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Argb32P : IPixel, IPackedVector +{ + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Argb32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Argb32P(byte r, byte g, byte b, byte a) + { + this.A = a; + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Argb32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Argb32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Argb32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Argb32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Argb32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Argb32P left, Argb32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Argb32P left, Argb32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + // Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint. + return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.Alpha | PixelColorType.RGB, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static Argb32P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromUnassociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToArgb32P(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Argb32P FromAssociatedScaledVector4(Vector4 source) + { + // Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written. + return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToArgb32P(source); + } + + /// + public static Argb32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Argb32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Argb32P other && this.Equals(other); + + /// + public readonly bool Equals(Argb32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Argb32P({this.R}, {this.G}, {this.B}, {this.A})"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Argb32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + // Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12. + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Argb32P(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 aedf4ad198..c64047518e 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; @@ -35,13 +36,16 @@ public partial struct Bgr24 : IPixel [FieldOffset(2)] 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(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgr24(byte r, byte g, byte b) { this.R = r; @@ -49,22 +53,6 @@ public Bgr24(byte r, byte g, byte b) 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. /// @@ -73,7 +61,7 @@ public Bgr24(byte r, byte g, byte b) /// /// 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); /// @@ -84,150 +72,162 @@ public Bgr24(byte r, byte g, byte b) /// /// 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); - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromBgr24(this); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - 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( + PixelComponentInfo.Create(3, 8, 8, 8), + PixelColorType.BGR, + PixelAlphaRepresentation.None); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromAssociatedScaledVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromAssociatedVector4(Vector4 source) { - Rgba32 rgba = default; - rgba.FromVector4(vector); - this.FromRgba32(rgba); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; + source *= MaxBytes; + source += Half; + source = Numerics.Clamp(source, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(source.AsVector128()).AsByte(); + return new Bgr24(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 FromAbgr32(Abgr32 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 FromArgb32(Argb32 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 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [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 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromBgra32(Bgra32 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; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromL16(L16 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new Bgr24(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 FromLa16(La16 source) => new(source.L, source.L, source.L); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromLa32(La32 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); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new Bgr24(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source.Bgr; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr24 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); - /// - [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); /// @@ -237,6 +237,5 @@ public void FromRgba64(Rgba64 source) 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 ac3b6f8299..338c55fe36 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -7,13 +7,17 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// Packed pixel type containing three unsigned normalized values. /// The x and z components use 5 bits, and the y component uses 6 bits. -/// -/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. -/// /// -public partial struct Bgr565 : IPixel, IPackedVector +/// +/// and scaled vector conversions return color components in [0, 1] with an implicit +/// alpha of 1. The storage layout matches DXGI_FORMAT_B5G6R5_UNORM. +/// +/// +/// 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 +30,8 @@ public Bgr565(float x, float y, float z) { } - /// - /// 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 +41,7 @@ public Bgr565(float x, float y, float z) /// /// 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,94 +52,138 @@ public Bgr565(float x, float y, float z) /// /// 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(); + /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector3(), 1F); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 5, 6, 5), + PixelColorType.BGR, + PixelAlphaRepresentation.None); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromAssociatedScaledVector4(Vector4 source) { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector3(), 1F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromUnassociatedVector4(Vector4 source) => FromVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromVector4(source.ToVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromAssociatedVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); + } + + /// + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector3(source.X, source.Y, source.Z)) }; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromVector4(source.ToVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromVector4(source.ToVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromVector4(source.ToVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgr565 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector3 ToVector3() => new( ((this.PackedValue >> 11) & 0x1F) * (1F / 31F), ((this.PackedValue >> 5) & 0x3F) * (1F / 63F), @@ -153,22 +193,20 @@ public void FromVector4(Vector4 vector) 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 6b859cda64..e8405dd2b9 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 Bgra32(byte r, byte g, byte b) /// 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 Bgra32(byte r, byte g, byte b, byte a) /// public uint Bgra { - [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] set => Unsafe.As(ref this) = value; } @@ -98,22 +92,6 @@ public uint PackedValue 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. /// @@ -122,7 +100,7 @@ public uint PackedValue /// /// 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); /// @@ -133,157 +111,153 @@ public uint PackedValue /// /// 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); - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromBgra32(this); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromAssociatedScaledVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + /// - [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 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 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 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(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 Bgra32 FromBgr24(Bgr24 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 Bgra32 FromBgra32(Bgra32 source) => new() { PackedValue = source.PackedValue }; /// - [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 Bgra32 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 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 Bgra32(rgb, rgb, rgb); } /// - [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 Bgra32 FromLa16(La16 source) => new(source.L, source.L, source.L, source.A); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromLa32(La32 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new Bgra32(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(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 FromRgb24(Rgb24 source) => new(source.R, source.G, 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); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32 FromRgba32(Rgba32 source) => new(source.R, source.G, source.B, source.A); + + /// + [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(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); @@ -301,16 +275,14 @@ public void FromRgba64(Rgba64 source) /// 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 Bgra32(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs new file mode 100644 index 0000000000..6ce3138f7c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs @@ -0,0 +1,273 @@ +// 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 SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing associated blue, green, red, and alpha components as 8-bit unsigned normalized values. +/// Components are stored in blue, green, red, and alpha order from least to most significant byte. +/// +/// +/// The native component, packed, and vector representations use associated alpha. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Bgra32P : IPixel, IPackedVector +{ + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Bgra32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Bgra32P(byte r, byte g, byte b, byte a) + { + this.B = b; + this.G = g; + this.R = r; + this.A = a; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Bgra32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Bgra32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Bgra32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Bgra32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Bgra32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Bgra32P left, Bgra32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Bgra32P left, Bgra32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + // Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint. + return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static Bgra32P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromUnassociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToBgra32P(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra32P FromAssociatedScaledVector4(Vector4 source) + { + // Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written. + return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToBgra32P(source); + } + + /// + public static Bgra32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Bgra32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Bgra32P other && this.Equals(other); + + /// + public readonly bool Equals(Bgra32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Bgra32P({this.R}, {this.G}, {this.B}, {this.A})"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Bgra32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + // Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12. + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Bgra32P(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 8ba32c8ac2..17711835a1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -7,11 +7,12 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. -/// -/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four unsigned normalized values using 4 bits per component. /// +/// +/// and scaled vector conversions return all components in [0, 1]. The storage layout matches +/// DXGI_FORMAT_B4G4R4A4_UNORM. +/// public partial struct Bgra4444 : IPixel, IPackedVector { /// @@ -30,7 +31,7 @@ public Bgra4444(float x, float y, float z, float w) /// 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 Bgra4444(Vector4 vector) => this.PackedValue = Pack(vector); /// public ushort PackedValue { get; set; } @@ -43,7 +44,7 @@ public Bgra4444(float x, float y, float z, float w) /// /// 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,113 +55,159 @@ public Bgra4444(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 4, 4, 4, 4), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [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(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra4444 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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); /// - [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 c282f03d89..22a2c1f99f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -7,12 +7,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. -/// The x , y and z components use 5 bits, and the w component uses 1 bit. -/// -/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four unsigned normalized values. +/// The x, y, and z components use 5 bits, and the w component uses 1 bit. /// +/// +/// and scaled vector conversions return all components in [0, 1]. The storage layout matches +/// DXGI_FORMAT_B5G5R5A1_UNORM. +/// public partial struct Bgra5551 : IPixel, IPackedVector { /// @@ -33,7 +34,7 @@ public Bgra5551(float x, float y, float z, float w) /// /// The vector containing the components for the packed vector. /// - public Bgra5551(Vector4 vector) => this.PackedValue = Pack(ref vector); + public Bgra5551(Vector4 vector) => this.PackedValue = Pack(vector); /// public ushort PackedValue { get; set; } @@ -46,7 +47,7 @@ public Bgra5551(float x, float y, float z, float w) /// /// 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,26 +58,19 @@ public Bgra5551(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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, @@ -84,81 +78,134 @@ public Bgra5551(float x, float y, float z, float w) (this.PackedValue >> 15) & 0x01); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this = source; + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 5, 5, 5, 1), + PixelColorType.BGR | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Bgra5551 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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 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 e699e5fe58..c0372ab979 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -3,24 +3,20 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. -/// -/// Ranges from [0, 0, 0, 0] to [255, 255, 255, 255] in vector form. -/// +/// Packed pixel type containing four 8-bit unsigned integer values. /// +/// +/// returns components in [0, 255]. Scaled vector conversions map that range to +/// [0, 1]. The storage layout matches DXGI_FORMAT_R8G8B8A8_UINT. +/// public partial struct Byte4 : IPixel, IPackedVector { - /// - /// 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); + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -30,11 +26,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)) { - var vector = new Vector4(x, y, z, w); - this.PackedValue = Pack(ref 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; } @@ -46,7 +49,7 @@ public Byte4(float x, float y, float z, float w) /// /// 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,103 +60,160 @@ public Byte4(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() => new() { PackedValue = this.PackedValue }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector * 255F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4() / 255f; - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4() / 255F; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.PackedValue & 0xFF, + (this.PackedValue >> 8) & 0xFF, + (this.PackedValue >> 16) & 0xFF, + (this.PackedValue >> 24) & 0xFF); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new( - this.PackedValue & 0xFF, - (this.PackedValue >> 0x8) & 0xFF, - (this.PackedValue >> 0x10) & 0xFF, - (this.PackedValue >> 0x18) & 0xFF); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() + { + Vector4 vector = this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + // Native components use [0, 255], so association occurs in scaled [0, 1] space before mapping the result back. + return vector * MaxBytes; + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromAssociatedVector4(Vector4 source) + { + // Map every native component, including alpha, to scaled [0, 1] space before unassociating. + source /= MaxBytes; + return FromAssociatedScaledVector4(source); + } /// - [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(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Byte4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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); /// - [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.##})"); } @@ -162,13 +222,10 @@ public override readonly string ToString() /// /// 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; - - // Clamp the value between min and max values - vector = Numerics.Clamp(vector, Vector4.Zero, new Vector4(Max)); + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); uint byte4 = (uint)Math.Round(vector.X) & 0xFF; uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 0x8; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index db1e02adc2..cbb0cf0866 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -7,11 +7,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing a single 16 bit floating point value. -/// -/// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form. -/// +/// Packed pixel type containing a single IEEE 754 binary16 floating-point value. /// +/// +/// returns the stored IEEE 754 binary16 value directly. Scaled vector conversions normalize +/// the finite range [-65504, 65504] to [0, 1]. The packed representation is binary-compatible with +/// DXGI_FORMAT_R16_FLOAT. +/// public partial struct HalfSingle : IPixel, IPackedVector { /// @@ -31,7 +33,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,113 +44,154 @@ 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 PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() { - float scaled = vector.X; - scaled *= 2F; - scaled--; - this.PackedValue = HalfTypeHelper.Pack(scaled); + float scaled = HalfTypeHelper.ToScaled(this.ToSingle()); + return new Vector4(scaled, 0, 0, 1F); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToSingle(), 0, 0, 1F); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 16), + PixelColorType.Red, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromAssociatedScaledVector4(Vector4 source) { - float single = this.ToSingle() + 1F; - single /= 2F; - return new Vector4(single, 0, 0, 1F); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = HalfTypeHelper.Pack(vector.X); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromUnassociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToSingle(), 0, 0, 1F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromAssociatedVector4(Vector4 source) + { + // This format has no native alpha component: X uses the binary16 finite range, while W remains the source opacity. + source.X = HalfTypeHelper.ToScaled(source.X); + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromScaledVector4(Vector4 source) + => new() { PackedValue = HalfTypeHelper.Pack(HalfTypeHelper.FromScaled(source.X)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromVector4(Vector4 source) => new() { PackedValue = HalfTypeHelper.Pack(source.X) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfSingle FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); /// /// 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 9caae58c95..ad6c7aefab 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -7,11 +7,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing two 16-bit floating-point values. -/// -/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. -/// +/// Packed pixel type containing two IEEE 754 binary16 floating-point values. /// +/// +/// and return the stored IEEE 754 binary16 values directly. Scaled +/// vector conversions normalize the finite range [-65504, 65504] to [0, 1]. The packed representation is +/// binary-compatible with DXGI_FORMAT_R16G16_FLOAT. +/// public partial struct HalfVector2 : IPixel, IPackedVector { /// @@ -38,7 +40,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,104 +51,152 @@ 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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(); - scaled += Vector2.One; - scaled /= 2F; + Vector2 scaled = HalfTypeHelper.ToScaled(this.ToVector2()); return new Vector4(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(); + Vector2 vector = this.ToVector2(); return new Vector4(vector.X, vector.Y, 0F, 1F); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 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 FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromAssociatedScaledVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromAssociatedVector4(Vector4 source) + { + // This format has no native alpha component: XY use the binary16 finite range, while W remains the source opacity. + Vector2 scaled = HalfTypeHelper.ToScaled(new Vector2(source.X, source.Y)); + source.X = scaled.X; + source.Y = scaled.Y; + return FromAssociatedScaledVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromScaledVector4(Vector4 source) + { + Vector2 native = HalfTypeHelper.FromScaled(new Vector2(source.X, source.Y)); + return new HalfVector2 { PackedValue = Pack(native.X, native.Y) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromVector4(Vector4 source) => new() { PackedValue = Pack(source.X, source.Y) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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 . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { Vector2 vector; @@ -159,21 +209,19 @@ public readonly Vector2 ToVector2() 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 609fec3bd7..42531937cb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -7,11 +7,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing four 16-bit floating-point values. -/// -/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four IEEE 754 binary16 floating-point values. /// +/// +/// returns the stored IEEE 754 binary16 values directly. Scaled vector conversions normalize +/// the finite range [-65504, 65504] to [0, 1]. The packed representation is binary-compatible with +/// DXGI_FORMAT_R16G16B16A16_FLOAT. +/// public partial struct HalfVector4 : IPixel, IPackedVector { /// @@ -30,7 +32,7 @@ public HalfVector4(float x, float y, float z, float w) /// 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 HalfVector4(Vector4 vector) => this.PackedValue = Pack(vector); /// public ulong PackedValue { get; set; } @@ -43,7 +45,7 @@ public HalfVector4(float x, float y, float z, float w) /// /// 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,115 +56,161 @@ public HalfVector4(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => HalfTypeHelper.ToScaled(this.ToVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + HalfTypeHelper.Unpack((ushort)this.PackedValue), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - vector *= 2F; - vector -= Vector4.One; - this.FromVector4(vector); + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; + Vector4 vector = this.ToAssociatedScaledVector4(); + + // Association is defined in scaled color space, so map the associated result back to the native binary16 range. + return HalfTypeHelper.FromScaled(vector); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new( - HalfTypeHelper.Unpack((ushort)this.PackedValue), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromUnassociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromAssociatedVector4(Vector4 source) + { + // Restore scaled opacity before unassociating the color channels. + source = HalfTypeHelper.ToScaled(source); + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromScaledVector4(Vector4 source) => FromVector4(HalfTypeHelper.FromScaled(source)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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); /// - [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(); /// @@ -170,8 +218,8 @@ public override readonly string ToString() /// /// 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/HalfVector4P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs new file mode 100644 index 0000000000..ca90afcf75 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs @@ -0,0 +1,270 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing four associated 16-bit floating-point values. +/// +/// +/// returns the stored associated IEEE 754 binary16 values directly. Scaled vector conversions +/// normalize the finite range [-65504, 65504] to [0, 1] while preserving associated alpha. The packed +/// representation is binary-compatible with DXGI_FORMAT_R16G16B16A16_FLOAT. +/// +public partial struct HalfVector4P : IPixel, IPackedVector +{ + /// + /// Initializes a new instance of the struct. + /// + /// The associated x-component. + /// The associated y-component. + /// The associated z-component. + /// The alpha component. + public HalfVector4P(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the associated component values. + public HalfVector4P(Vector4 vector) => this.PackedValue = Pack(vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(HalfVector4P left, HalfVector4P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(HalfVector4P left, HalfVector4P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + { + return Rgba32.FromScaledVector4(this.ToUnassociatedScaledVector4()); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => HalfTypeHelper.ToScaled(this.ToVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.UnPremultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() => new( + HalfTypeHelper.Unpack((ushort)this.PackedValue), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() + { + Vector4 vector = this.ToUnassociatedScaledVector4(); + return HalfTypeHelper.FromScaled(vector); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + public static HalfVector4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static HalfVector4P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4P FromUnassociatedVector4(Vector4 source) + { + source = HalfTypeHelper.ToScaled(source); + return FromUnassociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4P FromAssociatedVector4(Vector4 source) + { + source = HalfTypeHelper.ToScaled(source); + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HalfVector4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source)); + + /// + public static HalfVector4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static HalfVector4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is HalfVector4P other && this.Equals(other); + + /// + public readonly bool Equals(HalfVector4P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"HalfVector4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + /// Converts an unassociated scaled vector to the associated representation of a half-precision destination. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Associate(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + + // RGB must use the scaled alpha that the binary16 representation can reproduce. + source.W = QuantizeScaledAlpha(source.W); + Numerics.Premultiply(ref source); + return source; + } + + /// + /// Reassociates a scaled vector with the alpha value the destination stores. + /// + /// The associated scaled vector. + /// The reassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Reassociate(Vector4 source) + { + float alpha = source.W; + + if (alpha <= 0) + { + return Vector4.Zero; + } + + float storedAlpha = QuantizeScaledAlpha(alpha); + + // Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization. + source *= storedAlpha / alpha; + source.W = storedAlpha; + Numerics.ClampRgbToAlpha(ref source); + return source; + } + + /// + /// Packs an associated scaled vector into the native binary16 representation. + /// + /// The associated scaled vector. + /// The packed pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static HalfVector4P PackAssociatedScaledVector4(Vector4 source) + { + source = HalfTypeHelper.FromScaled(source); + return new HalfVector4P { PackedValue = Pack(source) }; + } + + /// + /// Quantizes scaled alpha through the native binary16 representation. + /// + /// The scaled alpha value. + /// The scaled value represented by the stored binary16 component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float QuantizeScaledAlpha(float alpha) + { + float nativeAlpha = HalfTypeHelper.FromScaled(alpha); + return HalfTypeHelper.ToScaled(HalfTypeHelper.Unpack(HalfTypeHelper.Pack(nativeAlpha))); + } + + /// + /// Packs native half-precision components into a 64-bit value. + /// + /// The native component values. + /// The packed value. + private static ulong Pack(Vector4 vector) + { + ulong x = HalfTypeHelper.Pack(vector.X); + ulong y = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; + ulong z = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20; + ulong w = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; + return x | y | z | w; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index c6ee8744d9..1ab8e9650b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -33,7 +33,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,134 +44,157 @@ 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = ColorNumerics.From16BitTo8Bit(this.PackedValue); + return new Rgba32(rgb, rgb, rgb); + } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { float scaled = this.PackedValue / Max; - return new Vector4(scaled, scaled, scaled, 1F); + return new Vector4(scaled, scaled, scaled, 1f); + } + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(1, 16), + PixelColorType.Luminance, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromAssociatedScaledVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L16 FromAssociatedVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); } /// - [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 FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [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 FromAbgr32(Abgr32 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 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(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 FromBgr24(Bgr24 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 FromBgra32(Bgra32 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 383e09b270..9908e03067 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; @@ -14,8 +15,8 @@ namespace SixLabors.ImageSharp.PixelFormats; /// public partial struct L8 : IPixel, IPackedVector { - private static readonly Vector4 MaxBytes = new(255F); - 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. @@ -34,7 +35,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,122 +46,161 @@ 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = this.PackedValue; + return new Rgba32(rgb, rgb, rgb); + } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [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( + PixelComponentInfo.Create(1, 8), + PixelColorType.Luminance, + PixelAlphaRepresentation.None); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromAssociatedScaledVector4(Vector4 source) { - float rgb = this.PackedValue / 255F; - return new Vector4(rgb, rgb, rgb, 1F); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(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 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromAssociatedVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 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 FromAbgr32(Abgr32 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(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgr24(Bgr24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromBgra32(Bgra32 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromL8(L8 source) => new(source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.PackedValue = source.L; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromL16(L16 source) => new(ColorNumerics.From16BitTo8Bit(source.PackedValue)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static L8 FromLa16(La16 source) => new(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 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.L)); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 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 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 7597677a26..3a15f24d51 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; @@ -16,8 +17,15 @@ namespace SixLabors.ImageSharp.PixelFormats; [StructLayout(LayoutKind.Explicit)] public partial struct La16 : 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. @@ -45,7 +53,7 @@ public La16(byte l, byte a) /// 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; } @@ -57,7 +65,7 @@ public ushort PackedValue /// /// 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,167 +76,159 @@ public ushort PackedValue /// /// 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); + /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); + [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 override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Luminance | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); - /// - public override readonly string ToString() => $"La16({this.L}, {this.A})"; + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [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 readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromAssociatedScaledVector4(Vector4 source) { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + /// - [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 FromScaledVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.A = byte.MaxValue; - } + [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 FromL8(L8 source) - { - this.L = source.PackedValue; - 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(MethodImplOptions.AggressiveInlining)] + public static La16 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this = source; + [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 FromLa32(La32 source) - { - this.L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(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 FromRgb24(Rgb24 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); - 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 FromRgb48(Rgb48 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromL8(L8 source) => new(source.PackedValue, byte.MaxValue); - this.A = byte.MaxValue; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromLa16(La16 source) => new(source.L, 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 FromLa32(La32 source) => new(ColorNumerics.From16BitTo8Bit(source.L), ColorNumerics.From16BitTo8Bit(source.A)); /// - public void FromRgba64(Rgba64 source) - { - this.L = ColorNumerics.Get8BitBT709Luminance( - ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), - ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromRgb24(Rgb24 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); - this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); - } + /// + [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 FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La16 FromRgb48(Rgb48 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), byte.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + public static La16 FromRgba64(Rgba64 source) => new(ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B), ColorNumerics.From16BitTo8Bit(source.A)); /// - [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; - } + public override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); - /// - [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 string ToString() => $"La16({this.L}, {this.A})"; + + /// + 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 La16(l, a); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index cb8fad228d..ae3190d964 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -44,10 +44,10 @@ public La32(ushort l, ushort a) /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [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 +59,7 @@ public uint PackedValue /// /// 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,186 +70,187 @@ public uint PackedValue /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() + { + byte rgb = ColorNumerics.From16BitTo8Bit(this.L); + return new Rgba32(rgb, rgb, rgb, ColorNumerics.From16BitTo8Bit(this.A)); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); - /// - public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + float rgb = this.L / Max; + return new Vector4(rgb, rgb, rgb, this.A / Max); + } /// - public override readonly string ToString() => $"La32({this.L}, {this.A})"; + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Luminance | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); - - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - this.A = ushort.MaxValue; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromScaledVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) - { - this.L = source.PackedValue; - this.A = ushort.MaxValue; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromVector4(Vector4 source) => Pack(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromAbgr32(Abgr32 source) { - this.L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); - this.A = ushort.MaxValue; + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new La32(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromArgb32(Argb32 source) { - this.L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new La32(l, a); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this = source; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [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 FromBgr24(Bgr24 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromBgra32(Bgra32 source) { - this.L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); - this.A = ushort.MaxValue; + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new La32(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) - { - this.L = ColorNumerics.Get16BitBT709Luminance( - ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), - ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromL16(L16 source) => new(source.PackedValue, ushort.MaxValue); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromL8(L8 source) => new(ColorNumerics.From8BitTo16Bit(source.PackedValue), ushort.MaxValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromLa16(La16 source) { - this.L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); - this.A = source.A; + ushort l = ColorNumerics.From8BitTo16Bit(source.L); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new La32(l, a); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromLa32(La32 source) => new(source.L, source.A); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + [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 ToRgba32(ref Rgba32 dest) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgb48(Rgb48 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(this.L); - dest.R = rgb; - dest.G = rgb; - dest.B = rgb; - dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + return new La32(l, ushort.MaxValue); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgba32(Rgba32 source) { - float scaled = this.L / Max; - return new Vector4(scaled, scaled, scaled, this.A / Max); + ushort l = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); + ushort a = ColorNumerics.From8BitTo16Bit(source.A); + return new La32(l, a); } - [MethodImpl(InliningOptions.ShortMethod)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static La32 FromRgba64(Rgba64 source) => new(ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B), source.A); + + /// + public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other); + + /// + public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly string ToString() => $"La32({this.L}, {this.A})"; + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [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 La32(l, a); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index 92b9e6148a..6039bd9d7c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -7,17 +7,21 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. -/// -/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. -/// +/// Packed pixel type containing two 8-bit signed normalized values. /// +/// +/// and return components in the native signed-normalized range +/// [-1, 1]. Scaled vector conversions return components in [0, 1]. +/// The packed two's-complement codes -128 and -127 both represent -1, +/// matching DXGI_FORMAT_R8G8_SNORM. +/// public partial struct NormalizedByte2 : IPixel, IPackedVector { - private const float MaxPos = 127F; - + private const float MaxPos = 127f; + private const float ScaledMagnitude = MaxPos * 2F; private static readonly Vector2 Half = new(MaxPos); - private static readonly Vector2 MinusOne = new(-1F); + private static readonly Vector2 Minimum = new(-MaxPos); + private static readonly Vector2 MinusOne = new(-1f); /// /// Initializes a new instance of the struct. @@ -46,7 +50,7 @@ public NormalizedByte2(float x, float y) /// /// 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,128 +61,183 @@ public NormalizedByte2(float x, float y) /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled); + Vector2 scaled = new( + (sbyte)(this.PackedValue & 0xFF), + (sbyte)(this.PackedValue >> 8)); + + // SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -128 cannot escape the scaled range. + scaled = Vector2.Max(scaled, Minimum); + scaled += Half; + scaled /= ScaledMagnitude; + return new Vector4(scaled, 0f, 1f); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromAssociatedScaledVector4(Vector4 source) { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromAssociatedVector4(Vector4 source) { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); + // Only the stored color components use the native [-1, 1] encoding; W remains the normalized source alpha. + source.X = (source.X + 1F) / 2F; + source.Y = (source.Y + 1F) / 2F; + return FromAssociatedScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromScaledVector4(Vector4 source) + { + Vector2 scaled = new Vector2(source.X, source.Y) * 2f; + scaled -= Vector2.One; + return new NormalizedByte2 { PackedValue = Pack(scaled) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector2 ToVector2() => new( - (sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos, - (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector2 ToVector2() + { + Vector2 vector = new( + (sbyte)(this.PackedValue & 0xFF), + (sbyte)(this.PackedValue >> 8)); + + // DirectX SNORM maps both -128 and -127 to -1. + return Vector2.Max(vector, Minimum) / MaxPos; + } /// public override readonly bool Equals(object? obj) => 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 f87bb5a602..2f91718cf8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -3,21 +3,26 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. -/// -/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four 8-bit signed normalized values. /// +/// +/// returns components in the native signed-normalized range [-1, 1]. +/// Scaled vector conversions return components in [0, 1]. +/// The packed two's-complement codes -128 and -127 both represent -1, +/// matching DXGI_FORMAT_R8G8B8A8_SNORM. +/// public partial struct NormalizedByte4 : 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 const float ScaledMagnitude = MaxPos * 2F; + private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); + private static readonly Vector4 Minimum = -Half; + private static readonly Vector4 MinusOne = Vector128.Create(-1f).AsVector4(); /// /// Initializes a new instance of the struct. @@ -35,7 +40,7 @@ public NormalizedByte4(float x, float y, float z, float w) /// Initializes a new instance of the struct. /// /// The vector containing the component values. - public NormalizedByte4(Vector4 vector) => this.PackedValue = Pack(ref vector); + public NormalizedByte4(Vector4 vector) => this.PackedValue = Pack(vector); /// public uint PackedValue { get; set; } @@ -48,7 +53,7 @@ public NormalizedByte4(float x, float y, float z, float w) /// /// 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,119 +64,190 @@ public NormalizedByte4(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() + { + // Offset the exact signed components before division. Mapping an already normalized value through (value + 1) / 2 loses precision near -1 through cancellation. + Vector4 scaled = new( + (sbyte)((this.PackedValue >> 0) & 0xFF), + (sbyte)((this.PackedValue >> 8) & 0xFF), + (sbyte)((this.PackedValue >> 16) & 0xFF), + (sbyte)((this.PackedValue >> 24) & 0xFF)); + + // SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -128 cannot escape the scaled range. + return (Vector4.Max(scaled, Minimum) + Half) / ScaledMagnitude; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + Vector4 vector = new( + (sbyte)((this.PackedValue >> 0) & 0xFF), + (sbyte)((this.PackedValue >> 8) & 0xFF), + (sbyte)((this.PackedValue >> 16) & 0xFF), + (sbyte)((this.PackedValue >> 24) & 0xFF)); + + // DirectX SNORM maps both -128 and -127 to -1. + return Vector4.Max(vector, Minimum) / MaxPos; + } + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() { + Vector4 vector = this.ToAssociatedScaledVector4(); + + // Native components use an affine [-1, 1] encoding, so direct multiplication would use the wrong zero point. vector *= 2F; vector -= Vector4.One; - this.FromVector4(vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromAssociatedScaledVector4(Vector4 source) { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromUnassociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new( - (sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos, - (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos, - (sbyte)((this.PackedValue >> 16) & 0xFF) / MaxPos, - (sbyte)((this.PackedValue >> 24) & 0xFF) / MaxPos); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromAssociatedVector4(Vector4 source) + { + // Map the affine native encoding to logical [0, 1] space before unassociating. + source += Vector4.One; + source /= 2F; + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromScaledVector4(Vector4 source) + { + source *= 2f; + source -= Vector4.One; + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4 FromRgba64(Rgba64 source) => 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/NormalizedByte4P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs new file mode 100644 index 0000000000..1172aafb95 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs @@ -0,0 +1,318 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing four associated 8-bit signed normalized values ranging from -1 to 1. +/// +/// +/// returns associated components in the native signed-normalized range [-1, 1]. +/// Scaled vector conversions return associated components in [0, 1]. +/// The packed two's-complement codes -128 and -127 both represent -1, +/// matching DXGI_FORMAT_R8G8B8A8_SNORM. +/// +public partial struct NormalizedByte4P : IPixel, IPackedVector +{ + private const float MaxPos = 127F; + private const float ScaledMagnitude = MaxPos * 2F; + private static readonly Vector4 Half = Vector128.Create(MaxPos).AsVector4(); + private static readonly Vector4 Minimum = -Half; + private static readonly Vector4 MinusOne = Vector128.Create(-1F).AsVector4(); + + /// + /// Initializes a new instance of the struct. + /// + /// The associated x-component. + /// The associated y-component. + /// The associated z-component. + /// The alpha component. + public NormalizedByte4P(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the associated component values. + public NormalizedByte4P(Vector4 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(NormalizedByte4P left, NormalizedByte4P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(NormalizedByte4P left, NormalizedByte4P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(ToUnassociatedScaledVector4(this)); + + /// + public readonly Vector4 ToScaledVector4() + { + // Offset the exact signed components before division. Mapping an already normalized value through (value + 1) / 2 loses precision near -1 through cancellation. + Vector4 scaled = new( + (sbyte)((this.PackedValue >> 0) & 0xFF), + (sbyte)((this.PackedValue >> 8) & 0xFF), + (sbyte)((this.PackedValue >> 16) & 0xFF), + (sbyte)((this.PackedValue >> 24) & 0xFF)); + + // SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -128 cannot escape the scaled range. + return (Vector4.Max(scaled, Minimum) + Half) / ScaledMagnitude; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => ToUnassociatedScaledVector4(this); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() + { + Vector4 vector = new( + (sbyte)((this.PackedValue >> 0) & 0xFF), + (sbyte)((this.PackedValue >> 8) & 0xFF), + (sbyte)((this.PackedValue >> 16) & 0xFF), + (sbyte)((this.PackedValue >> 24) & 0xFF)); + + // DirectX SNORM maps both -128 and -127 to -1. + return Vector4.Max(vector, Minimum) / MaxPos; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() + { + // Association is defined in the common scaled domain. Signed-native W is an affine encoding of alpha, so it cannot be used as a divisor directly. + Vector4 vector = this.ToUnassociatedScaledVector4(); + vector *= 2F; + vector -= Vector4.One; + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + public static NormalizedByte4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static NormalizedByte4P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4P FromUnassociatedVector4(Vector4 source) + { + // Convert the signed-native range to the common scaled domain before associating because native W is not opacity. + source += Vector4.One; + source /= 2F; + return FromUnassociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4P FromAssociatedVector4(Vector4 source) + { + // Reassociation must also operate in scaled space so RGB follows the quantized scaled alpha rather than the signed-native W component. + source += Vector4.One; + source /= 2F; + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedByte4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source)); + + /// + /// Packs an associated scaled vector into signed-normalized storage. + /// + /// The associated scaled vector. + /// The packed pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static NormalizedByte4P PackAssociatedScaledVector4(Vector4 source) + { + // Signed-normalized storage uses the native [-1, 1] range even though association is defined in scaled opacity space. + source *= 2F; + source -= Vector4.One; + return new() { PackedValue = Pack(source) }; + } + + /// + public static NormalizedByte4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static NormalizedByte4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is NormalizedByte4P other && this.Equals(other); + + /// + public readonly bool Equals(NormalizedByte4P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"NormalizedByte4P({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + /// Converts an unassociated scaled vector to the associated representation of a signed-normalized-byte destination. + /// + /// The unassociated scaled vector. + /// The associated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Associate(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + + // Reproduce the signed-normalized packer's alpha quantization, then associate RGB with the exact alpha that will be stored. + float nativeAlpha = Numerics.Clamp((source.W * 2F) - 1F, -1F, 1F); + float storedAlpha = MathF.Round(nativeAlpha * MaxPos); + source.W = (storedAlpha + MaxPos) / ScaledMagnitude; + Numerics.Premultiply(ref source); + return source; + } + + /// + /// Converts the stored associated components to an unassociated scaled vector. + /// + /// The associated pixel. + /// The unassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ToUnassociatedScaledVector4(NormalizedByte4P source) + { + // Offset signed storage into exact nonnegative byte magnitudes before division so the quotient retains the destination's byte-rounding midpoint. + Vector4 vector = new( + (sbyte)(source.PackedValue >> 0), + (sbyte)(source.PackedValue >> 8), + (sbyte)(source.PackedValue >> 16), + (sbyte)(source.PackedValue >> 24)); + + // Clamp the duplicate SNORM minimum encoding before converting it to the nonnegative associated domain. + vector = Vector4.Max(vector, Minimum) + Half; + + if (vector.W == 0F) + { + // Numerics.UnPremultiply preserves RGB when alpha is zero. Normalize the stored components because they already are the unassociated value in this case. + return vector / ScaledMagnitude; + } + + Numerics.UnPremultiply(ref vector); + vector.W /= ScaledMagnitude; + return vector; + } + + /// + /// Reassociates a scaled vector with the alpha value the destination stores. + /// + /// The associated scaled vector. + /// The reassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Reassociate(Vector4 source) + { + float alpha = source.W; + + if (alpha <= 0) + { + return Vector4.Zero; + } + + float nativeAlpha = Numerics.Clamp((alpha * 2F) - 1F, -1F, 1F); + float storedAlpha = (MathF.Round(nativeAlpha * MaxPos) + MaxPos) / ScaledMagnitude; + + // Associated RGB scales by the same ratio as alpha. Applying that ratio directly avoids the extra division and multiplication of an unpremultiply/premultiply round trip and preserves exact midpoints when alpha needs no quantization. + source *= storedAlpha / alpha; + source.W = storedAlpha; + Numerics.ClampRgbToAlpha(ref source); + return source; + } + + /// + /// Packs native signed normalized components into a 32-bit value. + /// + /// The native component values. + /// The packed value. + private static uint Pack(Vector4 vector) + { + vector = Numerics.Clamp(vector, MinusOne, Vector4.One) * Half; + + uint byte4 = ((uint)Convert.ToInt16(MathF.Round(vector.X)) & 0xFF) << 0; + uint byte3 = ((uint)Convert.ToInt16(MathF.Round(vector.Y)) & 0xFF) << 8; + uint byte2 = ((uint)Convert.ToInt16(MathF.Round(vector.Z)) & 0xFF) << 16; + uint byte1 = ((uint)Convert.ToInt16(MathF.Round(vector.W)) & 0xFF) << 24; + + return byte4 | byte3 | byte2 | byte1; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index f77dd69b71..3da0f7f0b9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -7,15 +7,19 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. -/// -/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. -/// +/// Packed pixel type containing two 16-bit signed normalized values. /// +/// +/// and return components in the native signed-normalized range +/// [-1, 1]. Scaled vector conversions return components in [0, 1]. +/// The packed two's-complement codes -32768 and -32767 both represent -1, +/// matching DXGI_FORMAT_R16G16_SNORM. +/// public partial struct NormalizedShort2 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; + private const float ScaledMagnitude = MaxPos * 2F; private static readonly Vector2 Max = new(MaxPos); private static readonly Vector2 Min = Vector2.Negate(Max); @@ -47,7 +51,7 @@ public NormalizedShort2(float x, float y) /// /// 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,128 +62,183 @@ public NormalizedShort2(float x, float y) /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled); + Vector2 scaled = new( + (short)(this.PackedValue & 0xFFFF), + (short)(this.PackedValue >> 0x10)); + + // SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -32768 cannot escape the scaled range. + scaled = Vector2.Max(scaled, Min); + scaled += Max; + scaled /= ScaledMagnitude; + return new Vector4(scaled, 0f, 1f); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromAssociatedScaledVector4(Vector4 source) { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromAssociatedVector4(Vector4 source) { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); + // Only the stored color components use the native [-1, 1] encoding; W remains the normalized source alpha. + source.X = (source.X + 1F) / 2F; + source.Y = (source.Y + 1F) / 2F; + return FromAssociatedScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.ToVector2(), 0, 1); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromScaledVector4(Vector4 source) + { + Vector2 scaled = new Vector2(source.X, source.Y) * 2f; + scaled -= Vector2.One; + return new NormalizedShort2 { PackedValue = Pack(scaled) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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. /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector2 ToVector2() => new( - (short)(this.PackedValue & 0xFFFF) / MaxPos, - (short)(this.PackedValue >> 0x10) / MaxPos); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector2 ToVector2() + { + Vector2 vector = new( + (short)(this.PackedValue & 0xFFFF), + (short)(this.PackedValue >> 0x10)); + + // DirectX SNORM maps both -32768 and -32767 to -1. + return Vector2.Max(vector, Min) / MaxPos; + } /// 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 989edbd2ba..7acac0b7dd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -3,21 +3,25 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. -/// -/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four 16-bit signed normalized values. /// +/// +/// returns components in the native signed-normalized range [-1, 1]. +/// Scaled vector conversions return components in [0, 1]. +/// The packed two's-complement codes -32768 and -32767 both represent -1, +/// matching DXGI_FORMAT_R16G16B16A16_SNORM. +/// public partial struct NormalizedShort4 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; private const float MaxPos = 0x7FFF; - - private static readonly Vector4 Max = new(MaxPos); + private const float ScaledMagnitude = MaxPos * 2F; + private static readonly Vector4 Max = Vector128.Create(MaxPos).AsVector4(); private static readonly Vector4 Min = Vector4.Negate(Max); /// @@ -36,7 +40,7 @@ public NormalizedShort4(float x, float y, float z, float w) /// Initializes a new instance of the struct. /// /// The vector containing the component values. - public NormalizedShort4(Vector4 vector) => this.PackedValue = Pack(ref vector); + public NormalizedShort4(Vector4 vector) => this.PackedValue = Pack(vector); /// public ulong PackedValue { get; set; } @@ -49,7 +53,7 @@ public NormalizedShort4(float x, float y, float z, float w) /// /// 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,119 +64,190 @@ public NormalizedShort4(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() + { + // Offset the exact signed components before division. Mapping an already normalized value through (value + 1) / 2 loses precision near -1 through cancellation. + Vector4 scaled = new( + (short)((this.PackedValue >> 0x00) & 0xFFFF), + (short)((this.PackedValue >> 0x10) & 0xFFFF), + (short)((this.PackedValue >> 0x20) & 0xFFFF), + (short)((this.PackedValue >> 0x30) & 0xFFFF)); + + // SNORM reserves both minimum two's-complement codes for -1. Clamp before offsetting so raw -32768 cannot escape the scaled range. + return (Vector4.Max(scaled, Min) + Max) / ScaledMagnitude; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() { + Vector4 vector = new( + (short)((this.PackedValue >> 0x00) & 0xFFFF), + (short)((this.PackedValue >> 0x10) & 0xFFFF), + (short)((this.PackedValue >> 0x20) & 0xFFFF), + (short)((this.PackedValue >> 0x30) & 0xFFFF)); + + // DirectX SNORM maps both -32768 and -32767 to -1. + return Vector4.Max(vector, Min) / MaxPos; + } + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() + { + Vector4 vector = this.ToAssociatedScaledVector4(); + + // Native components use an affine [-1, 1] encoding, so direct multiplication would use the wrong zero point. vector *= 2F; vector -= Vector4.One; - this.FromVector4(vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromAssociatedScaledVector4(Vector4 source) { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromUnassociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new( - (short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxPos, - (short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxPos, - (short)((this.PackedValue >> 0x20) & 0xFFFF) / MaxPos, - (short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxPos); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromAssociatedVector4(Vector4 source) + { + // Map the affine native encoding to logical [0, 1] space before unassociating. + source += Vector4.One; + source /= 2F; + return FromAssociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromScaledVector4(Vector4 source) + { + source *= 2f; + source -= Vector4.One; + return FromVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NormalizedShort4 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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); /// - [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/A8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs index a7b4b5df00..da6762912d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,18 @@ public partial struct A8 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + // A8 stores alpha without color components, so association cannot alter any emitted or consumed vector component. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) => this.FromUnassociatedVector4Destructive(configuration, source, destination); + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) => this.FromUnassociatedScaledVector4Destructive(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs index 66f3ecb245..d459fc4d6c 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; /// @@ -13,12 +11,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/Abgr32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs new file mode 100644 index 0000000000..746fbf85fd --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Abgr32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts. + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored. + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + // This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts. + Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32.PixelOperations.cs index 894e92963e..608d618d48 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; /// @@ -13,12 +11,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/Argb32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs new file mode 100644 index 0000000000..80dbd3c318 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Argb32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts. + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored. + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + // This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts. + Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr24.PixelOperations.cs index a8f6ab1556..36ffff7147 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; /// @@ -13,12 +11,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 de96903256..737f6999f7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct Bgr565 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32.PixelOperations.cs index 1a62b0809c..d2c2b2db74 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; /// @@ -13,12 +11,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/Bgra32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs new file mode 100644 index 0000000000..606990d660 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra32P.PixelOperations.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Bgra32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts. + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored. + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + // This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts. + Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgra4444.PixelOperations.cs index 8ffdaf6cb5..1a0be8b6ff 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; /// @@ -13,12 +11,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 97f5d805e6..3a2856125e 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; /// @@ -13,12 +11,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 f6e0b833c2..7f0919919e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Byte4.PixelOperations.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +17,91 @@ public partial struct Byte4 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + // Protected hooks are same-instance extension points. Cross-format reuse must request the representation through public modifier dispatch. + private const PixelConversionModifiers UnassociatedScaledModifiers = PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply; + private const PixelConversionModifiers AssociatedScaledModifiers = PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply; + + private static readonly Vector4 NativeMaximum = new(byte.MaxValue); + + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Byte4 and Rgba32 have the same packed layout. Reusing the optimized Rgba32 unpacker avoids four scalar byte + // extractions per pixel; the affine transform then restores Byte4's native [0, 255] vector range. + PixelOperations.Instance.ToVector4(configuration, MemoryMarshal.Cast(source), destination, UnassociatedScaledModifiers); + Vector4Converters.MultiplyThenAdd(destination, NativeMaximum, Vector4.Zero); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Association is performed in normalized color space before mapping all four components back to Byte4's native range. + PixelOperations.Instance.ToVector4(configuration, MemoryMarshal.Cast(source), destination, AssociatedScaledModifiers); + Vector4Converters.MultiplyThenAdd(destination, NativeMaximum, Vector4.Zero); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + PixelOperations.Instance.ToVector4(configuration, MemoryMarshal.Cast(source), destination, UnassociatedScaledModifiers); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + PixelOperations.Instance.ToVector4(configuration, MemoryMarshal.Cast(source), destination, AssociatedScaledModifiers); + } /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Vector4Converters.AddThenDivide(source, Vector4.Zero, NativeMaximum); + PixelOperations.Instance.FromVector4Destructive(configuration, source, MemoryMarshal.Cast(destination), UnassociatedScaledModifiers); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Native alpha is normalized with RGB so the Rgba32 bulk path can unassociate in a single, consistent coordinate space. + Vector4Converters.AddThenDivide(source, Vector4.Zero, NativeMaximum); + PixelOperations.Instance.FromVector4Destructive(configuration, source, MemoryMarshal.Cast(destination), AssociatedScaledModifiers); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + PixelOperations.Instance.FromVector4Destructive(configuration, source, MemoryMarshal.Cast(destination), UnassociatedScaledModifiers); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + PixelOperations.Instance.FromVector4Destructive(configuration, source, MemoryMarshal.Cast(destination), AssociatedScaledModifiers); + } } } 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 f24ed6fae8..bcfad0117c 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,330 @@ 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)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); } /// 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 37ac39a851..3ef93de9fc 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,330 @@ 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)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); } /// 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 f604d6f970..773b8c1b89 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; - namespace SixLabors.ImageSharp.PixelFormats; /// @@ -21,337 +20,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)); } /// 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 b9f7a49e11..6afcb64326 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,330 @@ 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)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + + Span destinationBytes = MemoryMarshal.Cast(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); } /// 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 c1ba4f0618..9e877cb9de 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 c38d752ea6..21c7824560 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 656a0546ba..6a7c458001 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 82be1323cd..2346f32e1b 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 a9ee9d6b78..d949d61e0d 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 1d87121e92..5c8aeea767 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; - namespace SixLabors.ImageSharp.PixelFormats; /// @@ -21,337 +20,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)); } /// 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 60cfdad4b6..69e06da219 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 da7c9a6c91..8a98521f0b 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 b6236f8a66..4679e950e5 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 f7e178d7f2..abf077d152 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -14,8 +14,8 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats.Utils; <#+ private static readonly string[] CommonPixelTypes = - { - "Argb32", + [ + "Argb32", "Abgr32", "Bgr24", "Bgra32", @@ -28,27 +28,27 @@ using SixLabors.ImageSharp.PixelFormats.Utils; "Rgb48", "Rgba64", "Bgra5551" - }; + ]; private static readonly string[] OptimizedPixelTypes = - { - "Rgba32", + [ + "Rgba32", "Argb32", "Abgr32", "Bgra32", "Rgb24", "Bgr24" - }; + ]; // Types with Rgba32-combatable to/from Vector4 conversion private static readonly string[] Rgba32CompatibleTypes = - { - "Argb32", + [ + "Argb32", "Abgr32", "Bgra32", "Rgb24", "Bgr24" - }; + ]; void GenerateGenericConverterMethods(string pixelType) { @@ -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,60 +118,109 @@ 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); } <#+ } void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType, bool hasAlpha) { - string removeTheseModifiers = "PixelConversionModifiers.Scale"; + string removeFromModifiers = "PixelConversionModifiers.Scale"; + string removeToModifiers = "PixelConversionModifiers.Scale"; + if (!hasAlpha) { - removeTheseModifiers += " | PixelConversionModifiers.Premultiply"; + // Premultiplication cannot change an alpha-less source, but premultiplied input + // must still be unassociated before its alpha channel is discarded. + removeToModifiers += " | PixelConversionModifiers.Premultiply"; } + + if (hasAlpha) + { + GenerateRgba32Compatible32BitVector4ConversionMethods(pixelType, removeFromModifiers); + return; + } +#> + + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span<<#=pixelType#>> destination, + PixelConversionModifiers modifiers) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destination, modifiers.Remove(<#=removeFromModifiers#>)); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan<<#=pixelType#>> source, + Span destination, + PixelConversionModifiers modifiers) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, source, destination, modifiers.Remove(<#=removeToModifiers#>)); + } +<#+ + } + + void GenerateRgba32Compatible32BitVector4ConversionMethods(string pixelType, string removeTheseModifiers) + { #> /// 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#>)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + destination = destination[..sourceVectors.Length]; + Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers.Remove(<#=removeTheseModifiers#>)); + + Span destinationBytes = MemoryMarshal.Cast<<#=pixelType#>, byte>(destination); + + // The SIMD saturating conversion produces RGBA byte order. Reusing the destination + // buffer and shuffling it in place avoids the row-sized Rgba32 temporary used by + // the generic Rgba-compatible path for non-Rgba32 formats. + SimdUtils.NormalizedFloatToByteSaturate( + MemoryMarshal.Cast(sourceVectors), + destinationBytes); + PixelConverter.FromRgba32.To<#=pixelType#>(destinationBytes, destinationBytes); } /// 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#>)); } <#+ } @@ -190,7 +236,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; var matching32BitTypes = OptimizedPixelTypes.Contains(pixelType) ? OptimizedPixelTypes.Where(p => p != pixelType) : - Enumerable.Empty(); + []; foreach (string destPixelType in matching32BitTypes) { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs index c8c4110c73..07428f4845 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfSingle.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct HalfSingle /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs index bdf58145fd..e167c4eb7b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector2.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct HalfVector2 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs index c3fe598045..a921f99bae 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4.PixelOperations.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +16,84 @@ public partial struct HalfVector4 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // The half-vector layouts are identical, so the shared expansion kernel can process the source without copying it. + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination[..source.Length]); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Association uses normalized opacity, not the native binary16 alpha value. + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination); + HalfTypeHelper.ToScaled(destination); + Numerics.Premultiply(destination); + HalfTypeHelper.FromScaled(destination); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination); + HalfTypeHelper.ToScaled(destination); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + this.ToUnassociatedScaledVector4(configuration, source, destination); + Numerics.Premultiply(destination[..source.Length]); + } /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // DirectX half vectors are not normalized formats, so values outside the nominal color range must reach storage unchanged. + RgbaHalfP.PixelOperations.PackUnclamped(source, MemoryMarshal.Cast(destination[..source.Length])); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // Restore normalized opacity before unassociating, then return the result to the native binary16 range. + HalfTypeHelper.ToScaled(source); + Numerics.UnPremultiply(source); + HalfTypeHelper.FromScaled(source); + RgbaHalfP.PixelOperations.PackUnclamped(source, MemoryMarshal.Cast(destination[..source.Length])); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + HalfTypeHelper.FromScaled(source); + RgbaHalfP.PixelOperations.PackUnclamped(source, MemoryMarshal.Cast(destination[..source.Length])); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Numerics.UnPremultiply(source); + HalfTypeHelper.FromScaled(source); + RgbaHalfP.PixelOperations.PackUnclamped(source, MemoryMarshal.Cast(destination[..source.Length])); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs new file mode 100644 index 0000000000..172159a49c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/HalfVector4P.PixelOperations.cs @@ -0,0 +1,364 @@ +// 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 SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct HalfVector4P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + this.ToUnassociatedScaledVector4(configuration, source, destination); + HalfTypeHelper.FromScaled(destination[..source.Length]); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination[..source.Length]); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + this.ToAssociatedScaledVector4(configuration, source, destination); + Numerics.UnPremultiply(destination[..source.Length]); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination); + HalfTypeHelper.ToScaled(destination); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + HalfTypeHelper.ToScaled(source); + Associate(source); + PackAssociatedScaled(source, destination[..source.Length]); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + HalfTypeHelper.ToScaled(source); + Reassociate(source); + PackAssociatedScaled(source, destination[..source.Length]); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Associate(source); + PackAssociatedScaled(source, destination[..source.Length]); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Reassociate(source); + PackAssociatedScaled(source, destination[..source.Length]); + } + + /// + /// Associates scaled vectors with the alpha values representable by native binary16 storage. + /// + /// The vectors to convert in place. + private static void Associate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector512.Count / Vector128.Count; + + for (; i <= source.Length - vectorsPerRegister; i += vectorsPerRegister) + { + ref Vector512 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Associate(vector); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector256.Count / Vector128.Count; + + for (; i <= source.Length - vectorsPerRegister; i += vectorsPerRegister) + { + ref Vector256 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Associate(vector); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i < source.Length; i++) + { + ref Vector128 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Associate(vector); + } + + return; + } + + for (; i < source.Length; i++) + { + Unsafe.Add(ref sourceBase, (uint)i) = HalfVector4P.Associate(Unsafe.Add(ref sourceBase, (uint)i)); + } + } + + /// + /// Reassociates scaled vectors with the alpha values representable by native binary16 storage. + /// + /// The vectors to convert in place. + private static void Reassociate(Span source) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector512.Count / Vector128.Count; + + for (; i <= source.Length - vectorsPerRegister; i += vectorsPerRegister) + { + ref Vector512 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Reassociate(vector); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector256.Count / Vector128.Count; + + for (; i <= source.Length - vectorsPerRegister; i += vectorsPerRegister) + { + ref Vector256 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Reassociate(vector); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i < source.Length; i++) + { + ref Vector128 vector = ref Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + vector = Reassociate(vector); + } + + return; + } + + for (; i < source.Length; i++) + { + Unsafe.Add(ref sourceBase, (uint)i) = HalfVector4P.Reassociate(Unsafe.Add(ref sourceBase, (uint)i)); + } + } + + /// + /// Converts an unassociated scaled vector to associated scaled components. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Associate(Vector128 source) + { + source = ClampUnit(source); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 storedAlpha = QuantizeScaledAlpha(alpha); + Vector128 result = source * storedAlpha; + return Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); + } + + /// + /// Converts unassociated scaled vectors to associated scaled components. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Associate(Vector256 source) + { + source = ClampUnit(source); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 storedAlpha = QuantizeScaledAlpha(alpha); + Vector256 result = source * storedAlpha; + return Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); + } + + /// + /// Converts unassociated scaled vectors to associated scaled components. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Associate(Vector512 source) + { + source = ClampUnit(source); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 storedAlpha = QuantizeScaledAlpha(alpha); + Vector512 result = source * storedAlpha; + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Reassociates an associated scaled vector after alpha quantization. + /// + /// The associated vectors. + /// The reassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Reassociate(Vector128 source) + { + Vector128 zero = Vector128.Zero; + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 storedAlpha = QuantizeScaledAlpha(alpha); + Vector128 result = source * (storedAlpha / alpha); + result = Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector128.ConditionalSelect(Vector128.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates associated scaled vectors after alpha quantization. + /// + /// The associated vectors. + /// The reassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Reassociate(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 storedAlpha = QuantizeScaledAlpha(alpha); + Vector256 result = source * (storedAlpha / alpha); + result = Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector256.ConditionalSelect(Vector256.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates associated scaled vectors after alpha quantization. + /// + /// The associated vectors. + /// The reassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Reassociate(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 storedAlpha = QuantizeScaledAlpha(alpha); + Vector512 result = source * (storedAlpha / alpha); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector512.ConditionalSelect(Vector512.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Quantizes scaled alpha through the native binary16 representation. + /// + /// The scaled alpha lanes. + /// The scaled alpha values represented by binary16 storage. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 QuantizeScaledAlpha(Vector128 alpha) + { + Vector128 native = HalfTypeHelper.FromScaled(alpha); + return HalfTypeHelper.ToScaled(HalfTypeHelper.RoundToHalf(native)); + } + + /// + /// Quantizes scaled alpha through the native binary16 representation. + /// + /// The scaled alpha lanes. + /// The scaled alpha values represented by binary16 storage. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 QuantizeScaledAlpha(Vector256 alpha) + { + Vector256 native = HalfTypeHelper.FromScaled(alpha); + return HalfTypeHelper.ToScaled(HalfTypeHelper.RoundToHalf(native)); + } + + /// + /// Quantizes scaled alpha through the native binary16 representation. + /// + /// The scaled alpha lanes. + /// The scaled alpha values represented by binary16 storage. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 QuantizeScaledAlpha(Vector512 alpha) + { + Vector512 native = HalfTypeHelper.FromScaled(alpha); + return HalfTypeHelper.ToScaled(HalfTypeHelper.RoundToHalf(native)); + } + + /// + /// Clamps vectors to the scaled color range, mapping NaN lanes to zero. + /// + /// The vectors to clamp. + /// The clamped vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ClampUnit(Vector128 source) => Numerics.Clamp(source, Vector128.Zero, Vector128.One); + + /// + /// Clamps vectors to the scaled color range, mapping NaN lanes to zero. + /// + /// The vectors to clamp. + /// The clamped vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ClampUnit(Vector256 source) => Numerics.Clamp(source, Vector256.Zero, Vector256.One); + + /// + /// Clamps vectors to the scaled color range, mapping NaN lanes to zero. + /// + /// The vectors to clamp. + /// The clamped vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ClampUnit(Vector512 source) => Numerics.Clamp(source, Vector512.Zero, Vector512.One); + + /// + /// Maps associated scaled vectors to native components and packs them as binary16 values. + /// + /// The associated scaled vectors. + /// The destination pixels. + private static void PackAssociatedScaled(Span source, Span destination) + { + HalfTypeHelper.FromScaled(source); + RgbaHalfP.PixelOperations.PackUnclamped(source, MemoryMarshal.Cast(destination)); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs index 7495cee53d..b79d2a5b21 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L16.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct L16 /// internal partial class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs index 5dd98c3a66..fb7945fdf8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/L8.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct L8 /// internal partial class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/La16.PixelOperations.cs index d9bda3e0fc..316c965650 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; /// @@ -13,12 +11,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 1fb5adfc8f..34a8655338 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; /// @@ -13,12 +11,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 7176295869..5568fc742d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte2.PixelOperations.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +16,23 @@ public partial struct NormalizedByte2 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + private static readonly Vector4 NativeOffset = new(1F, 1F, 0F, 0F); + private static readonly Vector4 NativeDivisor = new(2F, 2F, 1F, 1F); + + // Alpha is implicitly one, so both outward representations already contain associated color components. + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Only X and Y use affine native coordinates. Preserve W as normalized alpha for the scaled unassociation step. + Vector4Converters.AddThenDivide(source, NativeOffset, NativeDivisor); + this.FromAssociatedScaledVector4Destructive(configuration, source, destination); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs index 9bb48f5924..d467b802ea 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4.PixelOperations.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +17,87 @@ public partial struct NormalizedByte4 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + private static readonly Vector4 NativeScale = new(2F); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + NormalizedByte4P.PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], false); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // The packed P and non-P layouts are identical, so the shared SIMD unpacker supplies normalized values. Association + // happens in [0, 1] before the result is mapped back to the signed-native [-1, 1] coordinate system. + NormalizedByte4P.PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, true); + Numerics.Premultiply(destination); + Vector4Converters.MultiplyThenAdd(destination, NativeScale, -Vector4.One); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + NormalizedByte4P.PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], true); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + NormalizedByte4P.PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, true); + Numerics.Premultiply(destination); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + NormalizedByte4P.PixelOperations.Pack(source, MemoryMarshal.Cast(destination), false); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Convert all native components together so alpha and RGB enter unassociation in the same normalized coordinate system. + Vector4Converters.AddThenDivide(source, Vector4.One, NativeScale); + Numerics.UnPremultiply(source); + NormalizedByte4P.PixelOperations.Pack(source, MemoryMarshal.Cast(destination), true); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + NormalizedByte4P.PixelOperations.Pack(source, MemoryMarshal.Cast(destination), true); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Numerics.UnPremultiply(source); + NormalizedByte4P.PixelOperations.Pack(source, MemoryMarshal.Cast(destination), true); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs new file mode 100644 index 0000000000..de2f8f17b7 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedByte4P.PixelOperations.cs @@ -0,0 +1,814 @@ +// 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; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct NormalizedByte4P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + private const byte RestorePackedPixelOrder = 0b_11_01_10_00; + + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ToUnassociatedVector4(source, destination[..source.Length], false); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ToVector4(source, destination[..source.Length], false); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Associate(source, false); + Pack(source, destination, true); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Reassociate(source, false); + Pack(source, destination, true); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ToUnassociatedVector4(source, destination[..source.Length], true); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ToVector4(source, destination[..source.Length], true); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Associate(source, true); + Pack(source, destination, true); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Reassociate(source, true); + Pack(source, destination, true); + } + + /// + /// Converts packed signed-normalized pixels to native or scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + /// Whether to produce scaled vectors. + internal static void ToVector4(ReadOnlySpan source, Span destination, bool scaled) + { + ref byte sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + // Each packed component occupies one byte, so the destination Vector4 lane count also defines the source byte stride. + int componentsPerPixel = Vector128.Count; + int i = 0; + + // Portable widening advances one integer width at a time, so assemble the wider vectors from ordered 128-bit halves. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i * componentsPerPixel)).AsSByte(); + Vector128 lowerShorts = Vector128.WidenLower(packed); + Vector128 upperShorts = Vector128.WidenUpper(packed); + Vector256 lowerIntegers = Vector256.Create(Vector128.WidenLower(lowerShorts), Vector128.WidenUpper(lowerShorts)); + Vector256 upperIntegers = Vector256.Create(Vector128.WidenLower(upperShorts), Vector128.WidenUpper(upperShorts)); + Vector512 integers = Vector512.Create(lowerIntegers, upperIntegers); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToVector4(Vector512.ConvertToSingle(integers), scaled); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + ulong packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBase, (uint)(i * componentsPerPixel))); + Vector128 shorts = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsSByte()); + Vector256 integers = Vector256.Create(Vector128.WidenLower(shorts), Vector128.WidenUpper(shorts)); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToVector4(Vector256.ConvertToSingle(integers), scaled); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i < source.Length; i++) + { + uint packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBase, (uint)(i * componentsPerPixel))); + Vector128 shorts = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsSByte()); + Vector128 integers = Vector128.WidenLower(shorts); + Unsafe.Add(ref destinationBase, (uint)i) = ToVector4(Vector128.ConvertToSingle(integers), scaled).AsVector4(); + } + + return; + } + + ref NormalizedByte4P pixelBase = ref Unsafe.As(ref sourceBase); + + for (; i < source.Length; i++) + { + NormalizedByte4P pixel = Unsafe.Add(ref pixelBase, (uint)i); + Unsafe.Add(ref destinationBase, (uint)i) = scaled ? pixel.ToScaledVector4() : pixel.ToVector4(); + } + } + + /// + /// Converts signed-normalized storage to native or scaled vectors. + /// + /// The signed storage components. + /// Whether to produce scaled vectors. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ToVector4(Vector512 source, bool scaled) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector512.Max(source, Vector512.Create(-MaxPos)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + return (source + Vector512.Create(MaxPos)) / Vector512.Create(ScaledMagnitude); + } + + return source / Vector512.Create(MaxPos); + } + + /// + /// Converts signed-normalized storage to native or scaled vectors. + /// + /// The signed storage components. + /// Whether to produce scaled vectors. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ToVector4(Vector256 source, bool scaled) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector256.Max(source, Vector256.Create(-MaxPos)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + return (source + Vector256.Create(MaxPos)) / Vector256.Create(ScaledMagnitude); + } + + return source / Vector256.Create(MaxPos); + } + + /// + /// Converts signed-normalized storage to native or scaled vectors. + /// + /// The signed storage components. + /// Whether to produce scaled vectors. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ToVector4(Vector128 source, bool scaled) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector128.Max(source, Vector128.Create(-MaxPos)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + return (source + Vector128.Create(MaxPos)) / Vector128.Create(ScaledMagnitude); + } + + return source / Vector128.Create(MaxPos); + } + + /// + /// Converts packed associated pixels directly to unassociated native or scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + /// Whether to produce scaled vectors. + private static void ToUnassociatedVector4(ReadOnlySpan source, Span destination, bool scaled) + { + ref byte sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + // Each packed component occupies one byte, so the destination Vector4 lane count also defines the source byte stride. + int componentsPerPixel = Vector128.Count; + int i = 0; + + // Portable widening advances one integer width at a time, so assemble the wider vectors from ordered 128-bit halves. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i * componentsPerPixel)).AsSByte(); + Vector128 lowerShorts = Vector128.WidenLower(packed); + Vector128 upperShorts = Vector128.WidenUpper(packed); + Vector256 lowerIntegers = Vector256.Create(Vector128.WidenLower(lowerShorts), Vector128.WidenUpper(lowerShorts)); + Vector256 upperIntegers = Vector256.Create(Vector128.WidenLower(upperShorts), Vector128.WidenUpper(upperShorts)); + Vector512 integers = Vector512.Create(lowerIntegers, upperIntegers); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToUnassociatedVector4(Vector512.ConvertToSingle(integers), scaled); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + ulong packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBase, (uint)(i * componentsPerPixel))); + Vector128 shorts = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsSByte()); + Vector256 integers = Vector256.Create(Vector128.WidenLower(shorts), Vector128.WidenUpper(shorts)); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToUnassociatedVector4(Vector256.ConvertToSingle(integers), scaled); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i < source.Length; i++) + { + uint packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBase, (uint)(i * componentsPerPixel))); + Vector128 shorts = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsSByte()); + Vector128 integers = Vector128.WidenLower(shorts); + Unsafe.Add(ref destinationBase, (uint)i) = ToUnassociatedVector4(Vector128.ConvertToSingle(integers), scaled).AsVector4(); + } + + return; + } + + ref NormalizedByte4P pixelBase = ref Unsafe.As(ref sourceBase); + + for (; i < source.Length; i++) + { + NormalizedByte4P pixel = Unsafe.Add(ref pixelBase, (uint)i); + Unsafe.Add(ref destinationBase, (uint)i) = scaled ? pixel.ToUnassociatedScaledVector4() : pixel.ToUnassociatedVector4(); + } + } + + /// + /// Converts four associated signed-byte vectors to unassociated native or scaled vectors. + /// + /// The signed storage components. + /// Whether to produce scaled vectors. + /// The unassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ToUnassociatedVector4(Vector512 source, bool scaled) + { + // Clamp the duplicate SNORM minimum encoding before converting it to the nonnegative associated domain. + source = Vector512.Max(source, Vector512.Create(-MaxPos)); + source += Vector512.Create(MaxPos); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + Vector512 normalizeMask = Vector512.Equals(alpha, Vector512.Zero) | alphaMask; + Vector512 divisor = Vector512.ConditionalSelect(normalizeMask, Vector512.Create(ScaledMagnitude), alpha); + Vector512 result = source / divisor; + + // Unassociation is performed in scaled space; map the result back only after alpha has served as a true opacity value. + return scaled ? result : (result * Vector512.Create(2F)) - Vector512.One; + } + + /// + /// Converts two associated signed-byte vectors to unassociated native or scaled vectors. + /// + /// The signed storage components. + /// Whether to produce scaled vectors. + /// The unassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ToUnassociatedVector4(Vector256 source, bool scaled) + { + // Clamp the duplicate SNORM minimum encoding before converting it to the nonnegative associated domain. + source = Vector256.Max(source, Vector256.Create(-MaxPos)); + source += Vector256.Create(MaxPos); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + Vector256 normalizeMask = Vector256.Equals(alpha, Vector256.Zero) | alphaMask; + Vector256 divisor = Vector256.ConditionalSelect(normalizeMask, Vector256.Create(ScaledMagnitude), alpha); + Vector256 result = source / divisor; + + // Unassociation is performed in scaled space; map the result back only after alpha has served as a true opacity value. + return scaled ? result : (result * Vector256.Create(2F)) - Vector256.One; + } + + /// + /// Converts an associated signed-byte vector to an unassociated native or scaled vector. + /// + /// The signed storage components. + /// Whether to produce a scaled vector. + /// The unassociated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ToUnassociatedVector4(Vector128 source, bool scaled) + { + // Clamp the duplicate SNORM minimum encoding before converting it to the nonnegative associated domain. + source = Vector128.Max(source, Vector128.Create(-MaxPos)); + source += Vector128.Create(MaxPos); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + Vector128 normalizeMask = Vector128.Equals(alpha, Vector128.Zero) | alphaMask; + Vector128 divisor = Vector128.ConditionalSelect(normalizeMask, Vector128.Create(ScaledMagnitude), alpha); + Vector128 result = source / divisor; + + // Unassociation is performed in scaled space; map the result back only after alpha has served as a true opacity value. + return scaled ? result : (result * Vector128.Create(2F)) - Vector128.One; + } + + /// + /// Converts unassociated native or scaled vectors to the destination's associated scaled representation. + /// + /// The vectors to convert in place. + /// Whether the source contains scaled vectors. + private static void Associate(Span source, bool scaled) + { + // Quantize alpha through signed-normalized destination storage before associating RGB. The kernels replace W separately + // so alpha is stored unchanged instead of being multiplied by itself. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Associate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Associate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector128.IsHardwareAccelerated) + { + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Associate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + return; + } + + ref Vector4 tailBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Vector4 vector = Unsafe.Add(ref tailBase, i); + + if (!scaled) + { + // Signed-native W is an affine encoding rather than opacity, so association must happen after mapping to scaled space. + vector += Vector4.One; + vector /= 2F; + } + + // Qualify the containing pixel type because this nested class also declares SIMD helpers named Associate. + Unsafe.Add(ref tailBase, i) = NormalizedByte4P.Associate(vector); + } + } + + /// + /// Converts four unassociated native or scaled vectors to the destination's associated scaled representation. + /// + /// The unassociated vectors. + /// Whether the source contains scaled vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Associate(Vector512 source, bool scaled) + { + Vector512 zero = Vector512.Zero; + Vector512 one = Vector512.One; + + if (!scaled) + { + // Signed-native W is an affine encoding rather than opacity, so association must happen after mapping to scaled space. + source = (source + one) / Vector512.Create(2F); + } + + source = Vector512.Min(Vector512.Max(source, zero), one); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 nativeAlpha = Vector512.Min(Vector512.Max((alpha * Vector512.Create(2F)) - one, -one), one); + Vector512 storedAlpha = Vector512_.RoundToNearestInteger(nativeAlpha * Vector512.Create(MaxPos)); + storedAlpha += Vector512.Create(MaxPos); + storedAlpha /= Vector512.Create(ScaledMagnitude); + Vector512 result = source * storedAlpha; + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Converts two unassociated native or scaled vectors to the destination's associated scaled representation. + /// + /// The unassociated vectors. + /// Whether the source contains scaled vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Associate(Vector256 source, bool scaled) + { + Vector256 zero = Vector256.Zero; + Vector256 one = Vector256.One; + + if (!scaled) + { + // Signed-native W is an affine encoding rather than opacity, so association must happen after mapping to scaled space. + source = (source + one) / Vector256.Create(2F); + } + + source = Vector256.Min(Vector256.Max(source, zero), one); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 nativeAlpha = Vector256.Min(Vector256.Max((alpha * Vector256.Create(2F)) - one, -one), one); + Vector256 storedAlpha = Vector256_.RoundToNearestInteger(nativeAlpha * Vector256.Create(MaxPos)); + storedAlpha += Vector256.Create(MaxPos); + storedAlpha /= Vector256.Create(ScaledMagnitude); + Vector256 result = source * storedAlpha; + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector256.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Converts an unassociated native or scaled vector to the destination's associated scaled representation. + /// + /// The unassociated vector. + /// Whether the source contains a scaled vector. + /// The associated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Associate(Vector128 source, bool scaled) + { + Vector128 zero = Vector128.Zero; + Vector128 one = Vector128.One; + + if (!scaled) + { + // Signed-native W is an affine encoding rather than opacity, so association must happen after mapping to scaled space. + source = (source + one) / Vector128.Create(2F); + } + + source = Vector128.Min(Vector128.Max(source, zero), one); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 nativeAlpha = Vector128.Min(Vector128.Max((alpha * Vector128.Create(2F)) - one, -one), one); + Vector128 storedAlpha = Vector128_.RoundToNearestInteger(nativeAlpha * Vector128.Create(MaxPos)); + storedAlpha += Vector128.Create(MaxPos); + storedAlpha /= Vector128.Create(ScaledMagnitude); + Vector128 result = source * storedAlpha; + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + return Vector128.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Reassociates native or scaled vectors with the scaled alpha values the destination stores. + /// + /// The vectors to convert in place. + /// Whether the source contains scaled vectors. + private static void Reassociate(Span source, bool scaled) + { + // Preserve straight color across destination-alpha quantization by scaling RGB by storedAlpha / inputAlpha. The kernels + // replace W, clamp RGB to stored alpha, and clear zero-alpha vectors so the stored result remains valid associated color. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Reassociate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Reassociate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector128.IsHardwareAccelerated) + { + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = Reassociate(sourceBase, scaled); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + return; + } + + ref Vector4 tailBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Vector4 vector = Unsafe.Add(ref tailBase, i); + + if (!scaled) + { + // Convert native components together so RGB and alpha enter reassociation in the same scaled coordinate system. + vector += Vector4.One; + vector /= 2F; + } + + // Qualify the containing pixel type because this nested class also declares SIMD helpers named Reassociate. + Unsafe.Add(ref tailBase, i) = NormalizedByte4P.Reassociate(vector); + } + } + + /// + /// Reassociates four native or scaled vectors with the scaled alpha values the destination stores. + /// + /// The associated vectors. + /// Whether the source contains scaled vectors. + /// The reassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Reassociate(Vector512 source, bool scaled) + { + Vector512 zero = Vector512.Zero; + Vector512 one = Vector512.One; + + if (!scaled) + { + // Convert native components together so RGB and alpha enter reassociation in the same scaled coordinate system. + source = (source + one) / Vector512.Create(2F); + } + + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 nativeAlpha = Vector512.Min(Vector512.Max((alpha * Vector512.Create(2F)) - one, -one), one); + Vector512 storedAlpha = Vector512_.RoundToNearestInteger(nativeAlpha * Vector512.Create(MaxPos)); + storedAlpha += Vector512.Create(MaxPos); + storedAlpha /= Vector512.Create(ScaledMagnitude); + Vector512 result = source * (storedAlpha / alpha); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector512.Min(Vector512.Max(result, zero), storedAlpha); + return Vector512.ConditionalSelect(Vector512.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates two native or scaled vectors with the scaled alpha values the destination stores. + /// + /// The associated vectors. + /// Whether the source contains scaled vectors. + /// The reassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Reassociate(Vector256 source, bool scaled) + { + Vector256 zero = Vector256.Zero; + Vector256 one = Vector256.One; + + if (!scaled) + { + // Convert native components together so RGB and alpha enter reassociation in the same scaled coordinate system. + source = (source + one) / Vector256.Create(2F); + } + + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 nativeAlpha = Vector256.Min(Vector256.Max((alpha * Vector256.Create(2F)) - one, -one), one); + Vector256 storedAlpha = Vector256_.RoundToNearestInteger(nativeAlpha * Vector256.Create(MaxPos)); + storedAlpha += Vector256.Create(MaxPos); + storedAlpha /= Vector256.Create(ScaledMagnitude); + Vector256 result = source * (storedAlpha / alpha); + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector256.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector256.Min(Vector256.Max(result, zero), storedAlpha); + return Vector256.ConditionalSelect(Vector256.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates a native or scaled vector with the scaled alpha value the destination stores. + /// + /// The associated vector. + /// Whether the source contains a scaled vector. + /// The reassociated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Reassociate(Vector128 source, bool scaled) + { + Vector128 zero = Vector128.Zero; + Vector128 one = Vector128.One; + + if (!scaled) + { + // Convert native components together so RGB and alpha enter reassociation in the same scaled coordinate system. + source = (source + one) / Vector128.Create(2F); + } + + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 nativeAlpha = Vector128.Min(Vector128.Max((alpha * Vector128.Create(2F)) - one, -one), one); + Vector128 storedAlpha = Vector128_.RoundToNearestInteger(nativeAlpha * Vector128.Create(MaxPos)); + storedAlpha += Vector128.Create(MaxPos); + storedAlpha /= Vector128.Create(ScaledMagnitude); + Vector128 result = source * (storedAlpha / alpha); + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + result = Vector128.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector128.Min(Vector128.Max(result, zero), storedAlpha); + return Vector128.ConditionalSelect(Vector128.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Packs native or scaled vectors into signed-normalized storage. + /// + /// The source vectors. + /// The destination pixels. + /// Whether the source contains scaled vectors. + internal static void Pack(Span source, Span destination, bool scaled) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + ref NormalizedByte4P destinationBase = ref MemoryMarshal.GetReference(destination); + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector512 vector = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + Vector512 integers = ConvertToPackedInt32(vector, scaled); + Vector256 shorts = Vector256_.PackSignedSaturate(integers.GetLower(), integers.GetUpper()); + Vector128 packed = Vector128_.PackSignedSaturate(shorts.GetLower(), shorts.GetUpper()); + + if (Avx2.IsSupported) + { + // AVX2 narrows each 128-bit lane independently, placing the middle two pixels out of order. + packed = Vector128_.ShuffleNative(packed.AsSingle(), RestorePackedPixelOrder).AsSByte(); + } + + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = packed; + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + + for (; i <= source.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector256 vector = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + Vector256 integers = ConvertToPackedInt32(vector, scaled); + Vector128 shorts = Vector128_.PackSignedSaturate(integers.GetLower(), integers.GetUpper()); + Vector128 packed = Vector128_.PackSignedSaturate(shorts, shorts); + Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)) = packed.AsUInt64().GetElement(0); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i < source.Length; i++) + { + Vector128 vector = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)i)); + Vector128 integers = ConvertToPackedInt32(vector, scaled); + Vector128 shorts = Vector128_.PackSignedSaturate(integers, integers); + Vector128 packed = Vector128_.PackSignedSaturate(shorts, shorts); + Unsafe.Add(ref destinationBase, (uint)i).PackedValue = packed.AsUInt32().GetElement(0); + } + + return; + } + + for (; i < source.Length; i++) + { + Vector4 vector = Unsafe.Add(ref sourceBase, (uint)i); + Unsafe.Add(ref destinationBase, (uint)i) = scaled ? FromScaledVector4(vector) : FromVector4(vector); + } + } + + /// + /// Converts four vectors to signed integers using the destination's packing contract. + /// + /// The source vectors. + /// Whether the source contains scaled vectors. + /// The packed integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ConvertToPackedInt32(Vector512 source, bool scaled) + { + Vector512 one = Vector512.One; + + if (scaled) + { + source *= Vector512.Create(2F); + source -= one; + } + + source = Vector512.Min(Vector512.Max(source, -one), one) * Vector512.Create(MaxPos); + return Vector512_.ConvertToInt32RoundToEven(source); + } + + /// + /// Converts two vectors to signed integers using the destination's packing contract. + /// + /// The source vectors. + /// Whether the source contains scaled vectors. + /// The packed integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertToPackedInt32(Vector256 source, bool scaled) + { + Vector256 one = Vector256.One; + + if (scaled) + { + source *= Vector256.Create(2F); + source -= one; + } + + source = Vector256.Min(Vector256.Max(source, -one), one) * Vector256.Create(MaxPos); + return Vector256_.ConvertToInt32RoundToEven(source); + } + + /// + /// Converts a vector to signed integers using the destination's packing contract. + /// + /// The source vector. + /// Whether the source contains a scaled vector. + /// The packed integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ConvertToPackedInt32(Vector128 source, bool scaled) + { + Vector128 one = Vector128.One; + + if (scaled) + { + source *= Vector128.Create(2F); + source -= one; + } + + source = Vector128.Min(Vector128.Max(source, -one), one) * Vector128.Create(MaxPos); + return Vector128_.ConvertToInt32RoundToEven(source); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs index 3913f64bb5..f35f2a2dad 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort2.PixelOperations.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +16,23 @@ public partial struct NormalizedShort2 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + private static readonly Vector4 NativeOffset = new(1F, 1F, 0F, 0F); + private static readonly Vector4 NativeDivisor = new(2F, 2F, 1F, 1F); + + // Alpha is implicitly one, so both outward representations already contain associated color components. + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Only X and Y use affine native coordinates. Preserve W as normalized alpha for the scaled unassociation step. + Vector4Converters.AddThenDivide(source, NativeOffset, NativeDivisor); + this.FromAssociatedScaledVector4Destructive(configuration, source, destination); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs index 6334f4e7e4..8a60d68b4b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/NormalizedShort4.PixelOperations.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +17,87 @@ public partial struct NormalizedShort4 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + private static readonly Vector4 NativeScale = new(2F); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], true, false); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Association is defined in scaled color space, not around the signed-native zero point. The shared signed-short SIMD + // kernel handles unpacking before the result is mapped back to the format's native [-1, 1] range. + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, true, true); + Numerics.Premultiply(destination); + Vector4Converters.MultiplyThenAdd(destination, NativeScale, -Vector4.One); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], true, true); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, true, true); + Numerics.Premultiply(destination); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), true, false); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Convert native RGB and alpha together before unassociation so the subsequent scaled pack retains the scalar contract. + Vector4Converters.AddThenDivide(source, Vector4.One, NativeScale); + Numerics.UnPremultiply(source); + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), true, true); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), true, true); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Numerics.UnPremultiply(source); + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), true, true); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs index a5b803f768..5be973404e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rg32.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct Rg32 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs index 5473a602f9..435a8c0779 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; /// @@ -15,12 +13,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( ReadOnlySpan redChannel, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs index 56a052a7dd..31816ceb0f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb48.PixelOperations.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +15,12 @@ public partial struct Rgb48 /// internal partial class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + // Alpha is implicitly one, so both outward representations already contain associated color components. /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb96.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb96.PixelOperations.cs new file mode 100644 index 0000000000..990fd64bd1 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb96.PixelOperations.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// The alpha component is always fully opaque. +/// +public partial struct Rgb96 +{ + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + // Alpha is implicitly one, so both outward representations already contain associated color components. + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba1010102.PixelOperations.cs index f550396275..8dcbb319f9 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; /// @@ -13,12 +11,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 a4887b393c..065e34c336 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; @@ -18,24 +17,18 @@ 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, - 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); } @@ -44,16 +37,16 @@ public override void ToVector4( 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/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs new file mode 100644 index 0000000000..19338f12f8 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32P.PixelOperations.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct Rgba32P +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // Native and scaled vectors have the same range for this format, so the byte-specialized converter is valid for both contracts. + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored. + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + // This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts. + Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba64.PixelOperations.cs index 56bbc6b25b..6b362d44ec 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; /// @@ -13,12 +11,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/RgbaHalf.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaHalf.PixelOperations.cs new file mode 100644 index 0000000000..f3c01e082a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaHalf.PixelOperations.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct RgbaHalf +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : PixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // RgbaHalf and RgbaHalfP have the same four-half layout. Sharing the binary16 expansion kernel keeps this path + // vectorized without changing the unassociated meaning of the source components. + RgbaHalfP.PixelOperations.Unpack(MemoryMarshal.Cast(source), destination[..source.Length]); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // Association is fused with half expansion so the conversion reads and writes each span once. + RgbaHalfP.PixelOperations.UnpackAssociated(MemoryMarshal.Cast(source), destination[..source.Length]); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ToAssociatedVector4(configuration, source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // The layouts are identical, so the shared packer can write RgbaHalf storage without an intermediate buffer. + RgbaHalfP.PixelOperations.Pack(source, MemoryMarshal.Cast(destination[..source.Length])); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // Unassociation is fused with binary16 packing so processors do not pay for another pass over their vector buffer. + RgbaHalfP.PixelOperations.PackFromAssociated(source, MemoryMarshal.Cast(destination[..source.Length])); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + => this.FromUnassociatedVector4Destructive(configuration, source, destination); + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + => this.FromAssociatedVector4Destructive(configuration, source, destination); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaHalfP.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaHalfP.PixelOperations.cs new file mode 100644 index 0000000000..a9f3081f1a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaHalfP.PixelOperations.cs @@ -0,0 +1,759 @@ +// 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 SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Provides optimized overrides for bulk operations. +/// +public partial struct RgbaHalfP +{ + /// + /// Provides optimized bulk operations for . + /// + internal class PixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + UnpackUnassociated(source, destination[..source.Length]); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Unpack(source, destination[..source.Length]); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => this.ToAssociatedVector4(configuration, source, destination); + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + PackUnassociated(source, destination[..source.Length]); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + PackAssociated(source, destination[..source.Length]); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + => this.FromUnassociatedVector4Destructive(configuration, source, destination); + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + => this.FromAssociatedVector4Destructive(configuration, source, destination); + + /// + /// Expands binary16 components without changing their unit-range representation. + /// + /// The packed source pixels. + /// The destination vectors. + internal static void Unpack(ReadOnlySpan source, Span destination) + { + ref ushort sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref float destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector512 lower, Vector512 upper) = HalfTypeHelper.Unpack(packed); + Vector512.StoreUnsafe(lower, ref destinationBase, (nuint)i); + Vector512.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector512.Count)); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector256 lower, Vector256 upper) = HalfTypeHelper.Unpack(packed); + Vector256.StoreUnsafe(lower, ref destinationBase, (nuint)i); + Vector256.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector256.Count)); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector128 lower, Vector128 upper) = HalfTypeHelper.Unpack(packed); + Vector128.StoreUnsafe(lower, ref destinationBase, (nuint)i); + Vector128.StoreUnsafe(upper, ref destinationBase, (nuint)(i + Vector128.Count)); + } + + if (i < componentCount) + { + // Four binary16 components form one pixel, so the only possible remainder is one complete pixel. + ulong remainder = Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref sourceBase, (uint)i))); + Vector128 packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); + Vector128.StoreUnsafe(HalfTypeHelper.Unpack(packed).Lower, ref destinationBase, (nuint)i); + } + + return; + } + + ref RgbaHalfP pixelBase = ref Unsafe.As(ref sourceBase); + ref Vector4 vectorBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Unsafe.Add(ref vectorBase, (uint)pixelIndex) = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToVector4(); + } + } + + /// + /// Expands unassociated binary16 components and associates RGB in the same pass. + /// + /// The packed source pixels. + /// The destination vectors. + internal static void UnpackAssociated(ReadOnlySpan source, Span destination) + { + ref ushort sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref float destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector512 lower, Vector512 upper) = HalfTypeHelper.Unpack(packed); + Vector512.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); + Vector512.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector512.Count)); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector256 lower, Vector256 upper) = HalfTypeHelper.Unpack(packed); + Vector256.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); + Vector256.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector256.Count)); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector128 lower, Vector128 upper) = HalfTypeHelper.Unpack(packed); + Vector128.StoreUnsafe(Associate(lower), ref destinationBase, (nuint)i); + Vector128.StoreUnsafe(Associate(upper), ref destinationBase, (nuint)(i + Vector128.Count)); + } + + if (i < componentCount) + { + // Four binary16 components form one pixel, so the only possible remainder is one complete pixel. + ulong remainder = Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref sourceBase, (uint)i))); + Vector128 packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); + Vector128.StoreUnsafe(Associate(HalfTypeHelper.Unpack(packed).Lower), ref destinationBase, (nuint)i); + } + + return; + } + + ref RgbaHalfP pixelBase = ref Unsafe.As(ref sourceBase); + ref Vector4 vectorBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Vector4 vector = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToVector4(); + Numerics.Premultiply(ref vector); + Unsafe.Add(ref vectorBase, (uint)pixelIndex) = vector; + } + } + + /// + /// Expands associated binary16 components and unassociates RGB in the same pass. + /// + /// The packed source pixels. + /// The destination vectors. + internal static void UnpackUnassociated(ReadOnlySpan source, Span destination) + { + ref ushort sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref float destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 packed = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector512 lower, Vector512 upper) = HalfTypeHelper.Unpack(packed); + Vector512.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); + Vector512.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector512.Count)); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector256 lower, Vector256 upper) = HalfTypeHelper.Unpack(packed); + Vector256.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); + Vector256.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector256.Count)); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); + (Vector128 lower, Vector128 upper) = HalfTypeHelper.Unpack(packed); + Vector128.StoreUnsafe(Unassociate(lower), ref destinationBase, (nuint)i); + Vector128.StoreUnsafe(Unassociate(upper), ref destinationBase, (nuint)(i + Vector128.Count)); + } + + if (i < componentCount) + { + // Four binary16 components form one pixel, so the only possible remainder is one complete pixel. + ulong remainder = Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref sourceBase, (uint)i))); + Vector128 packed = Vector128.CreateScalarUnsafe(remainder).AsUInt16(); + Vector128.StoreUnsafe(Unassociate(HalfTypeHelper.Unpack(packed).Lower), ref destinationBase, (nuint)i); + } + + return; + } + + ref RgbaHalfP pixelBase = ref Unsafe.As(ref sourceBase); + ref Vector4 vectorBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Unsafe.Add(ref vectorBase, (uint)pixelIndex) = Unsafe.Add(ref pixelBase, (uint)pixelIndex).ToUnassociatedVector4(); + } + } + + /// + /// Packs unassociated unit-range vectors directly into binary16 storage. + /// + /// The source vectors. + /// The destination pixels. + internal static void Pack(Span source, Span destination) + { + ref float sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ushort destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 lower = Numerics.Clamp(Vector512.LoadUnsafe(ref sourceBase, (nuint)i), Vector512.Zero, Vector512.One); + Vector512 upper = Numerics.Clamp(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512.Count)), Vector512.Zero, Vector512.One); + Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 lower = Numerics.Clamp(Vector256.LoadUnsafe(ref sourceBase, (nuint)i), Vector256.Zero, Vector256.One); + Vector256 upper = Numerics.Clamp(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256.Count)), Vector256.Zero, Vector256.One); + Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 lower = Numerics.Clamp(Vector128.LoadUnsafe(ref sourceBase, (nuint)i), Vector128.Zero, Vector128.One); + Vector128 upper = Numerics.Clamp(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128.Count)), Vector128.Zero, Vector128.One); + Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + + if (i < componentCount) + { + // Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel. + Vector128 vector = Numerics.Clamp(Vector128.LoadUnsafe(ref sourceBase, (nuint)i), Vector128.Zero, Vector128.One); + Vector128 packed = HalfTypeHelper.Pack(vector, vector); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); + } + + return; + } + + ref Vector4 vectorBase = ref Unsafe.As(ref sourceBase); + ref RgbaHalfP pixelBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Vector4 vector = Numerics.Clamp(Unsafe.Add(ref vectorBase, (uint)pixelIndex), Vector4.Zero, Vector4.One); + Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); + } + } + + /// + /// Packs vectors directly into IEEE 754 binary16 storage without applying unit-range color constraints. + /// + /// The source vectors. + /// The destination pixels. + internal static void PackUnclamped(Span source, Span destination) + { + ref float sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ushort destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 lower = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); + Vector512 upper = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512.Count)); + Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 lower = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); + Vector256 upper = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256.Count)); + Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 lower = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); + Vector128 upper = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128.Count)); + Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + + if (i < componentCount) + { + // Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel. + Vector128 vector = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); + Vector128 packed = HalfTypeHelper.Pack(vector, vector); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); + } + + return; + } + + ref Vector4 vectorBase = ref Unsafe.As(ref sourceBase); + ref RgbaHalfP pixelBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Vector4 vector = Unsafe.Add(ref vectorBase, (uint)pixelIndex); + Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); + } + } + + /// + /// Unassociates vectors and packs unassociated unit-range binary16 storage in the same pass. + /// + /// The associated source vectors. + /// The destination pixels. + internal static void PackFromAssociated(Span source, Span destination) + { + ref float sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ushort destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 lower = Numerics.Clamp(Unassociate(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)), Vector512.Zero, Vector512.One); + Vector512 upper = Numerics.Clamp(Unassociate(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512.Count))), Vector512.Zero, Vector512.One); + Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 lower = Numerics.Clamp(Unassociate(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)), Vector256.Zero, Vector256.One); + Vector256 upper = Numerics.Clamp(Unassociate(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256.Count))), Vector256.Zero, Vector256.One); + Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 lower = Numerics.Clamp(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)), Vector128.Zero, Vector128.One); + Vector128 upper = Numerics.Clamp(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128.Count))), Vector128.Zero, Vector128.One); + Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + + if (i < componentCount) + { + // Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel. + Vector128 vector = Numerics.Clamp(Unassociate(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)), Vector128.Zero, Vector128.One); + Vector128 packed = HalfTypeHelper.Pack(vector, vector); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); + } + + return; + } + + ref Vector4 vectorBase = ref Unsafe.As(ref sourceBase); + ref RgbaHalfP pixelBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Vector4 vector = Unsafe.Add(ref vectorBase, (uint)pixelIndex); + Numerics.UnPremultiply(ref vector); + vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); + Unsafe.Add(ref pixelBase, (uint)pixelIndex) = new RgbaHalfP(vector.X, vector.Y, vector.Z, vector.W); + } + } + + /// + /// Associates unassociated vectors with their stored binary16 alpha and packs them in one pass. + /// + /// The unassociated source vectors. + /// The destination pixels. + internal static void PackUnassociated(Span source, Span destination) + { + ref float sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ushort destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + // Alpha is rounded to the exact binary16 value that will be stored before RGB is associated with it. + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 lower = AssociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector512 upper = AssociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512.Count))); + Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 lower = AssociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector256 upper = AssociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256.Count))); + Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 lower = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector128 upper = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128.Count))); + Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + + if (i < componentCount) + { + // Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel. + Vector128 vector = AssociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector128 packed = HalfTypeHelper.Pack(vector, vector); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); + } + + return; + } + + ref Vector4 vectorBase = ref Unsafe.As(ref sourceBase); + ref RgbaHalfP pixelBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Unsafe.Add(ref pixelBase, (uint)pixelIndex) = RgbaHalfP.FromUnassociatedVector4(Unsafe.Add(ref vectorBase, (uint)pixelIndex)); + } + } + + /// + /// Reassociates vectors with their stored binary16 alpha and packs them in one pass. + /// + /// The associated source vectors. + /// The destination pixels. + internal static void PackAssociated(Span source, Span destination) + { + ref float sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ushort destinationBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + int componentCount = source.Length * Vector128.Count; + int i = 0; + + // Scaling RGB by storedAlpha / inputAlpha preserves straight color when binary16 rounds the alpha channel. + if (Vector512.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector512.Count; i += Vector512.Count) + { + Vector512 lower = ReassociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector512 upper = ReassociateForStorage(Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + Vector512.Count))); + Vector512.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector256.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector256.Count; i += Vector256.Count) + { + Vector256 lower = ReassociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector256 upper = ReassociateForStorage(Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + Vector256.Count))); + Vector256.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + } + + if (Vector128.IsHardwareAccelerated) + { + for (; i <= componentCount - Vector128.Count; i += Vector128.Count) + { + Vector128 lower = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector128 upper = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + Vector128.Count))); + Vector128.StoreUnsafe(HalfTypeHelper.Pack(lower, upper), ref destinationBase, (nuint)i); + } + + if (i < componentCount) + { + // Duplicate the final vector to use the two-input narrowing primitive, then store only one complete pixel. + Vector128 vector = ReassociateForStorage(Vector128.LoadUnsafe(ref sourceBase, (nuint)i)); + Vector128 packed = HalfTypeHelper.Pack(vector, vector); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref destinationBase, (uint)i)), packed.AsUInt64().GetElement(0)); + } + + return; + } + + ref Vector4 vectorBase = ref Unsafe.As(ref sourceBase); + ref RgbaHalfP pixelBase = ref Unsafe.As(ref destinationBase); + + for (int pixelIndex = 0; pixelIndex < source.Length; pixelIndex++) + { + Unsafe.Add(ref pixelBase, (uint)pixelIndex) = RgbaHalfP.FromAssociatedVector4(Unsafe.Add(ref vectorBase, (uint)pixelIndex)); + } + } + + /// + /// Associates RGB with alpha while preserving each alpha lane. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Associate(Vector128 source) + { + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 result = source * alpha; + return Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), alpha, result); + } + + /// + /// Associates RGB with alpha while preserving each alpha lane. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Associate(Vector256 source) + { + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 result = source * alpha; + return Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), alpha, result); + } + + /// + /// Associates RGB with alpha while preserving each alpha lane. + /// + /// The unassociated vectors. + /// The associated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Associate(Vector512 source) + { + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 result = source * alpha; + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector512.ConditionalSelect(alphaMask, alpha, result); + } + + /// + /// Unassociates RGB while preserving each alpha lane. + /// + /// The associated vectors. + /// The unassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 Unassociate(Vector128 source) + { + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + return Numerics.UnPremultiply(source, alpha); + } + + /// + /// Unassociates RGB while preserving each alpha lane. + /// + /// The associated vectors. + /// The unassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Unassociate(Vector256 source) + { + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + return Numerics.UnPremultiply(source, alpha); + } + + /// + /// Unassociates RGB while preserving each alpha lane. + /// + /// The associated vectors. + /// The unassociated vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 Unassociate(Vector512 source) + { + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + return Numerics.UnPremultiply(source, alpha); + } + + /// + /// Associates unassociated vectors with the alpha value binary16 storage can reproduce. + /// + /// The unassociated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 AssociateForStorage(Vector128 source) + { + source = Numerics.Clamp(source, Vector128.Zero, Vector128.One); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 storedAlpha = HalfTypeHelper.RoundToHalf(alpha); + Vector128 result = source * storedAlpha; + return Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); + } + + /// + /// Associates unassociated vectors with the alpha value binary16 storage can reproduce. + /// + /// The unassociated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 AssociateForStorage(Vector256 source) + { + source = Numerics.Clamp(source, Vector256.Zero, Vector256.One); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 storedAlpha = HalfTypeHelper.RoundToHalf(alpha); + Vector256 result = source * storedAlpha; + return Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); + } + + /// + /// Associates unassociated vectors with the alpha value binary16 storage can reproduce. + /// + /// The unassociated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 AssociateForStorage(Vector512 source) + { + source = Numerics.Clamp(source, Vector512.Zero, Vector512.One); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 storedAlpha = HalfTypeHelper.RoundToHalf(alpha); + Vector512 result = source * storedAlpha; + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Reassociates vectors with the alpha value binary16 storage can reproduce. + /// + /// The associated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ReassociateForStorage(Vector128 source) + { + Vector128 zero = Vector128.Zero; + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 clampedAlpha = Numerics.Clamp(alpha, Vector128.Zero, Vector128.One); + Vector128 storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); + Vector128 result = source * (storedAlpha / alpha); + result = Vector128.ConditionalSelect(Vector128.Create(0, 0, 0, -1).AsSingle(), storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector128.ConditionalSelect(Vector128.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates vectors with the alpha value binary16 storage can reproduce. + /// + /// The associated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ReassociateForStorage(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 clampedAlpha = Numerics.Clamp(alpha, Vector256.Zero, Vector256.One); + Vector256 storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); + Vector256 result = source * (storedAlpha / alpha); + result = Vector256.ConditionalSelect(Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(), storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector256.ConditionalSelect(Vector256.LessThanOrEqual(alpha, zero), zero, result); + } + + /// + /// Reassociates vectors with the alpha value binary16 storage can reproduce. + /// + /// The associated vectors. + /// The associated vectors ready for packing. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ReassociateForStorage(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 clampedAlpha = Numerics.Clamp(alpha, Vector512.Zero, Vector512.One); + Vector512 storedAlpha = HalfTypeHelper.RoundToHalf(clampedAlpha); + Vector512 result = source * (storedAlpha / alpha); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + + // Clamp after the alpha ratio, matching the scalar conversion for nonfinite RGB. + result = Numerics.Clamp(result, zero, storedAlpha); + return Vector512.ConditionalSelect(Vector512.LessThanOrEqual(alpha, zero), zero, result); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index a3833583fc..d4b868871e 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; @@ -19,21 +17,22 @@ 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, ReadOnlySpan sourcePixels, Span destinationPixels) { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + destinationPixels = destinationPixels[..sourcePixels.Length]; Span destinationVectors = MemoryMarshal.Cast(destinationPixels); - PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.Scale); + // Cross-format conversion uses public dispatch so associated source pixels are unassociated before entering RgbaVector storage. + PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + // RgbaVector.FromScaledVector4 clamps scaled input, so the optimized bulk path must preserve that behavior after unassociating. + Numerics.Clamp(MemoryMarshal.Cast(destinationVectors), 0F, 1F); } /// @@ -46,7 +45,11 @@ public override void FromVector4Destructive( Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, nameof(destinationPixels)); Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); - MemoryMarshal.Cast(sourceVectors).CopyTo(destinationPixels); + + // RgbaVector.FromVector4 and FromScaledVector4 both clamp to the representable [0, 1] range. Preserve that scalar + // contract before the zero-copy representation cast so bulk processor output cannot retain HDR or negative values. + Numerics.Clamp(MemoryMarshal.Cast(sourceVectors), 0F, 1F); + MemoryMarshal.Cast(sourceVectors).CopyTo(destinationPixels[..sourceVectors.Length]); } /// @@ -58,46 +61,9 @@ public override void ToVector4( { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, nameof(destinationVectors)); + destinationVectors = destinationVectors[..sourcePixels.Length]; 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.ConvertFromRgbaScaledVector4(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.ConvertFromRgbaScaledVector4(sp); - } - } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs index 435a521ba7..4d6b066ad5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short2.PixelOperations.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +16,24 @@ public partial struct Short2 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), true); + private static readonly Vector4 NativeOffset = new(-MinNeg, -MinNeg, 0F, 0F); + private static readonly Vector4 NativeRange = new(Range, Range, 1F, 1F); + + // Alpha is implicitly one, so both outward representations already contain associated color components. + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedVector4(configuration, source, destination); + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.ToUnassociatedScaledVector4(configuration, source, destination); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + // X and Y use signed-native coordinates while incoming W remains normalized alpha. Map only the stored components before + // the scaled bulk path unassociates RGB; transforming W would change the opacity that must be removed. + Vector4Converters.AddThenDivide(source, NativeOffset, NativeRange); + this.FromAssociatedScaledVector4Destructive(configuration, source, destination); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs index 546da9c57d..83c3ce0531 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Short4.PixelOperations.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using System.Numerics; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; namespace SixLabors.ImageSharp.PixelFormats; @@ -15,10 +17,88 @@ public partial struct Short4 /// internal class PixelOperations : PixelOperations { - private static readonly Lazy LazyInfo = - new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + private static readonly Vector4 NativeOffset = new(-MinNeg); + private static readonly Vector4 NativeMagnitude = new(Range); /// - public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], false, false); + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Signed integer components use an affine native range. Expand to scaled vectors, associate there, then restore the + // native zero point so RGB and alpha remain in the same coordinate system. + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, false, true); + Numerics.Premultiply(destination); + Vector4Converters.MultiplyThenAdd(destination, NativeMagnitude, -NativeOffset); + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination[..source.Length], false, true); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.ToVector4(MemoryMarshal.Cast(source), destination, false, true); + Numerics.Premultiply(destination); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), false, false); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Normalize all four components before unassociating, then use the scaled packing path to preserve Short4's affine mapping. + Vector4Converters.AddThenDivide(source, NativeOffset, NativeMagnitude); + Numerics.UnPremultiply(source); + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), false, true); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), false, true); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + Numerics.UnPremultiply(source); + SignedShort4PixelOperations.FromVector4(source, MemoryMarshal.Cast(destination), false, true); + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 0a13a15eda..a88823069d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -7,11 +7,12 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. -/// -/// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. -/// +/// Packed pixel type containing two 16-bit unsigned normalized values. /// +/// +/// , , and scaled vector conversions return x and y in [0, 1], z +/// as 0, and implicit alpha as 1. The packed storage layout matches DXGI_FORMAT_R16G16_UNORM. +/// public partial struct Rg32 : IPixel, IPackedVector { private static readonly Vector2 Max = new(ushort.MaxValue); @@ -43,7 +44,7 @@ public Rg32(float x, float y) /// /// 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,115 +55,163 @@ public Rg32(float x, float y) /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [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(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) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.ToVector2(), 0f, 1f); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromAssociatedScaledVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromAssociatedVector4(Vector4 source) { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromScaledVector4(Vector4 source) => FromVector4(source); + /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 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 FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromArgb32(Argb32 source) => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G)); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rg32 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 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 FromBgra5551(Bgra5551 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 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 105618cd96..b8c2d2098b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -4,6 +4,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.PixelFormats; @@ -35,8 +37,8 @@ public partial struct Rgb24 : IPixel [FieldOffset(2)] public byte B; - 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. @@ -44,7 +46,7 @@ public partial struct Rgb24 : IPixel /// The red component. /// The green component. /// The blue component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb24(byte r, byte g, byte b) { this.R = r; @@ -53,36 +55,14 @@ public Rgb24(byte r, byte g, byte 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 + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// 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(Rgb color) + => FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F)); /// /// Compares two objects for equality. @@ -92,7 +72,7 @@ public static implicit operator Rgb24(ColorSpaces.Rgb color) /// /// 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); /// @@ -103,169 +83,170 @@ public static implicit operator Rgb24(ColorSpaces.Rgb color) /// /// 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); - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromRgb24(this); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); - - /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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; - } + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 8, 8, 8), + PixelColorType.RGB, + PixelAlphaRepresentation.None); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } + public static PixelOperations CreatePixelOperations() => new PixelOperations(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromAssociatedScaledVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromAssociatedVector4(Vector4 source) { - this.R = source.R; - this.G = source.G; - this.B = source.B; + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); } /// - [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 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromVector4(Vector4 source) { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; + source *= MaxBytes; + source += Half; + source = Numerics.Clamp(source, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(source.AsVector128()).AsByte(); + return new Rgb24(result.GetElement(0), result.GetElement(4), result.GetElement(8)); } /// - [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 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) - { - byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 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 Rgb24 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromBgr24(Bgr24 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this = source.Rgb; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromBgra32(Bgra32 source) => new(source.R, source.G, source.B); - /// - [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 FromL8(L8 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromL16(L16 source) { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); + byte rgb = ColorNumerics.From16BitTo8Bit(source.PackedValue); + return new Rgb24(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromLa16(La16 source) => new(source.L, source.L, source.L); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromLa32(La32 source) { - this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); - this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); - this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); + byte rgb = ColorNumerics.From16BitTo8Bit(source.L); + return new Rgb24(rgb, rgb, rgb); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgb24(Rgb24 source) => new(source.R, source.G, source.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb24 FromRgba32(Rgba32 source) => new(source.R, source.G, 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(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 6bf25717ce..0a240b369a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -55,7 +55,7 @@ public Rgb48(ushort r, ushort g, ushort b) /// /// 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,159 +66,160 @@ public Rgb48(ushort r, ushort g, ushort b) /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromRgb48(this); /// - [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) - { - 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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.R / Max, this.G / Max, this.B / Max, 1f); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.R / Max, this.G / Max, this.B / Max, 1F); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 16, 16, 16), + PixelColorType.RGB, + PixelAlphaRepresentation.None); /// - [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); - } + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromAssociatedScaledVector4(Vector4 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromAssociatedVector4(Vector4 source) { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this = source.Rgb; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromVector4(Vector4 source) { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); - this.R = rgb; - this.G = rgb; - this.B = rgb; + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One) * Max; + return new Rgb48((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) + => new(ColorNumerics.From8BitTo16Bit(source.R), ColorNumerics.From8BitTo16Bit(source.G), ColorNumerics.From8BitTo16Bit(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 FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromL8(L8 source) { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; + ushort rgb = ColorNumerics.From8BitTo16Bit(source.PackedValue); + return new Rgb48(rgb, rgb, rgb); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromL16(L16 source) => new(source.PackedValue, source.PackedValue, source.PackedValue); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb48 FromLa16(La16 source) { - this.R = source.L; - this.G = source.L; - this.B = source.L; + ushort rgb = ColorNumerics.From8BitTo16Bit(source.L); + return new Rgb48(rgb, rgb, rgb); } - /// - [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 FromLa32(La32 source) => new(source.L, source.L, source.L); /// - [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(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 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 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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); /// - [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/Rgb96.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs new file mode 100644 index 0000000000..4b940a6a5f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs @@ -0,0 +1,245 @@ +// 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.PixelFormats; + +/// +/// Pixel type containing three 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Rgb96 : IPixel, IEquatable +{ + private const float InvMax = 1.0f / uint.MaxValue; + + // Use double here because at this magnitude a float cannot represent all 32-bit + // integer values exactly. A float only has 24 bits of precision, so around + // uint.MaxValue it can only represent multiples of 256 and will round + // 4294967295 up to 4294967296. Double has 53 bits of precision and can + // represent all uint values exactly, avoiding precision loss before scaling. + private const double Max = uint.MaxValue; + + /// + /// Gets the red component. + /// + public uint R; + + /// + /// Gets the green component. + /// + public uint G; + + /// + /// Gets the blue component. + /// + public uint B; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb96(uint r, uint g, uint b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb96 left, Rgb96 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(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb96 left, Rgb96 right) => !left.Equals(right); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R * InvMax, + this.G * InvMax, + this.B * InvMax, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromAssociatedScaledVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromAssociatedVector4(Vector4 source) + { + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new Rgb96( + (uint)Math.Round(source.X * Max), + (uint)Math.Round(source.Y * Max), + (uint)Math.Round(source.Z * Max)); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromAbgr32(Abgr32 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromArgb32(Argb32 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromBgr24(Bgr24 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromBgra32(Bgra32 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromL8(L8 source) + { + uint rgb = ColorNumerics.From8BitTo32Bit(source.PackedValue); + return new Rgb96(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromL16(L16 source) + { + uint rgb = ColorNumerics.From16BitTo32Bit(source.PackedValue); + return new(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromLa16(La16 source) + { + uint rgb = ColorNumerics.From8BitTo32Bit((byte)source.PackedValue); + return new(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromLa32(La32 source) + { + uint rgb = ColorNumerics.From16BitTo32Bit(source.L); + return new(rgb, rgb, rgb); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromRgb24(Rgb24 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromRgba32(Rgba32 source) => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromRgb48(Rgb48 source) => new(ColorNumerics.From16BitTo32Bit(source.R), ColorNumerics.From16BitTo32Bit(source.G), ColorNumerics.From16BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb96 FromRgba64(Rgba64 source) => new(ColorNumerics.From16BitTo32Bit(source.R), ColorNumerics.From16BitTo32Bit(source.G), ColorNumerics.From16BitTo32Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( + PixelComponentInfo.Create(3, 32, 32, 32), + PixelColorType.RGB, + PixelAlphaRepresentation.None); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromRgb96(this); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgb96({this.R}, {this.G}, {this.B})"); + + /// + public override readonly bool Equals(object? obj) => obj is Rgb96 rgb && rgb.R == this.R && rgb.G == this.G && rgb.B == this.B; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgb96 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 7bac1d9208..340e247772 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -7,12 +7,13 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed vector type containing unsigned normalized values ranging from 0 to 1. +/// Packed pixel type containing four unsigned normalized values. /// 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. -/// /// +/// +/// and scaled vector conversions return all components in [0, 1]. The storage layout matches +/// DXGI_FORMAT_R10G10B10A2_UNORM. +/// public partial struct Rgba1010102 : IPixel, IPackedVector { private static readonly Vector4 Multiplier = new(1023F, 1023F, 1023F, 3F); @@ -33,7 +34,7 @@ public Rgba1010102(float x, float y, float z, float w) /// Initializes a new instance of the struct. /// /// The vector containing the component values. - public Rgba1010102(Vector4 vector) => this.PackedValue = Pack(ref vector); + public Rgba1010102(Vector4 vector) => this.PackedValue = Pack(vector); /// public uint PackedValue { get; set; } @@ -46,7 +47,7 @@ public Rgba1010102(float x, float y, float z, float w) /// /// 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,26 +58,19 @@ public Rgba1010102(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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, @@ -84,81 +78,134 @@ public Rgba1010102(float x, float y, float z, float w) (this.PackedValue >> 30) & 0x03) / Multiplier; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 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 FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [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(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba1010102 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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); /// - [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/Rgba128.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs new file mode 100644 index 0000000000..86fab4f61f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs @@ -0,0 +1,241 @@ +// 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.PixelFormats; + +/// +/// Pixel type containing four 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue and alpha. +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Rgba128 : IPixel, IEquatable +{ + private const float InvMax = 1.0f / uint.MaxValue; + + // Use double here because at this magnitude a float cannot represent all 32-bit + // integer values exactly. A float only has 24 bits of precision, so around + // uint.MaxValue it can only represent multiples of 256 and will round + // 4294967295 up to 4294967296. Double has 53 bits of precision and can + // represent all uint values exactly, avoiding precision loss before scaling. + private const double Max = uint.MaxValue; + + /// + /// Gets the red component. + /// + public uint R; + + /// + /// Gets the green component. + /// + public uint G; + + /// + /// Gets the blue component. + /// + public uint B; + + /// + /// Gets the alpha channel. + /// + public uint A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba128(uint r, uint g, uint b, uint a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba128 left, Rgba128 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(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba128 left, Rgba128 right) => !left.Equals(right); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R * InvMax, + this.G * InvMax, + this.B * InvMax, + this.A * InvMax); + + /// + public static PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba128 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba128 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba128 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba128 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static Rgba128 FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + public static Rgba128 FromVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new Rgba128( + (uint)Math.Round(source.X * Max), + (uint)Math.Round(source.Y * Max), + (uint)Math.Round(source.Z * Max), + (uint)Math.Round(source.W * Max)); + } + + /// + public static Rgba128 FromAbgr32(Abgr32 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), ColorNumerics.From8BitTo32Bit(source.A)); + + /// + public static Rgba128 FromArgb32(Argb32 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), ColorNumerics.From8BitTo32Bit(source.A)); + + /// + public static Rgba128 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba128 FromBgr24(Bgr24 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), uint.MaxValue); + + /// + public static Rgba128 FromBgra32(Bgra32 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), ColorNumerics.From8BitTo32Bit(source.A)); + + /// + public static Rgba128 FromL8(L8 source) + { + uint rgb = ColorNumerics.From8BitTo32Bit(source.PackedValue); + return new Rgba128(rgb, rgb, rgb, rgb); + } + + /// + public static Rgba128 FromL16(L16 source) + { + uint rgb = ColorNumerics.From16BitTo32Bit(source.PackedValue); + return new(rgb, rgb, rgb, rgb); + } + + /// + public static Rgba128 FromLa16(La16 source) + { + uint rgb = ColorNumerics.From8BitTo32Bit((byte)source.PackedValue); + return new(rgb, rgb, rgb, rgb); + } + + /// + public static Rgba128 FromLa32(La32 source) + { + uint rgb = ColorNumerics.From16BitTo32Bit(source.L); + return new(rgb, rgb, rgb, rgb); + } + + /// + public static Rgba128 FromRgb24(Rgb24 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), uint.MaxValue); + + /// + public static Rgba128 FromRgba32(Rgba32 source) + => new(ColorNumerics.From8BitTo32Bit(source.R), ColorNumerics.From8BitTo32Bit(source.G), ColorNumerics.From8BitTo32Bit(source.B), ColorNumerics.From8BitTo32Bit(source.A)); + + /// + public static Rgba128 FromRgb48(Rgb48 source) + => new(ColorNumerics.From16BitTo32Bit(source.R), ColorNumerics.From16BitTo32Bit(source.G), ColorNumerics.From16BitTo32Bit(source.B), uint.MaxValue); + + /// + public static Rgba128 FromRgba64(Rgba64 source) + => new(ColorNumerics.From16BitTo32Bit(source.R), ColorNumerics.From16BitTo32Bit(source.G), ColorNumerics.From16BitTo32Bit(source.B), ColorNumerics.From16BitTo32Bit(source.A)); + + /// + public static PixelTypeInfo GetPixelTypeInfo() => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 32, 32, 32, 32), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public readonly Rgba32 ToRgba32() => Rgba32.FromRgba128(this); + + /// + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgba128({this.R}, {this.G}, {this.B}, {this.A})"); + + /// + public override readonly bool Equals(object? obj) => obj is Rgba128 rgb && rgb.R == this.R && rgb.G == this.G && rgb.B == this.B && rgb.A == this.A; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba128 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A); +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index a652c2b339..689d317935 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -1,11 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers.Binary; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; 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 Rgba32(byte r, byte g, byte b) /// 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,11 @@ public Rgba32(byte r, byte g, byte b, byte a) /// 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(new Vector4(r, g, b, a)) + { + } /// /// Initializes a new instance of the struct. @@ -94,9 +97,11 @@ public Rgba32(float r, float g, float b, float a = 1) /// /// 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(new Vector4(vector, 1f)) + { + } /// /// Initializes a new instance of the struct. @@ -104,9 +109,9 @@ public Rgba32(Vector3 vector) /// /// 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 +119,7 @@ public Rgba32(Vector4 vector) /// /// The packed value. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(uint packed) : this() => this.Rgba = packed; @@ -123,10 +128,10 @@ public Rgba32(uint packed) /// public uint Rgba { - [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [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 +140,10 @@ public uint Rgba /// 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 +157,10 @@ public Rgb24 Rgb /// 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,44 +172,21 @@ public Bgr24 Bgr /// public uint PackedValue { - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => this.Rgba; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// 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(Rgb color) => FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F)); /// /// Compares two objects for equality. @@ -214,7 +196,7 @@ public static implicit operator Rgba32(ColorSpaces.Rgb color) /// /// 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); /// @@ -225,205 +207,183 @@ public static implicit operator Rgba32(ColorSpaces.Rgb color) /// /// 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); - /// - /// Creates a new instance of the struct - /// from the given hexadecimal string. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - /// - /// The . - /// - /// Hexadecimal string is not in the correct format. - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgba32 ParseHex(string hex) - { - Guard.NotNull(hex, nameof(hex)); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => this; - if (!TryParseHex(hex, out Rgba32 rgba)) - { - throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); - return rgba; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; - /// - /// Attempts to creates a new instance of the struct - /// from the given hexadecimal string. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - /// When this method returns, contains the equivalent of the hexadecimal input. - /// - /// The . - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool TryParseHex(string? hex, out Rgba32 result) + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - result = default; - if (string.IsNullOrWhiteSpace(hex)) - { - return false; - } + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } - hex = ToRgbaHex(hex); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - if (hex is null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue)) - { - return false; - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - packedValue = BinaryPrimitives.ReverseEndianness(packedValue); - result = Unsafe.As(ref packedValue); - return true; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromUnassociatedVector4(Vector4 source) => FromVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) => this.Pack(ref vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 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 Rgba32 FromAbgr32(Abgr32 source) => new(source.R, source.G, source.B, source.A); /// - [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 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); /// - [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 FromBgr24(Bgr24 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; - this.A = source.A; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromBgra32(Bgra32 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 Rgba32(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 Rgba32(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) + }; + + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + /// The pixel value as Rgba32. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgb96(Rgb96 source) + => new() + { + R = ColorNumerics.From32BitTo8Bit(source.R), + G = ColorNumerics.From32BitTo8Bit(source.G), + B = ColorNumerics.From32BitTo8Bit(source.B), + A = byte.MaxValue + }; + + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + /// The pixel value as Rgba32. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32 FromRgba128(Rgba128 source) + => new() + { + R = ColorNumerics.From32BitTo8Bit(source.R), + G = ColorNumerics.From32BitTo8Bit(source.G), + B = ColorNumerics.From32BitTo8Bit(source.B), + A = ColorNumerics.From32BitTo8Bit(source.A), + }; /// /// Converts the value of this instance to a hexadecimal string. @@ -439,107 +399,26 @@ public readonly string ToHex() 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. - /// - /// The x-component - /// 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); - } - - /// - /// 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); - } - /// /// 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; - } - - /// - /// Converts the specified hex value to an rrggbbaa hex value. - /// - /// The hex value to convert. - /// - /// A rrggbbaa hex value. - /// - private static string? ToRgbaHex(string hex) - { - if (hex[0] == '#') - { - hex = hex[1..]; - } - - if (hex.Length == 8) - { - return hex; - } - - if (hex.Length == 6) - { - return hex + "FF"; - } - - if (hex.Length is < 3 or > 4) - { - return null; - } - - char r = hex[0]; - char g = hex[1]; - char b = hex[2]; - char a = hex.Length == 3 ? 'F' : hex[3]; - - return new string(new[] { r, r, g, g, b, b, a, a }); + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Rgba32(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs new file mode 100644 index 0000000000..c7bb17ce87 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32P.cs @@ -0,0 +1,273 @@ +// 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 SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Packed pixel type containing associated red, green, blue, and alpha components as 8-bit unsigned normalized values. +/// Components are stored in red, green, blue, and alpha order from least to most significant byte. +/// +/// +/// The native component, packed, and vector representations use associated alpha. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct Rgba32P : IPixel, IPackedVector +{ + private static readonly Vector4 Half = new(0.5F); + private static readonly Vector4 MaxBytes = new(byte.MaxValue); + + /// + /// Gets or sets the associated red component. + /// + public byte R; + + /// + /// Gets or sets the associated green component. + /// + public byte G; + + /// + /// Gets or sets the associated blue component. + /// + public byte B; + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Rgba32P(byte r, byte g, byte b) + : this(r, g, b, byte.MaxValue) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Rgba32P(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public Rgba32P(float r, float g, float b) + : this(new Vector4(r, g, b, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public Rgba32P(float r, float g, float b, float a) + : this(new Vector4(r, g, b, a)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Rgba32P(Vector3 vector) + : this(new Vector4(vector, 1F)) + { + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public Rgba32P(Vector4 vector) + : this() => this = FromScaledVector4(vector); + + /// + /// Initializes a new instance of the struct from a packed associated value. + /// + /// The packed associated value. + public Rgba32P(uint packed) + : this() => this.PackedValue = packed; + + /// + public uint PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(Rgba32P left, Rgba32P right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(Rgba32P left, Rgba32P right) => !left.Equals(right); + + /// + public readonly Rgba32 ToRgba32() + => Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A)); + + /// + public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + // Divide the stored byte magnitudes before normalization so unassociation cannot move an exact byte conversion across its rounding midpoint. + return Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + public readonly Vector4 ToVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 8, 8, 8, 8), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static Rgba32P FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromUnassociatedScaledVector4(Vector4 source) + => Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToRgba32P(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba32P FromAssociatedScaledVector4(Vector4 source) + { + // Rescale associated RGB when alpha rounds to a different byte so the stored channels remain associated with the alpha actually written. + return Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4ToRgba32P(source); + } + + /// + public static Rgba32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static Rgba32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is Rgba32P other && this.Equals(other); + + /// + public readonly bool Equals(Rgba32P other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => $"Rgba32P({this.R}, {this.G}, {this.B}, {this.A})"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Rgba32P Pack(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + // Each converted component occupies one 32-bit lane. Reinterpreting those lanes as bytes exposes their low bytes at offsets 0, 4, 8, and 12. + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new Rgba32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 81c9591480..300f2357fb 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -8,11 +8,12 @@ namespace SixLabors.ImageSharp.PixelFormats; /// -/// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 65535. -/// -/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. -/// +/// Packed pixel type containing four 16-bit unsigned normalized values. /// +/// +/// Component fields expose storage values in [0, 65535]. and scaled vector conversions +/// return them in [0, 1]. The storage layout matches DXGI_FORMAT_R16G16B16A16_UNORM. +/// [StructLayout(LayoutKind.Sequential)] public partial struct Rgba64 : IPixel, IPackedVector { @@ -45,7 +46,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 +59,64 @@ public Rgba64(ushort r, ushort g, ushort b, ushort a) /// 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 +124,12 @@ public Rgba64(Rgb24 source) /// 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 +137,7 @@ public Rgba64(Bgr24 source) /// 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,39 +152,23 @@ public Rgba64(Vector4 vector) /// public Rgb48 Rgb { - [MethodImpl(InliningOptions.ShortMethod)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [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)] - readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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. /// @@ -192,7 +177,7 @@ public ulong PackedValue /// /// 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; /// @@ -203,256 +188,184 @@ public ulong PackedValue /// /// 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; /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromRgba64(this); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [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 readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [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); - } + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [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 readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); - this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); - this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } /// - [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); - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); /// - [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(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromAssociatedScaledVector4(Vector4 source) { - this.R = source.PackedValue; - this.G = source.PackedValue; - this.B = source.PackedValue; - this.A = ushort.MaxValue; + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) - { - ushort rgb = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); - this.R = rgb; - this.G = rgb; - this.B = rgb; - this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [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(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromScaledVector4(Vector4 source) => FromVector4(source); /// - [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 FromVector4(Vector4 source) => new(source); /// - [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 FromAbgr32(Abgr32 source) => new(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromArgb32(Argb32 source) => new(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromBgr24(Bgr24 source) => new(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromBgra32(Bgra32 source) => new(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromL8(L8 source) { - dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - dest.G = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - dest.B = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + ushort rgb = ColorNumerics.From8BitTo16Bit(source.PackedValue); + return new Rgba64(rgb, rgb, rgb, 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 FromRgb48(Rgb48 source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromLa16(La16 source) { - this.Rgb = source; - this.A = ushort.MaxValue; + ushort rgb = ColorNumerics.From8BitTo16Bit(source.L); + return new Rgba64(rgb, rgb, rgb, ColorNumerics.From8BitTo16Bit(source.A)); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromLa32(La32 source) => new(source.L, source.L, source.L, source.A); - /// - /// 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 FromRgb24(Rgb24 source) => new(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgba32(Rgba32 source) => new(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgb48(Rgb48 source) => new(source.R, source.G, source.B, ushort.MaxValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba64 FromRgba64(Rgba64 source) => new(source.R, source.G, source.B, source.A); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - 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); - return new Bgra32(r, g, b, a); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Bgra32 ToBgra32() => Bgra32.FromRgba64(this); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - 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); - return new Argb32(r, g, b, a); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Argb32 ToArgb32() => Argb32.FromRgba64(this); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - 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); - return new Abgr32(r, g, b, a); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Abgr32 ToAbgr32() => Abgr32.FromRgba64(this); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Rgb24 ToRgb24() - { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - return new Rgb24(r, g, b); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgb24 ToRgb24() => Rgb24.FromRgba64(this); /// /// Convert to . /// /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Bgr24 ToBgr24() - { - byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); - byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); - byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); - return new Bgr24(r, g, b); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Bgr24 ToBgr24() => Bgr24.FromRgba64(this); /// 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/RgbaHalf.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaHalf.cs new file mode 100644 index 0000000000..573bfca55f --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaHalf.cs @@ -0,0 +1,227 @@ +// 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.PixelFormats; + +/// +/// Packed pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. +/// The color components are stored in red, green, blue, and alpha order. +/// +/// +/// and scaled vector conversions return the same component values in the nominal color range +/// [0, 1]. The packed representation is binary-compatible with DXGI_FORMAT_R16G16B16A16_FLOAT. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct RgbaHalf : IPixel, IPackedVector +{ + /// + /// Gets or sets the red component. + /// + public Half R; + + /// + /// Gets or sets the green component. + /// + public Half G; + + /// + /// Gets or sets the blue component. + /// + public Half B; + + /// + /// Gets or sets the alpha component. + /// + public Half A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + public RgbaHalf(float r, float g, float b) + : this(r, g, b, 1F) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + public RgbaHalf(float r, float g, float b, float a) + { + this.R = (Half)r; + this.G = (Half)g; + this.B = (Half)b; + this.A = (Half)a; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public RgbaHalf(Vector4 vector) + : this(vector.X, vector.Y, vector.Z, vector.W) + { + } + + /// + public ulong PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(RgbaHalf left, RgbaHalf right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(RgbaHalf left, RgbaHalf 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( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalf FromVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new RgbaHalf(source); + } + + /// + public static RgbaHalf FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalf FromRgba64(Rgba64 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is RgbaHalf other && this.Equals(other); + + /// + public readonly bool Equals(RgbaHalf other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => FormattableString.Invariant($"RgbaHalf({(float)this.R:#0.##}, {(float)this.G:#0.##}, {(float)this.B:#0.##}, {(float)this.A:#0.##})"); +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaHalfP.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaHalfP.cs new file mode 100644 index 0000000000..75fe4907ce --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaHalfP.cs @@ -0,0 +1,242 @@ +// 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.PixelFormats; + +/// +/// Packed pixel type containing four associated 16-bit floating-point values typically ranging from 0 to 1. +/// The color components are stored in red, green, blue, and alpha order. +/// +/// +/// and scaled vector conversions return the same associated component values in the nominal +/// color range [0, 1]. The packed representation is binary-compatible with +/// DXGI_FORMAT_R16G16B16A16_FLOAT. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct RgbaHalfP : IPixel, IPackedVector +{ + /// + /// Gets or sets the associated red component. + /// + public Half R; + + /// + /// Gets or sets the associated green component. + /// + public Half G; + + /// + /// Gets or sets the associated blue component. + /// + public Half B; + + /// + /// Gets or sets the alpha component. + /// + public Half A; + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + public RgbaHalfP(float r, float g, float b) + : this(r, g, b, 1F) + { + } + + /// + /// Initializes a new instance of the struct from associated components. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + public RgbaHalfP(float r, float g, float b, float a) + { + this.R = (Half)r; + this.G = (Half)g; + this.B = (Half)b; + this.A = (Half)a; + } + + /// + /// Initializes a new instance of the struct from an associated vector. + /// + /// The associated vector. + public RgbaHalfP(Vector4 vector) + : this() => this = FromAssociatedScaledVector4(vector); + + /// + public ulong PackedValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two values for equality. + /// + /// The left value. + /// The right value. + /// when the values are equal. + public static bool operator ==(RgbaHalfP left, RgbaHalfP right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left value. + /// The right value. + /// when the values are not equal. + public static bool operator !=(RgbaHalfP left, RgbaHalfP right) => !left.Equals(right); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToUnassociatedScaledVector4()); + + /// + [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); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.UnPremultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromVector4(Vector4 source) => FromAssociatedVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromUnassociatedScaledVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + + // RGB must be associated with the alpha value that binary16 storage can reproduce, not the higher-precision input alpha. + source.W = HalfTypeHelper.Unpack(HalfTypeHelper.Pack(source.W)); + Numerics.Premultiply(ref source); + return new RgbaHalfP(source.X, source.Y, source.Z, source.W); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromAssociatedScaledVector4(Vector4 source) + { + float alpha = source.W; + + if (alpha <= 0F) + { + return default; + } + + float storedAlpha = HalfTypeHelper.Unpack(HalfTypeHelper.Pack(Numerics.Clamp(alpha, 0F, 1F))); + + // Preserve the represented straight color when binary16 rounds alpha, then restore the associated RGB <= alpha invariant. + source *= storedAlpha / alpha; + source.W = storedAlpha; + Numerics.ClampRgbToAlpha(ref source); + return new RgbaHalfP(source.X, source.Y, source.Z, source.W); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaHalfP FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + public static RgbaHalfP FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public static RgbaHalfP FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + /// + public override readonly bool Equals(object? obj) => obj is RgbaHalfP other && this.Equals(other); + + /// + public readonly bool Equals(RgbaHalfP other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override readonly string ToString() => FormattableString.Invariant($"RgbaHalfP({(float)this.R:#0.##}, {(float)this.G:#0.##}, {(float)this.B:#0.##}, {(float)this.A:#0.##})"); +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 899987b712..b1704d5267 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -53,7 +53,7 @@ public partial struct RgbaVector : IPixel /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public RgbaVector(float r, float g, float b, float a = 1) { this.R = r; @@ -70,7 +70,7 @@ public RgbaVector(float r, float g, float b, float a = 1) /// /// 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,102 +81,147 @@ public RgbaVector(float r, float g, float b, float a = 1) /// /// 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(); - /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.R, this.G, this.B, this.A); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 32, 32, 32, 32), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() { - vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.A = vector.W; + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new(this.R, this.G, this.B, this.A); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromScaledVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new RgbaVector(source.X, source.Y, source.Z, source.W); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaVector FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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. + /// + /// + /// 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. @@ -195,7 +240,6 @@ public readonly string ToHex() 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 832e8c770f..d6ab2ac1a3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -8,10 +8,12 @@ namespace SixLabors.ImageSharp.PixelFormats; /// /// Packed pixel type containing two 16-bit signed integer values. -/// -/// Ranges from [-32767, -32767, 0, 1] to [32767, 32767, 0, 1] in vector form. -/// /// +/// +/// and return stored components in [-32768, 32767]. +/// Scaled vector conversions map the full stored range to [0, 1]. The packed storage layout matches +/// DXGI_FORMAT_R16G16_SINT. +/// public partial struct Short2 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; @@ -20,6 +22,9 @@ public partial struct Short2 : IPixel, IPackedVector // Two's complement private const float MinNeg = ~(int)MaxPos; + // Scaled conversions cover every signed 16-bit code, including the asymmetric minimum value. + private const float Range = MaxPos - MinNeg; + private static readonly Vector2 Max = new(MaxPos); private static readonly Vector2 Min = new(MinNeg); @@ -50,7 +55,7 @@ public Short2(float x, float y) /// /// 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,126 +66,170 @@ public Short2(float x, float y) /// /// 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); /// - public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 65534F; - scaled -= new Vector2(32767F); - this.PackedValue = Pack(scaled); + Vector2 scaled = this.ToVector2(); + scaled -= Min; + scaled /= Range; + return new Vector4(scaled, 0f, 1f); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToScaledVector4() + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0f, 1f); + + /// + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 16, 16), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); + + /// + public static PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromAssociatedScaledVector4(Vector4 source) { - var scaled = this.ToVector2(); - scaled += new Vector2(32767F); - scaled /= 65534F; - return new Vector4(scaled, 0F, 1F); + // The destination has implicit alpha one, but associated input must be restored before its alpha is discarded. + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromVector4(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromAssociatedVector4(Vector4 source) { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(vector2); + // Only the stored color components use the signed native encoding; W remains the scaled source alpha. + source.X = (source.X - MinNeg) / Range; + source.Y = (source.Y - MinNeg) / Range; + return FromAssociatedScaledVector4(source); } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly Vector4 ToVector4() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromScaledVector4(Vector4 source) + { + Vector2 scaled = new Vector2(source.X, source.Y) * Range; + scaled += Min; + return new Short2 { PackedValue = Pack(scaled) }; + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromVector4(Vector4 source) => new() { PackedValue = Pack(new Vector2(source.X, source.Y)) }; /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short2 FromRgb48(Rgb48 source) => FromScaledVector4(source.ToScaledVector4()); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.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. /// /// 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 c4dc324a13..fc3d3367df 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -8,10 +8,11 @@ namespace SixLabors.ImageSharp.PixelFormats; /// /// Packed pixel type containing four 16-bit signed integer values. -/// -/// Ranges from [-37267, -37267, -37267, -37267] to [37267, 37267, 37267, 37267] in vector form. -/// /// +/// +/// returns stored components in [-32768, 32767]. Scaled vector conversions map the full +/// stored range to [0, 1]. The packed storage layout matches DXGI_FORMAT_R16G16B16A16_SINT. +/// public partial struct Short4 : IPixel, IPackedVector { // Largest two byte positive number 0xFFFF >> 1; @@ -20,8 +21,11 @@ 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); + // Scaled conversions cover every signed 16-bit code, including the asymmetric minimum value. + private const float Range = MaxPos - MinNeg; + + private static readonly Vector4 Max = new(MaxPos); + private static readonly Vector4 Min = new(MinNeg); /// /// Initializes a new instance of the struct. @@ -39,7 +43,7 @@ public Short4(float x, float y, float z, float w) /// 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 Short4(Vector4 vector) => this.PackedValue = Pack(vector); /// public ulong PackedValue { get; set; } @@ -52,7 +56,7 @@ public Short4(float x, float y, float z, float w) /// /// 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,132 +67,186 @@ public Short4(float x, float y, float z, float w) /// /// 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 readonly PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromScaledVector4(Vector4 vector) - { - vector *= 65534F; - vector -= new Vector4(32767F); - this.FromVector4(vector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4()); /// - [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 -= Min; + scaled /= Range; 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()); + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 16, 16, 16, 16), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.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 readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() + { + Vector4 vector = this.ToAssociatedScaledVector4(); - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + // Native components use the full asymmetric signed range, so scaled zero must map to -32768 rather than -32767. + vector *= Range; + vector += Min; + return vector; + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromUnassociatedVector4(Vector4 source) => FromVector4(source); /// - [MethodImpl(InliningOptions.ShortMethod)] - public void ToRgba32(ref Rgba32 dest) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromAssociatedVector4(Vector4 source) { - dest.FromScaledVector4(this.ToScaledVector4()); + // Map the full signed native encoding to scaled [0, 1] space before unassociating. + source -= Min; + source /= Range; + return FromAssociatedScaledVector4(source); } /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromScaledVector4(Vector4 source) + { + source *= Range; + source += Min; + return FromVector4(source); + } - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Short4 FromVector4(Vector4 source) => 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); /// - [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/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index 7e84bd6392..854b515dc0 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 virtual void FromArgb32(Configuration configuration, ReadOnlySpan /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToArgb32Bytes(Configuration configuration, ReadOnlySpan sour /// /// 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 virtual void FromAbgr32(Configuration configuration, ReadOnlySpan /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToAbgr32Bytes(Configuration configuration, ReadOnlySpan sour /// /// 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 virtual void FromBgr24(Configuration configuration, ReadOnlySpan s /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToBgr24Bytes(Configuration configuration, ReadOnlySpan sourc /// /// 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 virtual void FromBgra32(Configuration configuration, ReadOnlySpan /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToBgra32Bytes(Configuration configuration, ReadOnlySpan sour /// /// 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 virtual void FromL8(Configuration configuration, ReadOnlySpan source, /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToL8Bytes(Configuration configuration, ReadOnlySpan sourcePi /// /// 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 virtual void FromL16(Configuration configuration, ReadOnlySpan sourc /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToL16Bytes(Configuration configuration, ReadOnlySpan sourceP /// /// 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 virtual void FromLa16(Configuration configuration, ReadOnlySpan sou /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToLa16Bytes(Configuration configuration, ReadOnlySpan source /// /// 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 virtual void FromLa32(Configuration configuration, ReadOnlySpan sou /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToLa32Bytes(Configuration configuration, ReadOnlySpan source /// /// 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 virtual void FromRgb24(Configuration configuration, ReadOnlySpan s /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToRgb24Bytes(Configuration configuration, ReadOnlySpan sourc /// /// 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 virtual void FromRgba32(Configuration configuration, ReadOnlySpan /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToRgba32Bytes(Configuration configuration, ReadOnlySpan sour /// /// 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 virtual void FromRgb48(Configuration configuration, ReadOnlySpan s /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToRgb48Bytes(Configuration configuration, ReadOnlySpan sourc /// /// 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 virtual void FromRgba64(Configuration configuration, ReadOnlySpan /// /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 void ToRgba64Bytes(Configuration configuration, ReadOnlySpan sour /// /// 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 virtual void FromBgra5551(Configuration configuration, ReadOnlySpan /// 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.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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 8ba3398e39..769f3b44ba 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#>.FromUnassociatedScaledVector4(Unsafe.Add(ref sourceBase, i).ToUnassociatedScaledVector4()); } } /// /// 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}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index bf1391990f..b17c90b923 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -34,182 +34,314 @@ public virtual PixelBlender GetPixelBlender(PixelColorBlendingMode color case PixelAlphaCompositionMode.Clear: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyClear.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddClear.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractClear.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenClear.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenClear.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenClear.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayClear.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightClear.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyClear; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddClear; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractClear; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenClear; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenClear; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenClear; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayClear; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightClear; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeClear; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnClear; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightClear; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceClear; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionClear; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueClear; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationClear; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorClear; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityClear; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalClear.Instance; + default: return DefaultPixelBlenders.NormalClear; } case PixelAlphaCompositionMode.Xor: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyXor.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddXor.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractXor.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenXor.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenXor.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenXor.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayXor.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightXor.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyXor; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddXor; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractXor; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenXor; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenXor; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenXor; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayXor; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightXor; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeXor; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnXor; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightXor; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceXor; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionXor; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueXor; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationXor; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorXor; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityXor; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalXor.Instance; + default: return DefaultPixelBlenders.NormalXor; } case PixelAlphaCompositionMode.Src: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrc.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrc.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrc.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrc.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrc.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrc.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrc.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrc.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrc; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrc; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrc; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrc; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrc; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrc; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrc; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrc; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeSrc; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnSrc; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightSrc; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceSrc; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionSrc; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueSrc; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationSrc; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorSrc; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminositySrc; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrc.Instance; + default: return DefaultPixelBlenders.NormalSrc; } case PixelAlphaCompositionMode.SrcAtop: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcAtop.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcAtop.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcAtop.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcAtop.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcAtop.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcAtop.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcAtop.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcAtop.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcAtop; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcAtop; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcAtop; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcAtop; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcAtop; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcAtop; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcAtop; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcAtop; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeSrcAtop; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnSrcAtop; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightSrcAtop; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceSrcAtop; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionSrcAtop; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueSrcAtop; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationSrcAtop; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorSrcAtop; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminositySrcAtop; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcAtop.Instance; + default: return DefaultPixelBlenders.NormalSrcAtop; } case PixelAlphaCompositionMode.SrcIn: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcIn.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcIn.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcIn.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcIn.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcIn.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcIn.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcIn.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcIn.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcIn; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcIn; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcIn; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcIn; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcIn; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcIn; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcIn; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcIn; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeSrcIn; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnSrcIn; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightSrcIn; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceSrcIn; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionSrcIn; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueSrcIn; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationSrcIn; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorSrcIn; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminositySrcIn; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcIn.Instance; + default: return DefaultPixelBlenders.NormalSrcIn; } case PixelAlphaCompositionMode.SrcOut: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOut.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOut.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOut.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOut.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOut.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOut.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOut.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOut.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOut; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOut; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOut; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOut; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOut; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOut; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOut; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOut; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeSrcOut; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnSrcOut; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightSrcOut; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceSrcOut; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionSrcOut; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueSrcOut; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationSrcOut; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorSrcOut; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminositySrcOut; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcOut.Instance; + default: return DefaultPixelBlenders.NormalSrcOut; } case PixelAlphaCompositionMode.Dest: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDest.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDest.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDest.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDest.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDest.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDest.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDest.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDest.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDest; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDest; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDest; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDest; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDest; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDest; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDest; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDest; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeDest; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnDest; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightDest; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceDest; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionDest; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueDest; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationDest; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorDest; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityDest; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDest.Instance; + default: return DefaultPixelBlenders.NormalDest; } case PixelAlphaCompositionMode.DestAtop: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestAtop.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestAtop.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestAtop.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestAtop.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestAtop.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestAtop.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestAtop.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestAtop.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestAtop; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestAtop; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestAtop; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestAtop; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestAtop; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestAtop; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestAtop; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestAtop; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeDestAtop; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnDestAtop; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightDestAtop; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceDestAtop; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionDestAtop; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueDestAtop; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationDestAtop; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorDestAtop; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityDestAtop; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestAtop.Instance; + default: return DefaultPixelBlenders.NormalDestAtop; } case PixelAlphaCompositionMode.DestIn: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestIn.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestIn.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestIn.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestIn.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestIn.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestIn.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestIn.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestIn.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestIn; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestIn; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestIn; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestIn; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestIn; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestIn; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestIn; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestIn; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeDestIn; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnDestIn; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightDestIn; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceDestIn; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionDestIn; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueDestIn; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationDestIn; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorDestIn; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityDestIn; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestIn.Instance; + default: return DefaultPixelBlenders.NormalDestIn; } case PixelAlphaCompositionMode.DestOut: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOut.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOut.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOut.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOut.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOut.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOut.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOut.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOut.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOut; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOut; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOut; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOut; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOut; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOut; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOut; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOut; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeDestOut; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnDestOut; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightDestOut; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceDestOut; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionDestOut; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueDestOut; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationDestOut; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorDestOut; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityDestOut; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestOut.Instance; + default: return DefaultPixelBlenders.NormalDestOut; } case PixelAlphaCompositionMode.DestOver: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOver.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOver.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOver.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOver.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOver.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOver.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOver.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOver.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOver; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOver; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOver; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOver; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOver; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOver; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOver; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOver; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeDestOver; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnDestOver; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightDestOver; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceDestOver; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionDestOver; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueDestOver; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationDestOver; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorDestOver; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityDestOver; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalDestOver.Instance; + default: return DefaultPixelBlenders.NormalDestOver; + } + + case PixelAlphaCompositionMode.Plus: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyPlus; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddPlus; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractPlus; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenPlus; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenPlus; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenPlus; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayPlus; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightPlus; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgePlus; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnPlus; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightPlus; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferencePlus; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionPlus; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HuePlus; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationPlus; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorPlus; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminosityPlus; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalPlus; } case PixelAlphaCompositionMode.SrcOver: default: switch (colorMode) { - case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver.Instance; - case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOver.Instance; - case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver.Instance; - case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOver.Instance; - case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOver.Instance; - case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOver.Instance; - case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver.Instance; - case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver.Instance; + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOver; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOver; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOver; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOver; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver; + case PixelColorBlendingMode.ColorDodge: return DefaultPixelBlenders.ColorDodgeSrcOver; + case PixelColorBlendingMode.ColorBurn: return DefaultPixelBlenders.ColorBurnSrcOver; + case PixelColorBlendingMode.SoftLight: return DefaultPixelBlenders.SoftLightSrcOver; + case PixelColorBlendingMode.Difference: return DefaultPixelBlenders.DifferenceSrcOver; + case PixelColorBlendingMode.Exclusion: return DefaultPixelBlenders.ExclusionSrcOver; + case PixelColorBlendingMode.Hue: return DefaultPixelBlenders.HueSrcOver; + case PixelColorBlendingMode.Saturation: return DefaultPixelBlenders.SaturationSrcOver; + case PixelColorBlendingMode.Color: return DefaultPixelBlenders.ColorSrcOver; + case PixelColorBlendingMode.Luminosity: return DefaultPixelBlenders.LuminositySrcOver; case PixelColorBlendingMode.Normal: - default: return DefaultPixelBlenders.NormalSrcOver.Instance; + default: return DefaultPixelBlenders.NormalSrcOver; } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index beebec8283..87957a995a 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -5,21 +5,24 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats; /// -/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations -/// for pixel buffers of type . +/// Provides bulk conversion operations for pixel buffers of type . /// +/// +/// The default conversion behavior is suitable for pixel formats that store unassociated alpha. +/// Pixel formats that store associated alpha should derive their operations from +/// . +/// /// The pixel format. 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); + private static readonly Lazy> LazyInstance = new(TPixel.CreatePixelOperations, true); /// /// Gets the global instance for the pixel type @@ -28,140 +31,319 @@ public partial class PixelOperations public static PixelOperations Instance => LazyInstance.Value; #pragma warning restore CA1000 // Do not declare static members on generic types + /// + /// Converts pixels to unassociated vectors in the pixel format's native numeric range. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Utils.Vector4Converters.Default.ToVector4(source, destination, PixelConversionModifiers.UnPremultiply); + + /// + /// Converts pixels to associated vectors in the pixel format's native numeric range. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + => Utils.Vector4Converters.Default.ToVector4(source, destination, PixelConversionModifiers.Premultiply); + + /// + /// Converts pixels to the unassociated scaled vector representation used by color operations. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToUnassociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + => Utils.Vector4Converters.Default.ToVector4(source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + /// + /// Converts pixels to associated scaled vectors. + /// + /// The configuration. + /// The source pixels. + /// The destination vectors. + protected virtual void ToAssociatedScaledVector4( + Configuration configuration, + ReadOnlySpan source, + Span destination) + => Utils.Vector4Converters.Default.ToVector4(source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + + /// + /// Converts unassociated vectors in the pixel format's native numeric range to pixels. + /// + /// The configuration. + /// The source vectors. Implementations may modify this buffer during conversion. + /// The destination pixels. + protected virtual void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + => Utils.Vector4Converters.Default.FromVector4(source, destination, PixelConversionModifiers.UnPremultiply); + + /// + /// Converts associated vectors in the pixel format's native numeric range to pixels. + /// + /// The configuration. + /// The source vectors. Implementations may modify this buffer during conversion. + /// The destination pixels. + protected virtual void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + => Utils.Vector4Converters.Default.FromVector4(source, destination, PixelConversionModifiers.Premultiply); + + /// + /// Converts unassociated scaled vectors to pixels. + /// + /// The configuration. + /// The source vectors. Implementations may modify this buffer during conversion. + /// The destination pixels. + protected virtual void FromUnassociatedScaledVector4Destructive( + Configuration configuration, + Span source, + Span destination) + => Utils.Vector4Converters.Default.FromVector4(source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + /// + /// Converts associated scaled vectors to pixels. + /// + /// The configuration. + /// The source vectors. Implementations may modify this buffer during conversion. + /// The destination pixels. + protected virtual void FromAssociatedScaledVector4Destructive( + Configuration configuration, + Span source, + Span destination) + => Utils.Vector4Converters.Default.FromVector4(source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + /// /// Gets the pixel type info for the given . /// /// The . - public virtual PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + public PixelTypeInfo GetPixelTypeInfo() => TPixel.GetPixelTypeInfo(); /// - /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. - /// The method is DESTRUCTIVE altering the contents of . + /// Converts vectors to pixels using the numeric range, alpha representation, and companding specified by . + /// The contents of are not preserved. /// /// - /// The destructive behavior is a design choice for performance reasons. - /// In a typical use case the contents of are abandoned after the conversion. + /// Callers must not rely on the contents of after this method returns. /// - /// A to configure internal operations - /// The to the source vectors. - /// The to the destination colors. - /// The to apply during the conversion + /// The configuration. + /// The source vectors. Their contents may be modified during conversion. + /// The destination pixels. + /// The representation of the source vectors and the transformations applied before storing the pixels. public virtual void FromVector4Destructive( Configuration configuration, Span sourceVectors, - Span destinationPixels, + Span destination, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destination, nameof(destination)); + + bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply); + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + // Transfer functions operate on straight color components, so restore straight RGB before compressing associated input. + if (associated) + { + Numerics.UnPremultiply(sourceVectors); + } - Utils.Vector4Converters.Default.FromVector4(sourceVectors, destinationPixels, modifiers); + SRgbCompanding.Compress(sourceVectors); + + if (scaled) + { + this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); + } + + return; + } + + if (scaled) + { + if (associated) + { + this.FromAssociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedScaledVector4Destructive(configuration, sourceVectors, destination); + } + } + else if (associated) + { + this.FromAssociatedVector4Destructive(configuration, sourceVectors, destination); + } + else + { + this.FromUnassociatedVector4Destructive(configuration, sourceVectors, destination); + } } /// - /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. - /// The method is DESTRUCTIVE altering the contents of . + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. + /// The contents of are not preserved. /// /// - /// The destructive behavior is a design choice for performance reasons. - /// In a typical use case the contents of are abandoned after the conversion. + /// Callers must not rely on the contents of after this method returns. /// /// A to configure internal operations - /// The to the source vectors. - /// The to the destination colors. + /// The source vectors. Their contents may be modified during conversion. + /// 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'. + /// Converts pixels to vectors using the numeric range, alpha representation, and companding specified by . /// - /// A to configure internal operations - /// The to the source colors. - /// The to the destination vectors. - /// The to apply during the conversion + /// The configuration. + /// The source pixels. + /// The destination vectors. + /// The requested vector representation and the transformations applied after reading the pixels. public virtual void ToVector4( Configuration configuration, - ReadOnlySpan sourcePixels, + ReadOnlySpan source, Span destinationVectors, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destinationVectors, nameof(destinationVectors)); + + bool associated = modifiers.IsDefined(PixelConversionModifiers.Premultiply); + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + // Expand straight RGB first because a transfer function applied to associated components would make color depend on alpha. + if (scaled) + { + this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedVector4(configuration, source, destinationVectors); + } - Utils.Vector4Converters.Default.ToVector4(sourcePixels, destinationVectors, modifiers); + Span converted = destinationVectors[..source.Length]; + SRgbCompanding.Expand(converted); + + if (associated) + { + Numerics.Premultiply(converted); + } + + return; + } + + if (scaled) + { + if (associated) + { + this.ToAssociatedScaledVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedScaledVector4(configuration, source, destinationVectors); + } + } + else if (associated) + { + this.ToAssociatedVector4(configuration, source, destinationVectors); + } + else + { + this.ToUnassociatedVector4(configuration, source, destinationVectors); + } } /// /// 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 - /// format. + /// Bulk operation that converts pixels from format to + /// destination pixels. /// - /// The destination pixel type. + /// The source 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); - PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale); - this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale); + ReadOnlySpan s = source.Slice(start, sliceLength); + Span d = destination.Slice(start, sliceLength); + PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } 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); + PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } /// - /// 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); + if (typeof(TPixel) == typeof(TDestinationPixel)) + { + // An identical destination stores the same representation, including associated alpha, so copying preserves every packed component without a lossy representation round trip. + MemoryMarshal.Cast(source).CopyTo(destination); + return; + } + + PixelOperations.Instance.From(configuration, source, destination); } /// @@ -192,7 +374,7 @@ internal virtual void PackFromRgbPlanes( 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,17 +394,17 @@ internal virtual void UnpackIntoRgbPlanes( { GuardUnpackIntoRgbPlanes(redChannel, greenChannel, blueChannel, source); + // TODO: This can be much faster. + // Convert to Rgba32 first using pixel operations then use the R, G, B properties. 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/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs new file mode 100644 index 0000000000..7865b9900e --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +// 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. +// Only the bits per pixel is used by the decoder, we should make it a property of the image metadata. +namespace SixLabors.ImageSharp.PixelFormats; + +/// +/// Contains information about the pixels that make up an images visual data. +/// +/// +/// Initializes a new instance of the struct. +/// +/// Color depth, in number of bits per pixel. +public readonly struct PixelTypeInfo(int bitsPerPixel) +{ + /// + /// Gets color depth, in number of bits per pixel. + /// + public int BitsPerPixel { get; init; } = bitsPerPixel; + + /// + /// 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. + /// + public PixelAlphaRepresentation AlphaRepresentation { get; init; } + + /// + /// Creates a new instance. + /// + /// 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/src/ImageSharp/PixelFormats/README.md b/src/ImageSharp/PixelFormats/README.md index cbebaf23ad..4c7ee545a2 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/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs index 50a68906e7..c25cc301c5 100644 --- a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs +++ b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs @@ -33,7 +33,7 @@ public static class FromRgba32 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -44,7 +44,7 @@ public static void ToArgb32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -55,7 +55,7 @@ public static void ToBgra32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -66,7 +66,7 @@ public static void ToAbgr32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, default); + => SimdUtils.Shuffle4Slice3(source, dest); /// /// Converts a representing a collection of @@ -77,7 +77,7 @@ public static void ToRgb24(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Shuffle4Slice3(source, dest); } /// @@ -96,7 +96,7 @@ public static class FromArgb32 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -107,7 +107,7 @@ public static void ToRgba32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -118,7 +118,7 @@ public static void ToBgra32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -129,7 +129,7 @@ public static void ToAbgr32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0321)); + => SimdUtils.Shuffle4Slice3(source, dest); /// /// Converts a representing a collection of @@ -140,7 +140,7 @@ public static void ToRgb24(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0123)); + => SimdUtils.Shuffle4Slice3(source, dest); } /// @@ -159,7 +159,7 @@ public static class FromBgra32 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -170,7 +170,7 @@ public static void ToArgb32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -181,7 +181,7 @@ public static void ToRgba32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -192,7 +192,7 @@ public static void ToAbgr32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Shuffle4Slice3(source, dest); /// /// Converts a representing a collection of @@ -203,7 +203,7 @@ public static void ToRgb24(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, default); + => SimdUtils.Shuffle4Slice3(source, dest); } /// @@ -222,7 +222,7 @@ public static class FromAbgr32 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -233,7 +233,7 @@ public static void ToArgb32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -244,7 +244,7 @@ public static void ToRgba32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4(source, dest, default); + => SimdUtils.Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -255,7 +255,7 @@ public static void ToBgra32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0123)); + => SimdUtils.Shuffle4Slice3(source, dest); /// /// Converts a representing a collection of @@ -266,7 +266,7 @@ public static void ToRgb24(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0321)); + => SimdUtils.Shuffle4Slice3(source, dest); } /// @@ -285,7 +285,7 @@ public static class FromRgb24 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, default); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -296,7 +296,7 @@ public static void ToRgba32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle2103)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -307,7 +307,7 @@ public static void ToArgb32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -318,7 +318,7 @@ public static void ToBgra32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle0123)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -329,7 +329,7 @@ public static void ToAbgr32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Shuffle3(source, dest); } /// @@ -348,7 +348,7 @@ public static class FromBgr24 /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle0123)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -359,7 +359,7 @@ public static void ToArgb32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -370,7 +370,7 @@ public static void ToRgba32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, default); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -381,7 +381,7 @@ public static void ToBgra32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle2103)); + => SimdUtils.Pad3Shuffle4(source, dest); /// /// Converts a representing a collection of @@ -392,6 +392,6 @@ public static void ToAbgr32(ReadOnlySpan source, Span dest) /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(SimdUtils.Shuffle.MMShuffle3012)); + => SimdUtils.Shuffle3(source, dest); } } diff --git a/src/ImageSharp/PixelFormats/Utils/SignedShort4PixelOperations.cs b/src/ImageSharp/PixelFormats/Utils/SignedShort4PixelOperations.cs new file mode 100644 index 0000000000..4bf8c5e265 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/SignedShort4PixelOperations.cs @@ -0,0 +1,415 @@ +// 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; +using SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats.Utils; + +/// +/// Provides shared SIMD conversion for pixel layouts containing four signed 16-bit components. +/// +internal static class SignedShort4PixelOperations +{ + private const byte RestorePackedPixelOrder = 0b_11_01_10_00; + private const float ShortMaximum = short.MaxValue; + private const float SignedNormalizedMagnitude = ShortMaximum * 2F; + private const float SignedIntegerMinimum = short.MinValue; + private const float SignedIntegerRange = ushort.MaxValue; + + /// + /// Expands signed 16-bit components to native or scaled vectors. + /// + /// The source components. Each consecutive group of four components represents one pixel. + /// The destination vectors. + /// Whether native components use signed-normalized values rather than integer values. + /// Whether to map native components to the scaled range. + internal static void ToVector4(ReadOnlySpan source, Span destination, bool normalized, bool scaled) + { + ref short sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + int index = 0; + + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + + for (; index <= destination.Length - pixelsPerVector; index += pixelsPerVector) + { + // Packed shorts occupy half the expanded width, so one 256-bit load feeds a complete 512-bit conversion. + Vector256 packed = Vector256.LoadUnsafe(ref sourceBase, (nuint)(index * Vector128.Count)); + Vector512 integers = Vector512.Create(Vector256.WidenLower(packed), Vector256.WidenUpper(packed)); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)index)) = ConvertToVector4(Vector512.ConvertToSingle(integers), normalized, scaled); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + + for (; index <= destination.Length - pixelsPerVector; index += pixelsPerVector) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)(index * Vector128.Count)); + Vector256 integers = Vector256.Create(Vector128.WidenLower(packed), Vector128.WidenUpper(packed)); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)index)) = ConvertToVector4(Vector256.ConvertToSingle(integers), normalized, scaled); + } + } + + if (Vector128.IsHardwareAccelerated) + { + ref byte sourceBytes = ref Unsafe.As(ref sourceBase); + + for (; index < destination.Length; index++) + { + ulong packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBytes, (uint)(index * sizeof(ulong)))); + Vector128 integers = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsInt16()); + Unsafe.Add(ref destinationBase, (uint)index) = ConvertToVector4(Vector128.ConvertToSingle(integers), normalized, scaled).AsVector4(); + } + + return; + } + + // The fallback preserves the format implementations' operation order on platforms without vector acceleration. + for (; index < destination.Length; index++) + { + int componentIndex = index * Vector128.Count; + Vector4 vector = new(source[componentIndex], source[componentIndex + 1], source[componentIndex + 2], source[componentIndex + 3]); + + if (normalized) + { + // Both minimum two's-complement SNORM encodings represent -1. + vector = Vector4.Max(vector, new Vector4(-ShortMaximum)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + vector += new Vector4(ShortMaximum); + vector /= SignedNormalizedMagnitude; + } + else + { + vector /= ShortMaximum; + } + } + else if (scaled) + { + // SINT uses every two's-complement code, unlike SNORM where both minimum encodings represent -1. + vector -= new Vector4(SignedIntegerMinimum); + vector /= SignedIntegerRange; + } + + Unsafe.Add(ref destinationBase, (uint)index) = vector; + } + } + + /// + /// Packs native or scaled vectors into signed 16-bit components. + /// + /// The source vectors. + /// The destination components. Each consecutive group of four components represents one pixel. + /// Whether native components use signed-normalized values rather than integer values. + /// Whether the source vectors use the scaled range. + internal static void FromVector4(Span source, Span destination, bool normalized, bool scaled) + { + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + ref short destinationBase = ref MemoryMarshal.GetReference(destination); + int index = 0; + + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + + for (; index <= source.Length - pixelsPerVector; index += pixelsPerVector) + { + Vector512 vectors = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)index)); + Vector512 integers = ConvertToInt32(vectors, normalized, scaled); + + // AVX2 packing operates independently in each 128-bit lane, producing pixels 0, 2, 1, 3. Restore the + // source order with a native lane permutation; the portable PackSignedSaturate fallback is already ordered. + Vector256 packed = Vector256_.PackSignedSaturate(integers.GetLower(), integers.GetUpper()); + + if (Avx2.IsSupported) + { + packed = Avx2.Permute4x64(packed.AsInt64(), RestorePackedPixelOrder).AsInt16(); + } + + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)(index * Vector128.Count))) = packed; + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + + for (; index <= source.Length - pixelsPerVector; index += pixelsPerVector) + { + Vector256 vectors = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)index)); + Vector256 integers = ConvertToInt32(vectors, normalized, scaled); + Vector128 packed = Vector128_.PackSignedSaturate(integers.GetLower(), integers.GetUpper()); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)(index * Vector128.Count))) = packed; + } + } + + if (Vector128.IsHardwareAccelerated) + { + ref byte destinationBytes = ref Unsafe.As(ref destinationBase); + + for (; index < source.Length; index++) + { + Vector128 vector = Unsafe.As>(ref Unsafe.Add(ref sourceBase, (uint)index)); + Vector128 integers = ConvertToInt32(vector, normalized, scaled); + Vector128 packed = Vector128_.PackSignedSaturate(integers, integers); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destinationBytes, (uint)(index * sizeof(ulong))), packed.AsUInt64().GetElement(0)); + } + + return; + } + + for (; index < source.Length; index++) + { + Vector4 vector = Unsafe.Add(ref sourceBase, (uint)index); + + if (normalized) + { + if (scaled) + { + vector *= 2F; + vector -= Vector4.One; + } + + vector *= ShortMaximum; + vector = Numerics.Clamp(vector, new Vector4(-ShortMaximum), new Vector4(ShortMaximum)); + } + else + { + if (scaled) + { + vector *= SignedIntegerRange; + vector += new Vector4(SignedIntegerMinimum); + } + + vector = Numerics.Clamp(vector, new Vector4(short.MinValue), new Vector4(short.MaxValue)); + } + + int componentIndex = index * Vector128.Count; + destination[componentIndex] = (short)MathF.Round(vector.X); + destination[componentIndex + 1] = (short)MathF.Round(vector.Y); + destination[componentIndex + 2] = (short)MathF.Round(vector.Z); + destination[componentIndex + 3] = (short)MathF.Round(vector.W); + } + } + + /// + /// Converts signed integer components to native or scaled vectors while preserving the scalar conversion order. + /// + /// The signed integer components. + /// Whether native components use signed-normalized values. + /// Whether to map native components to the scaled range. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ConvertToVector4(Vector512 source, bool normalized, bool scaled) + { + if (normalized) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector512.Max(source, Vector512.Create(-ShortMaximum)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + source += Vector512.Create(ShortMaximum); + source /= Vector512.Create(SignedNormalizedMagnitude); + } + else + { + source /= Vector512.Create(ShortMaximum); + } + } + else if (scaled) + { + source -= Vector512.Create(SignedIntegerMinimum); + source /= Vector512.Create(SignedIntegerRange); + } + + return source; + } + + /// + /// Converts signed integer components to native or scaled vectors while preserving the scalar conversion order. + /// + /// The signed integer components. + /// Whether native components use signed-normalized values. + /// Whether to map native components to the scaled range. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertToVector4(Vector256 source, bool normalized, bool scaled) + { + if (normalized) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector256.Max(source, Vector256.Create(-ShortMaximum)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + source += Vector256.Create(ShortMaximum); + source /= Vector256.Create(SignedNormalizedMagnitude); + } + else + { + source /= Vector256.Create(ShortMaximum); + } + } + else if (scaled) + { + source -= Vector256.Create(SignedIntegerMinimum); + source /= Vector256.Create(SignedIntegerRange); + } + + return source; + } + + /// + /// Converts signed integer components to native or scaled vectors while preserving the scalar conversion order. + /// + /// The signed integer components. + /// Whether native components use signed-normalized values. + /// Whether to map native components to the scaled range. + /// The converted vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ConvertToVector4(Vector128 source, bool normalized, bool scaled) + { + if (normalized) + { + // Both minimum two's-complement SNORM encodings represent -1. + source = Vector128.Max(source, Vector128.Create(-ShortMaximum)); + + if (scaled) + { + // Offset exact integer components before division to avoid cancellation near the signed-normalized lower bound. + source += Vector128.Create(ShortMaximum); + source /= Vector128.Create(SignedNormalizedMagnitude); + } + else + { + source /= Vector128.Create(ShortMaximum); + } + } + else if (scaled) + { + source -= Vector128.Create(SignedIntegerMinimum); + source /= Vector128.Create(SignedIntegerRange); + } + + return source; + } + + /// + /// Converts vectors to signed integer components using the format's packing contract. + /// + /// The source vectors. + /// Whether native components use signed-normalized values. + /// Whether the source vectors use the scaled range. + /// The converted integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ConvertToInt32(Vector512 source, bool normalized, bool scaled) + { + if (normalized) + { + if (scaled) + { + source *= Vector512.Create(2F); + source -= Vector512.One; + } + + source *= Vector512.Create(ShortMaximum); + source = Vector512.Min(Vector512.Max(source, Vector512.Create(-ShortMaximum)), Vector512.Create(ShortMaximum)); + } + else + { + if (scaled) + { + source *= Vector512.Create(SignedIntegerRange); + source += Vector512.Create(SignedIntegerMinimum); + } + + source = Vector512.Min(Vector512.Max(source, Vector512.Create((float)short.MinValue)), Vector512.Create((float)short.MaxValue)); + } + + return Vector512_.ConvertToInt32RoundToEven(source); + } + + /// + /// Converts vectors to signed integer components using the format's packing contract. + /// + /// The source vectors. + /// Whether native components use signed-normalized values. + /// Whether the source vectors use the scaled range. + /// The converted integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ConvertToInt32(Vector256 source, bool normalized, bool scaled) + { + if (normalized) + { + if (scaled) + { + source *= Vector256.Create(2F); + source -= Vector256.One; + } + + source *= Vector256.Create(ShortMaximum); + source = Vector256.Min(Vector256.Max(source, Vector256.Create(-ShortMaximum)), Vector256.Create(ShortMaximum)); + } + else + { + if (scaled) + { + source *= Vector256.Create(SignedIntegerRange); + source += Vector256.Create(SignedIntegerMinimum); + } + + source = Vector256.Min(Vector256.Max(source, Vector256.Create((float)short.MinValue)), Vector256.Create((float)short.MaxValue)); + } + + return Vector256_.ConvertToInt32RoundToEven(source); + } + + /// + /// Converts vectors to signed integer components using the format's packing contract. + /// + /// The source vectors. + /// Whether native components use signed-normalized values. + /// Whether the source vectors use the scaled range. + /// The converted integer components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ConvertToInt32(Vector128 source, bool normalized, bool scaled) + { + if (normalized) + { + if (scaled) + { + source *= Vector128.Create(2F); + source -= Vector128.One; + } + + source *= Vector128.Create(ShortMaximum); + source = Vector128.Min(Vector128.Max(source, Vector128.Create(-ShortMaximum)), Vector128.Create(ShortMaximum)); + } + else + { + if (scaled) + { + source *= Vector128.Create(SignedIntegerRange); + source += Vector128.Create(SignedIntegerMinimum); + } + + source = Vector128.Min(Vector128.Max(source, Vector128.Create((float)short.MinValue)), Vector128.Create((float)short.MaxValue)); + } + + return Vector128_.ConvertToInt32RoundToEven(source); + } +} diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Affine.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Affine.cs new file mode 100644 index 0000000000..637071421e --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Affine.cs @@ -0,0 +1,96 @@ +// 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; + +namespace SixLabors.ImageSharp.PixelFormats.Utils; + +internal static partial class Vector4Converters +{ + /// + /// Multiplies each vector component and then adds the corresponding offset component. + /// + /// The vectors to transform in place. + /// The component-wise multiplier. + /// The component-wise offset applied after multiplication. + public static void MultiplyThenAdd(Span vectors, Vector4 multiplier, Vector4 offset) + => Apply(vectors, new MultiplyThenAddOperator(multiplier, offset)); + + /// + /// Adds the corresponding offset component and then divides each vector component by its divisor. + /// + /// The vectors to transform in place. + /// The component-wise offset applied before division. + /// The component-wise divisor. + public static void AddThenDivide(Span vectors, Vector4 offset, Vector4 divisor) + => Apply(vectors, new AddThenDivideOperator(offset, divisor)); + + /// + /// Applies a stateful component transform to a vector buffer in place. + /// + /// The transform selected for this closed traversal. + /// The vectors to transform. + /// The transform and its component-wise state. + // Closing and inlining the traversal lets the JIT devirtualize every Invoke call, + // specialize the active hardware-width branches, and discard unused operator state. + [MethodImpl(InliningOptions.AlwaysInline)] + private static void Apply(Span vectors, TOperator transform) + where TOperator : struct, IStatefulVector4Operator + { + ref Vector4 vectorBase = ref MemoryMarshal.GetReference(vectors); + int index = 0; + + // A Vector4 is one complete pixel. Descending register widths therefore consume groups + // of four, two, and one pixels without splitting a pixel across traversal boundaries. + if (Vector512.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector512.Count / Vector128.Count; + int oneRegisterFromEnd = vectors.Length - vectorsPerRegister; + + for (; index <= oneRegisterFromEnd; index += vectorsPerRegister) + { + ref Vector512 vector = ref Unsafe.As>(ref Unsafe.Add(ref vectorBase, (uint)index)); + + vector = transform.Invoke(vector); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int vectorsPerRegister = Vector256.Count / Vector128.Count; + int oneRegisterFromEnd = vectors.Length - vectorsPerRegister; + + for (; index <= oneRegisterFromEnd; index += vectorsPerRegister) + { + ref Vector256 vector = ref Unsafe.As>(ref Unsafe.Add(ref vectorBase, (uint)index)); + + vector = transform.Invoke(vector); + } + } + + if (Vector128.IsHardwareAccelerated) + { + // Vector128 and Vector4 have the same four-lane layout, so this stage + // consumes every remaining complete pixel and leaves no scalar remainder. + for (; index < vectors.Length; index++) + { + ref Vector128 vector = ref Unsafe.As>(ref Unsafe.Add(ref vectorBase, (uint)index)); + + vector = transform.Invoke(vector); + } + + return; + } + + // Vector4 retains the same component order and expression ordering when hardware + // intrinsics are unavailable, preserving the format-specific conversion contract. + for (; index < vectors.Length; index++) + { + ref Vector4 vector = ref Unsafe.Add(ref vectorBase, (uint)index); + vector = transform.Invoke(vector); + } + } +} diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AffineOperators.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AffineOperators.cs new file mode 100644 index 0000000000..32d6521dff --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AffineOperators.cs @@ -0,0 +1,142 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.PixelFormats.Utils; + +internal static partial class Vector4Converters +{ + /// + /// Defines a stateful component transform for each register width used by the shared traversal. + /// + private interface IStatefulVector4Operator + { + /// + /// Transforms one pixel represented by four components. + /// + /// The source components. + /// The transformed components. + public Vector4 Invoke(Vector4 source); + + /// + /// Transforms one pixel represented by the four single-precision lanes in a 128-bit register. + /// + /// The source components. + /// The transformed components. + public Vector128 Invoke(Vector128 source); + + /// + /// Transforms two pixels represented by the eight single-precision lanes in a 256-bit register. + /// + /// The source components. + /// The transformed components. + public Vector256 Invoke(Vector256 source); + + /// + /// Transforms four pixels represented by the sixteen single-precision lanes in a 512-bit register. + /// + /// The source components. + /// The transformed components. + public Vector512 Invoke(Vector512 source); + } + + /// + /// Carries the component state for a multiply-then-add transform. + /// + private readonly struct MultiplyThenAddOperator : IStatefulVector4Operator + { + private readonly Vector512 multiplier; + private readonly Vector512 offset; + + /// + /// Initializes a new instance of the struct. + /// + /// The component-wise multiplier. + /// The component-wise offset applied after multiplication. + public MultiplyThenAddOperator(Vector4 multiplier, Vector4 offset) + { + Vector128 multiplier128 = multiplier.AsVector128(); + Vector128 offset128 = offset.AsVector128(); + Vector256 multiplier256 = Vector256.Create(multiplier128, multiplier128); + Vector256 offset256 = Vector256.Create(offset128, offset128); + + // Expanding the invariant state once prevents the width-specific Invoke methods + // from rebuilding identical lane groups for every vector processed by the loop. + this.multiplier = Vector512.Create(multiplier256, multiplier256); + this.offset = Vector512.Create(offset256, offset256); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Invoke(Vector4 source) => this.Invoke(source.AsVector128()).AsVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 Invoke(Vector128 source) + => (source * this.multiplier.GetLower().GetLower()) + this.offset.GetLower().GetLower(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector256 Invoke(Vector256 source) + => (source * this.multiplier.GetLower()) + this.offset.GetLower(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector512 Invoke(Vector512 source) + => (source * this.multiplier) + this.offset; + } + + /// + /// Carries the component state for an add-then-divide transform. + /// + private readonly struct AddThenDivideOperator : IStatefulVector4Operator + { + private readonly Vector512 offset; + private readonly Vector512 divisor; + + /// + /// Initializes a new instance of the struct. + /// + /// The component-wise offset applied before division. + /// The component-wise divisor. + public AddThenDivideOperator(Vector4 offset, Vector4 divisor) + { + Vector128 offset128 = offset.AsVector128(); + Vector128 divisor128 = divisor.AsVector128(); + Vector256 offset256 = Vector256.Create(offset128, offset128); + Vector256 divisor256 = Vector256.Create(divisor128, divisor128); + + // All register widths consume prefixes of this repeated four-pixel state, + // so one construction serves the wide loop and every narrower remainder. + this.offset = Vector512.Create(offset256, offset256); + this.divisor = Vector512.Create(divisor256, divisor256); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 Invoke(Vector4 source) + { + Vector128 result = (source.AsVector128() + this.offset.GetLower().GetLower()) / this.divisor.GetLower().GetLower(); + + return result.AsVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 Invoke(Vector128 source) + => (source + this.offset.GetLower().GetLower()) / this.divisor.GetLower().GetLower(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector256 Invoke(Vector256 source) + => (source + this.offset.GetLower()) / this.divisor.GetLower(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector512 Invoke(Vector512 source) + => (source + this.offset) / this.divisor; + } +} diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs new file mode 100644 index 0000000000..b47779df8d --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.AssociatedRgbaCompatible.cs @@ -0,0 +1,892 @@ +// 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 SixLabors.ImageSharp.Common.Helpers; + +namespace SixLabors.ImageSharp.PixelFormats.Utils; + +/// +/// Contains . +/// +internal static partial class Vector4Converters +{ + /// + /// Provides efficient batched conversion for four-byte pixel types that store premultiplied alpha. + /// + public static class AssociatedRgbaCompatible + { + /// + /// Converts associated RGBA byte components to an unassociated scaled vector. + /// + /// The associated red component. + /// The associated green component. + /// The associated blue component. + /// The alpha component. + /// The unassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 ToUnassociatedVector4(byte red, byte green, byte blue, byte alpha) + { + // Divide the original byte magnitudes before normalization so a floating-point intermediate cannot move an exact byte conversion across its rounding midpoint. + Vector4 vector = new(red, green, blue, alpha); + float colorDivisor = alpha == 0 ? byte.MaxValue : alpha; + return vector / new Vector4(colorDivisor, colorDivisor, colorDivisor, byte.MaxValue); + } + + /// + /// Converts packed RGBA byte magnitudes to unassociated scaled vectors. + /// + /// The packed RGBA bytes. + /// The destination vectors. + private static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + ref byte sourceBase = ref MemoryMarshal.GetReference(source); + ref Vector4 destinationBase = ref MemoryMarshal.GetReference(destination); + + // Each packed component occupies one byte, so the destination Vector4 lane count also defines the source byte stride. + int componentsPerPixel = Vector128.Count; + int i = 0; + + // Portable widening advances one integer width at a time, so assemble the wider vectors from ordered 128-bit halves. + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / componentsPerPixel; + + for (; i <= destination.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i * componentsPerPixel)); + Vector128 lowerShorts = Vector128.WidenLower(packed); + Vector128 upperShorts = Vector128.WidenUpper(packed); + Vector256 lowerIntegers = Vector256.Create(Vector128.WidenLower(lowerShorts), Vector128.WidenUpper(lowerShorts)); + Vector256 upperIntegers = Vector256.Create(Vector128.WidenLower(upperShorts), Vector128.WidenUpper(upperShorts)); + Vector512 integers = Vector512.Create(lowerIntegers, upperIntegers).AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToUnassociatedVector4(Vector512.ConvertToSingle(integers)); + } + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / componentsPerPixel; + + for (; i <= destination.Length - pixelsPerVector; i += pixelsPerVector) + { + ulong packed = Unsafe.ReadUnaligned(ref Unsafe.Add(ref sourceBase, (uint)(i * componentsPerPixel))); + Vector128 shorts = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); + Vector256 integers = Vector256.Create(Vector128.WidenLower(shorts), Vector128.WidenUpper(shorts)).AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref destinationBase, (uint)i)) = ToUnassociatedVector4(Vector256.ConvertToSingle(integers)); + } + } + + if (Vector128.IsHardwareAccelerated) + { + int pixelsPerVector = Vector128.Count / componentsPerPixel; + + for (; i <= destination.Length - pixelsPerVector; i += pixelsPerVector) + { + Vector128 packed = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i * componentsPerPixel)); + (Vector128 shorts0, Vector128 shorts1) = Vector128.Widen(packed); + (Vector128 integers0, Vector128 integers1) = Vector128.Widen(shorts0); + (Vector128 integers2, Vector128 integers3) = Vector128.Widen(shorts1); + + Unsafe.Add(ref destinationBase, (uint)i) = ToUnassociatedVector4(Vector128.ConvertToSingle(integers0.AsInt32())).AsVector4(); + Unsafe.Add(ref destinationBase, (uint)i + 1u) = ToUnassociatedVector4(Vector128.ConvertToSingle(integers1.AsInt32())).AsVector4(); + Unsafe.Add(ref destinationBase, (uint)i + 2u) = ToUnassociatedVector4(Vector128.ConvertToSingle(integers2.AsInt32())).AsVector4(); + Unsafe.Add(ref destinationBase, (uint)i + 3u) = ToUnassociatedVector4(Vector128.ConvertToSingle(integers3.AsInt32())).AsVector4(); + } + } + + for (; i < destination.Length; i++) + { + int offset = i * componentsPerPixel; + + Unsafe.Add(ref destinationBase, (uint)i) = ToUnassociatedVector4( + Unsafe.Add(ref sourceBase, (uint)offset), + Unsafe.Add(ref sourceBase, (uint)offset + 1u), + Unsafe.Add(ref sourceBase, (uint)offset + 2u), + Unsafe.Add(ref sourceBase, (uint)offset + 3u)); + } + } + + /// + /// Converts four associated RGBA byte-magnitude vectors to unassociated scaled vectors. + /// + /// The associated byte-magnitude vectors. + /// The unassociated scaled vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ToUnassociatedVector4(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 byteMax = Vector512.Create((float)byte.MaxValue); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + + // Zero-alpha storage has no alpha divisor, while W always uses the byte normalization divisor. + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + Vector512 normalizeMask = Vector512.Equals(alpha, zero) | alphaMask; + Vector512 divisor = Vector512.ConditionalSelect(normalizeMask, byteMax, alpha); + return source / divisor; + } + + /// + /// Converts two associated RGBA byte-magnitude vectors to unassociated scaled vectors. + /// + /// The associated byte-magnitude vectors. + /// The unassociated scaled vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ToUnassociatedVector4(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 byteMax = Vector256.Create((float)byte.MaxValue); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + + // Zero-alpha storage has no alpha divisor, while W always uses the byte normalization divisor. + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + Vector256 normalizeMask = Vector256.Equals(alpha, zero) | alphaMask; + Vector256 divisor = Vector256.ConditionalSelect(normalizeMask, byteMax, alpha); + return source / divisor; + } + + /// + /// Converts an associated RGBA byte-magnitude vector to an unassociated scaled vector. + /// + /// The associated byte-magnitude vector. + /// The unassociated scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ToUnassociatedVector4(Vector128 source) + { + Vector128 zero = Vector128.Zero; + Vector128 byteMax = Vector128.Create((float)byte.MaxValue); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + + // Zero-alpha storage has no alpha divisor, while W always uses the byte normalization divisor. + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + Vector128 normalizeMask = Vector128.Equals(alpha, zero) | alphaMask; + Vector128 divisor = Vector128.ConditionalSelect(normalizeMask, byteMax, alpha); + return source / divisor; + } + + /// + /// Converts unassociated scaled vectors to associated byte magnitudes. + /// + /// The vectors to convert in place. + private static void AssociateToByte(Span source) + { + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + ref Vector512 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = AssociateToByte(sourceBase); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = AssociateToByte(sourceBase); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector128.IsHardwareAccelerated) + { + ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)source.Length); + + while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) + { + sourceBase = AssociateToByte(sourceBase); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + return; + } + + ref Vector4 tailBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref tailBase, i) = AssociateToByte(Unsafe.Add(ref tailBase, i)); + } + } + + /// + /// Converts four unassociated scaled vectors to associated byte magnitudes. + /// + /// The unassociated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 AssociateToByte(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 one = Vector512.One; + Vector512 byteMax = Vector512.Create((float)byte.MaxValue); + source = Vector512.Min(Vector512.Max(source, zero), one); + Vector512 alpha = Vector512_.ShuffleNative(source, 0b_11_11_11_11); + Vector512 storedAlpha = Vector512.Floor((alpha * byteMax) + Vector512.Create(.5F)); + Vector512 result = source * storedAlpha; + + // RGB is associated using the quantized alpha byte, while W stores that byte directly. + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Converts two unassociated scaled vectors to associated byte magnitudes. + /// + /// The unassociated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 AssociateToByte(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 one = Vector256.One; + Vector256 byteMax = Vector256.Create((float)byte.MaxValue); + source = Vector256.Min(Vector256.Max(source, zero), one); + Vector256 alpha = Vector256_.ShuffleNative(source, 0b_11_11_11_11); + Vector256 storedAlpha = Vector256.Floor((alpha * byteMax) + Vector256.Create(.5F)); + Vector256 result = source * storedAlpha; + + // RGB is associated using the quantized alpha byte, while W stores that byte directly. + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + return Vector256.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Converts an unassociated scaled vector to associated byte magnitudes. + /// + /// The unassociated scaled vector. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 AssociateToByte(Vector128 source) + { + Vector128 zero = Vector128.Zero; + Vector128 one = Vector128.One; + Vector128 byteMax = Vector128.Create((float)byte.MaxValue); + source = Vector128.Min(Vector128.Max(source, zero), one); + Vector128 alpha = Vector128_.ShuffleNative(source, 0b_11_11_11_11); + Vector128 storedAlpha = Vector128.Floor((alpha * byteMax) + Vector128.Create(.5F)); + Vector128 result = source * storedAlpha; + + // RGB is associated using the quantized alpha byte, while W stores that byte directly. + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + return Vector128.ConditionalSelect(alphaMask, storedAlpha, result); + } + + /// + /// Converts an unassociated scaled vector to associated byte magnitudes. + /// + /// The unassociated scaled vector. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 AssociateToByte(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + float storedAlpha = (byte)Numerics.Clamp((source.W * byte.MaxValue) + 0.5F, 0, byte.MaxValue); + + source *= storedAlpha; + source.W = storedAlpha; + return source; + } + + /// + /// Converts an unassociated scaled vector to an pixel. + /// + /// The unassociated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Rgba32P FromUnassociatedVector4ToRgba32P(Vector4 source) + { + source = AssociateToByte(source); + return new Rgba32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an unassociated scaled vector to a pixel. + /// + /// The unassociated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Bgra32P FromUnassociatedVector4ToBgra32P(Vector4 source) + { + source = AssociateToByte(source); + return new Bgra32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an unassociated scaled vector to an pixel. + /// + /// The unassociated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Argb32P FromUnassociatedVector4ToArgb32P(Vector4 source) + { + source = AssociateToByte(source); + return new Argb32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an unassociated scaled vector to an pixel. + /// + /// The unassociated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Abgr32P FromUnassociatedVector4ToAbgr32P(Vector4 source) + { + source = AssociateToByte(source); + return new Abgr32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to associated byte magnitudes using the alpha byte the destination stores. + /// + /// The associated scaled vector. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 ReassociateToByte(Vector4 source) + { + float alpha = source.W; + + if (alpha <= 0) + { + return Vector4.Zero; + } + + float byteAlpha = alpha * byte.MaxValue; + float storedAlpha = (byte)Numerics.Clamp(byteAlpha + 0.5F, 0, byte.MaxValue); + + if (byteAlpha == storedAlpha) + { + // Quantization leaves alpha unchanged, so RGB is already associated correctly. Avoiding division preserves exact byte midpoints produced by scaling stored components. + source *= byte.MaxValue; + } + else + { + // Recover straight RGB before multiplying by the stored alpha byte. Keeping the association in byte magnitude preserves exact half-byte rounding boundaries. + Numerics.UnPremultiply(ref source); + source *= storedAlpha; + } + + source.W = storedAlpha; + Numerics.ClampRgbToAlpha(ref source); + return source; + } + + /// + /// Converts associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The vectors to convert in place. + private static void ReassociateToByte(Span source) + { + if (Vector512.IsHardwareAccelerated) + { + int pixelsPerVector = Vector512.Count / Vector128.Count; + ref Vector512 vectorSourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector512 sourceEnd = ref Unsafe.Add(ref vectorSourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref vectorSourceBase, ref sourceEnd)) + { + vectorSourceBase = ReassociateToByte(vectorSourceBase); + vectorSourceBase = ref Unsafe.Add(ref vectorSourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector256.IsHardwareAccelerated) + { + int pixelsPerVector = Vector256.Count / Vector128.Count; + ref Vector256 vectorSourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector256 sourceEnd = ref Unsafe.Add(ref vectorSourceBase, (uint)source.Length / (uint)pixelsPerVector); + + while (Unsafe.IsAddressLessThan(ref vectorSourceBase, ref sourceEnd)) + { + vectorSourceBase = ReassociateToByte(vectorSourceBase); + vectorSourceBase = ref Unsafe.Add(ref vectorSourceBase, 1); + } + + // SIMD widths are powers of two, so masking finds the start of the unprocessed remainder without division. + source = source[(source.Length & ~(pixelsPerVector - 1))..]; + } + + if (Vector128.IsHardwareAccelerated) + { + ref Vector128 vectorSourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector128 sourceEnd = ref Unsafe.Add(ref vectorSourceBase, (uint)source.Length); + + while (Unsafe.IsAddressLessThan(ref vectorSourceBase, ref sourceEnd)) + { + vectorSourceBase = ReassociateToByte(vectorSourceBase); + vectorSourceBase = ref Unsafe.Add(ref vectorSourceBase, 1); + } + + return; + } + + ref Vector4 sourceBase = ref MemoryMarshal.GetReference(source); + + for (nuint i = 0; i < (uint)source.Length; i++) + { + Unsafe.Add(ref sourceBase, i) = ReassociateToByte(Unsafe.Add(ref sourceBase, i)); + } + } + + /// + /// Converts four associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The associated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 ReassociateToByte(Vector512 source) + { + Vector512 zero = Vector512.Zero; + Vector512 byteMax = Vector512.Create((float)byte.MaxValue); + Vector512 alpha = Vector512.Max(Vector512_.ShuffleNative(source, 0b_11_11_11_11), zero); + Vector512 byteAlpha = alpha * byteMax; + Vector512 storedAlpha = Vector512.Floor(Vector512.Min(Vector512.Max(byteAlpha + Vector512.Create(.5F), zero), byteMax)); + Vector512 result = (source / alpha) * storedAlpha; + + // Exact byte alpha values need no reassociation. Multiplying by 255 directly preserves RGB values that already lie on byte midpoints. + result = Vector512.ConditionalSelect(Vector512.Equals(byteAlpha, storedAlpha), source * byteMax, result); + Vector512 alphaMask = Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector512.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector512.Min(Vector512.Max(result, zero), storedAlpha); + return Vector512.ConditionalSelect(Vector512.Equals(alpha, zero), zero, result); + } + + /// + /// Converts two associated scaled vectors to associated byte magnitudes using the alpha bytes the destination stores. + /// + /// The associated scaled vectors. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ReassociateToByte(Vector256 source) + { + Vector256 zero = Vector256.Zero; + Vector256 byteMax = Vector256.Create((float)byte.MaxValue); + Vector256 alpha = Vector256.Max(Vector256_.ShuffleNative(source, 0b_11_11_11_11), zero); + Vector256 byteAlpha = alpha * byteMax; + Vector256 storedAlpha = Vector256.Floor(Vector256.Min(Vector256.Max(byteAlpha + Vector256.Create(.5F), zero), byteMax)); + Vector256 result = (source / alpha) * storedAlpha; + + // Exact byte alpha values need no reassociation. Multiplying by 255 directly preserves RGB values that already lie on byte midpoints. + result = Vector256.ConditionalSelect(Vector256.Equals(byteAlpha, storedAlpha), source * byteMax, result); + Vector256 alphaMask = Vector256.Create(0, 0, 0, -1, 0, 0, 0, -1).AsSingle(); + result = Vector256.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector256.Min(Vector256.Max(result, zero), storedAlpha); + return Vector256.ConditionalSelect(Vector256.Equals(alpha, zero), zero, result); + } + + /// + /// Converts an associated scaled vector to associated byte magnitudes using the alpha byte the destination stores. + /// + /// The associated scaled vector. + /// The associated byte magnitudes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ReassociateToByte(Vector128 source) + { + Vector128 zero = Vector128.Zero; + Vector128 byteMax = Vector128.Create((float)byte.MaxValue); + Vector128 alpha = Vector128.Max(Vector128_.ShuffleNative(source, 0b_11_11_11_11), zero); + Vector128 byteAlpha = alpha * byteMax; + Vector128 storedAlpha = Vector128.Floor(Vector128.Min(Vector128.Max(byteAlpha + Vector128.Create(.5F), zero), byteMax)); + Vector128 result = (source / alpha) * storedAlpha; + + // Exact byte alpha values need no reassociation. Multiplying by 255 directly preserves RGB values that already lie on byte midpoints. + result = Vector128.ConditionalSelect(Vector128.Equals(byteAlpha, storedAlpha), source * byteMax, result); + Vector128 alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle(); + result = Vector128.ConditionalSelect(alphaMask, storedAlpha, result); + result = Vector128.Min(Vector128.Max(result, zero), storedAlpha); + return Vector128.ConditionalSelect(Vector128.Equals(alpha, zero), zero, result); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Rgba32P FromAssociatedVector4ToRgba32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Rgba32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to a pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Bgra32P FromAssociatedVector4ToBgra32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Bgra32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Argb32P FromAssociatedVector4ToArgb32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Argb32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts an associated scaled vector to an pixel. + /// + /// The associated scaled vector. + /// The associated pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Abgr32P FromAssociatedVector4ToAbgr32P(Vector4 source) + { + source = ReassociateToByte(source); + return new Abgr32P(ConvertToByte(source.X), ConvertToByte(source.Y), ConvertToByte(source.Z), ConvertToByte(source.W)); + } + + /// + /// Converts associated RGBA pixels to associated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToAssociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + } + + /// + /// Converts premultiplied RGBA pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + ToUnassociatedVector4(MemoryMarshal.Cast(source), destination[..source.Length]); + } + + /// + /// Converts unassociated scaled vectors to premultiplied RGBA pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromUnassociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + AssociateToByte(source); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + } + + /// + /// Converts associated scaled vectors to premultiplied RGBA pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), MemoryMarshal.Cast(destination)); + } + + /// + /// Converts associated BGRA pixels to associated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToAssociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed BGRA bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromBgra32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + } + + /// + /// Converts premultiplied BGRA pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // Stage all but the final pixel as RGBA bytes in the unwritten end of the expanding Vector4 destination. + // The final expanded vector overlaps that staging region, so convert it directly after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromBgra32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + ToUnassociatedVector4(MemoryMarshal.Cast(temporary), destination[..lastIndex]); + destination[lastIndex] = ToUnassociatedVector4(source[lastIndex].R, source[lastIndex].G, source[lastIndex].B, source[lastIndex].A); + } + + /// + /// Converts unassociated scaled vectors to premultiplied BGRA pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromUnassociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + AssociateToByte(source); + + // FloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied BGRA pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToBgra32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated ARGB pixels to associated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToAssociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed ARGB bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromArgb32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + } + + /// + /// Converts premultiplied ARGB pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // Stage all but the final pixel as RGBA bytes in the unwritten end of the expanding Vector4 destination. + // The final expanded vector overlaps that staging region, so convert it directly after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromArgb32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + ToUnassociatedVector4(MemoryMarshal.Cast(temporary), destination[..lastIndex]); + destination[lastIndex] = ToUnassociatedVector4(source[lastIndex].R, source[lastIndex].G, source[lastIndex].B, source[lastIndex].A); + } + + /// + /// Converts unassociated scaled vectors to premultiplied ARGB pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromUnassociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + AssociateToByte(source); + + // FloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied ARGB pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToArgb32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated ABGR pixels to associated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ToAssociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // ByteToNormalizedFloat preserves component order, so the packed ABGR bytes must first be shuffled to RGBA. + // Reuse the unwritten end of the larger Vector4 destination as staging to avoid a temporary allocation. + // The final vector overlaps that staging, so its source pixel is converted after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromAbgr32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(temporary), MemoryMarshal.Cast(destination[..lastIndex])); + destination[lastIndex] = source[lastIndex].ToVector4(); + } + + /// + /// Converts premultiplied ABGR pixels to unassociated scaled vectors. + /// + /// The source pixels. + /// The destination vectors. + internal static void ToUnassociatedVector4(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + if (source.IsEmpty) + { + return; + } + + // Stage all but the final pixel as RGBA bytes in the unwritten end of the expanding Vector4 destination. + // The final expanded vector overlaps that staging region, so convert it directly after the staged bytes are consumed. + int lastIndex = source.Length - 1; + Span temporary = MemoryMarshal.Cast(destination).Slice((3 * source.Length) + 1, lastIndex); + PixelConverter.FromAbgr32.ToRgba32(MemoryMarshal.Cast(source[..lastIndex]), MemoryMarshal.Cast(temporary)); + ToUnassociatedVector4(MemoryMarshal.Cast(temporary), destination[..lastIndex]); + destination[lastIndex] = ToUnassociatedVector4(source[lastIndex].R, source[lastIndex].G, source[lastIndex].B, source[lastIndex].A); + } + + /// + /// Converts unassociated scaled vectors to premultiplied ABGR pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromUnassociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + AssociateToByte(source); + + // FloatToByteSaturate emits RGBA bytes, which can be shuffled in place to avoid a temporary pixel buffer. + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); + } + + /// + /// Converts associated scaled vectors to premultiplied ABGR pixels. + /// + /// The source vectors, modified in place during conversion. + /// The destination pixels. + internal static void FromAssociatedVector4(Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + ReassociateToByte(source); + + Span destinationBytes = MemoryMarshal.Cast(destination); + SimdUtils.FloatToByteSaturate(MemoryMarshal.Cast(source), destinationBytes); + PixelConverter.FromRgba32.ToAbgr32(destinationBytes, destinationBytes); + } + + /// + /// Converts a floating-point byte magnitude to a byte. + /// + /// The byte magnitude. + /// The rounded and clamped byte. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte ConvertToByte(float value) => (byte)Numerics.Clamp(value + .5F, 0, byte.MaxValue); + } +} diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 4d07a8a9b6..05799961d8 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -21,139 +21,322 @@ public static class Default { [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); + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); - if (modifiers.IsDefined(PixelConversionModifiers.Scale)) + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) { - UnsafeFromScaledVector4Core(sourceVectors, destPixels); + // Transfer functions consume straight color, so preserve the established modifier order before selecting a scalar storage conversion. + ApplyBackwardConversionModifiers(source, modifiers); + + if (scaled) + { + UnsafeFromUnassociatedScaledVector4Core(source, destination); + } + else + { + UnsafeFromUnassociatedVector4Core(source, destination); + } + + return; + } + + if (scaled) + { + if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) + { + UnsafeFromAssociatedScaledVector4Core(source, destination); + } + else + { + UnsafeFromUnassociatedScaledVector4Core(source, destination); + } + } + else if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) + { + UnsafeFromAssociatedVector4Core(source, destination); } else { - UnsafeFromVector4Core(sourceVectors, destPixels); + UnsafeFromUnassociatedVector4Core(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)) + // Conversion length is defined by the source. Limiting the destination here also keeps modifiers from + // changing spare capacity supplied by callers of either the checked or unchecked conversion path. + destination = destination[..source.Length]; + + bool scaled = modifiers.IsDefined(PixelConversionModifiers.Scale); + + if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) + { + if (scaled) + { + UnsafeToUnassociatedScaledVector4Core(source, destination); + } + else + { + UnsafeToUnassociatedVector4Core(source, destination); + } + + // Transfer functions consume straight color; association is applied only after expansion. + ApplyForwardConversionModifiers(destination, modifiers); + return; + } + + if (scaled) + { + if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) + { + UnsafeToAssociatedScaledVector4Core(source, destination); + } + else + { + UnsafeToUnassociatedScaledVector4Core(source, destination); + } + } + else if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) { - UnsafeToScaledVector4Core(sourcePixels, destVectors); + UnsafeToAssociatedVector4Core(source, destination); } else { - UnsafeToVector4Core(sourcePixels, destVectors); + UnsafeToUnassociatedVector4Core(source, destination); + } + } + + /// + /// Converts unassociated native-range vectors to pixels without checking span lengths. + /// + /// The destination pixel format. + /// The source vectors. + /// The destination pixels. Its length must be at least length. + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnsafeFromUnassociatedVector4Core( + ReadOnlySpan source, + Span destination) + where TPixel : unmanaged, IPixel + { + 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)) + { + destinationBase = TPixel.FromUnassociatedVector4(sourceStart); + + sourceStart = ref Unsafe.Add(ref sourceStart, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + } + } + + /// + /// Converts associated native-range vectors to pixels without checking span lengths. + /// + /// The destination pixel format. + /// The source vectors. + /// The destination pixels. Its length must be at least length. + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnsafeFromAssociatedVector4Core( + ReadOnlySpan source, + Span destination) + where TPixel : unmanaged, IPixel + { + 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)) + { + destinationBase = TPixel.FromAssociatedVector4(sourceStart); + + sourceStart = ref Unsafe.Add(ref sourceStart, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } + } - ApplyForwardConversionModifiers(destVectors, modifiers); + /// + /// Converts pixels to unassociated native-range vectors without checking span lengths. + /// + /// The source pixel format. + /// The source pixels. + /// The destination vectors. Its length must be at least length. + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnsafeToUnassociatedVector4Core( + ReadOnlySpan source, + Span destination) + where TPixel : unmanaged, IPixel + { + 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)) + { + destinationBase = sourceStart.ToUnassociatedVector4(); + + sourceStart = ref Unsafe.Add(ref sourceStart, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + } + } + + /// + /// Converts pixels to associated native-range vectors without checking span lengths. + /// + /// The source pixel format. + /// The source pixels. + /// The destination vectors. Its length must be at least length. + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnsafeToAssociatedVector4Core( + ReadOnlySpan source, + Span destination) + where TPixel : unmanaged, IPixel + { + 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)) + { + destinationBase = sourceStart.ToAssociatedVector4(); + + sourceStart = ref Unsafe.Add(ref sourceStart, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + } } + /// + /// Converts unassociated scaled vectors to pixels without checking span lengths. + /// + /// The destination pixel format. + /// The source vectors. + /// The destination pixels. Its length must be at least length. [MethodImpl(InliningOptions.ShortMethod)] - private static void UnsafeFromVector4Core( - ReadOnlySpan sourceVectors, - Span destPixels) + private static void UnsafeFromUnassociatedScaledVector4Core( + 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.FromUnassociatedScaledVector4(sourceStart); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } + /// + /// Converts associated scaled vectors to pixels without checking span lengths. + /// + /// The destination pixel format. + /// The source vectors. + /// The destination pixels. Its length must be at least length. [MethodImpl(InliningOptions.ShortMethod)] - private static void UnsafeToVector4Core( - ReadOnlySpan sourcePixels, - Span destVectors) + private static void UnsafeFromAssociatedScaledVector4Core( + 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 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 = sourceStart.ToVector4(); + destinationBase = TPixel.FromAssociatedScaledVector4(sourceStart); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } + /// + /// Converts pixels to unassociated scaled vectors without checking span lengths. + /// + /// The source pixel format. + /// The source pixels. + /// The destination vectors. Its length must be at least length. [MethodImpl(InliningOptions.ShortMethod)] - private static void UnsafeFromScaledVector4Core( - ReadOnlySpan sourceVectors, - Span destinationColors) + private static void UnsafeToUnassociatedScaledVector4Core( + 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 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.FromScaledVector4(sourceStart); + destinationBase = sourceStart.ToUnassociatedScaledVector4(); sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destRef = ref Unsafe.Add(ref destRef, 1); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); } } + /// + /// Converts pixels to associated scaled vectors without checking span lengths. + /// + /// The source pixel format. + /// The source pixels. + /// The destination vectors. Its length must be at least length. [MethodImpl(InliningOptions.ShortMethod)] - private static void UnsafeToScaledVector4Core( - ReadOnlySpan sourceColors, - Span destinationVectors) + private static void UnsafeToAssociatedScaledVector4Core( + 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.ToAssociatedScaledVector4(); 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/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 3442a08075..886732eb93 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.PixelFormats.Utils; @@ -14,10 +15,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils; internal static partial class Vector4Converters { /// - /// Provides efficient implementations for batched to/from conversion. - /// which is applicable for -compatible pixel types where - /// returns the same scaled result as . - /// The method is works by internally converting to a therefore it's not applicable for that type! + /// Provides efficient implementations for batched conversion between RGBA-compatible pixel types and values. /// public static class RgbaCompatible { @@ -28,100 +26,125 @@ public static class RgbaCompatible 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)] + /// 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; + destination = destination[..count]; // 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: + // ToVector4 expands each pixel into a 16-byte vector. Reuse the unwritten destination tail as RGBA staging + // so pixelOperations can reorder the source without allocating a temporary row. 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, - // but we are always reading/writing at different positions: + // Staging overlaps the final output vector, which remains unwritten until the staged bytes are consumed. 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 + /// 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; + + // Source defines the conversion length. Do not expose spare destination capacity + // to downstream shuffles whose scalar tail is destination-length driven. + destination = destination[..count]; // 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: - 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(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/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs index 8f682ae8f6..d701b0ab5f 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs @@ -3,15 +3,17 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; namespace SixLabors.ImageSharp.PixelFormats.Utils; internal static partial class Vector4Converters { /// - /// Apply modifiers used requested by ToVector4() conversion. + /// Applies the modifiers requested by ToVector4() conversion. /// + /// The span of vectors. + /// The modifier rule. [MethodImpl(InliningOptions.ShortMethod)] internal static void ApplyForwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { @@ -27,8 +29,10 @@ internal static void ApplyForwardConversionModifiers(Span vectors, Pixe } /// - /// Apply modifiers used requested by FromVector4() conversion. + /// Applies the modifiers 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/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 4baedd9bae..061e2bd804 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -42,7 +42,7 @@ public Complex64(float real, float imaginary) /// The scalar to use to multiply the value. /// The result [MethodImpl(InliningOptions.ShortMethod)] - public static Complex64 operator *(Complex64 value, float scalar) => new Complex64(value.Real * scalar, value.Imaginary * scalar); + public static Complex64 operator *(Complex64 value, float scalar) => new(value.Real * scalar, value.Imaginary * scalar); /// /// Performs the multiplication operation between a instance and a . diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 9849e0211f..6925070964 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -198,7 +198,7 @@ public DenseMatrix(int columns, int rows, Span data) [MethodImpl(MethodImplOptions.AggressiveInlining)] public DenseMatrix Transpose() { - DenseMatrix result = new DenseMatrix(this.Rows, this.Columns); + DenseMatrix result = new(this.Rows, this.Columns); for (int y = 0; y < this.Rows; y++) { diff --git a/src/ImageSharp/Primitives/Number.cs b/src/ImageSharp/Primitives/Number.cs index fae67bbea1..a996a94f4d 100644 --- a/src/ImageSharp/Primitives/Number.cs +++ b/src/ImageSharp/Primitives/Number.cs @@ -47,19 +47,19 @@ public Number(uint value) /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(int value) => new Number(value); + public static implicit operator Number(int value) => new(value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(uint value) => new Number(value); + public static implicit operator Number(uint value) => new(value); /// /// Converts the specified to an instance of this type. /// /// The value. - public static implicit operator Number(ushort value) => new Number((uint)value); + public static implicit operator Number(ushort value) => new((uint)value); /// /// Converts the specified to a . diff --git a/src/ImageSharp/Primitives/Point.cs b/src/ImageSharp/Primitives/Point.cs index 8ace7ffacf..99193e3bb0 100644 --- a/src/ImageSharp/Primitives/Point.cs +++ b/src/ImageSharp/Primitives/Point.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp; @@ -69,7 +70,7 @@ public Point(Size size) /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -232,14 +233,26 @@ public Point(Size size) /// The transformation matrix used. /// The transformed . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Transform(Point point, Matrix3x2 matrix) => Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); + public static PointF Transform(Point point, Matrix3x2 matrix) + => Vector2.Transform(new Vector2(point.X, point.Y), matrix); + + /// + /// Transforms a point by a specified 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Transform(Point point, Matrix4x4 matrix) + => TransformUtilities.ProjectiveTransform2D(point.X, point.Y, matrix); /// /// Deconstructs this point into two integers. /// /// The out value for X. /// The out value for Y. - public void Deconstruct(out int x, out int y) + public readonly void Deconstruct(out int x, out int y) { x = this.X; y = this.Y; @@ -268,17 +281,17 @@ public void Offset(int dx, int dy) public void Offset(Point point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y); /// - public override string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; + public override readonly string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; /// - public override bool Equals(object? obj) => obj is Point other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Point other && this.Equals(other); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + public readonly bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); diff --git a/src/ImageSharp/Primitives/PointF.cs b/src/ImageSharp/Primitives/PointF.cs index de363e2bd3..7979c0af06 100644 --- a/src/ImageSharp/Primitives/PointF.cs +++ b/src/ImageSharp/Primitives/PointF.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp; @@ -58,7 +59,7 @@ public PointF(SizeF size) /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -246,12 +247,23 @@ public PointF(SizeF size) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Transform(PointF point, Matrix3x2 matrix) => Vector2.Transform(point, matrix); + /// + /// Transforms a point by a specified 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Transform(PointF point, Matrix4x4 matrix) + => TransformUtilities.ProjectiveTransform2D(point.X, point.Y, matrix); + /// /// Deconstructs this point into two floats. /// /// The out value for X. /// The out value for Y. - public void Deconstruct(out float x, out float y) + public readonly void Deconstruct(out float x, out float y) { x = this.X; y = this.Y; @@ -277,15 +289,15 @@ public void Offset(float dx, float dy) public void Offset(PointF point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y); /// - public override string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; + public override readonly string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; /// - public override bool Equals(object? obj) => obj is PointF pointF && this.Equals(pointF); + public override readonly bool Equals(object? obj) => obj is PointF pointF && this.Equals(pointF); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + public readonly bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); } diff --git a/src/ImageSharp/Primitives/Rectangle.cs b/src/ImageSharp/Primitives/Rectangle.cs index e2ae5071ef..26a94837fe 100644 --- a/src/ImageSharp/Primitives/Rectangle.cs +++ b/src/ImageSharp/Primitives/Rectangle.cs @@ -260,11 +260,17 @@ public static Rectangle Ceiling(RectangleF rectangle) /// The transformation matrix. /// A transformed rectangle. public static RectangleF Transform(Rectangle rectangle, Matrix3x2 matrix) - { - PointF bottomRight = Point.Transform(new Point(rectangle.Right, rectangle.Bottom), matrix); - PointF topLeft = Point.Transform(rectangle.Location, matrix); - return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); - } + => RectangleF.Transform(rectangle, matrix); + + /// + /// Transforms a rectangle by the given 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed rectangle. + public static RectangleF Transform(Rectangle rectangle, Matrix4x4 matrix) + => RectangleF.Transform(rectangle, matrix); /// /// Converts a to a by performing a truncate operation on all the coordinates. diff --git a/src/ImageSharp/Primitives/RectangleF.cs b/src/ImageSharp/Primitives/RectangleF.cs index 68add77d09..3f41c2d073 100644 --- a/src/ImageSharp/Primitives/RectangleF.cs +++ b/src/ImageSharp/Primitives/RectangleF.cs @@ -236,9 +236,39 @@ public static RectangleF Inflate(RectangleF rectangle, float x, float y) /// A transformed . public static RectangleF Transform(RectangleF rectangle, Matrix3x2 matrix) { + PointF topLeft = PointF.Transform(rectangle.Location, matrix); + PointF topRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Top), matrix); + PointF bottomLeft = PointF.Transform(new PointF(rectangle.Left, rectangle.Bottom), matrix); PointF bottomRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); + + float left = MathF.Min(MathF.Min(topLeft.X, topRight.X), MathF.Min(bottomLeft.X, bottomRight.X)); + float top = MathF.Min(MathF.Min(topLeft.Y, topRight.Y), MathF.Min(bottomLeft.Y, bottomRight.Y)); + float right = MathF.Max(MathF.Max(topLeft.X, topRight.X), MathF.Max(bottomLeft.X, bottomRight.X)); + float bottom = MathF.Max(MathF.Max(topLeft.Y, topRight.Y), MathF.Max(bottomLeft.Y, bottomRight.Y)); + + return FromLTRB(left, top, right, bottom); + } + + /// + /// Transforms a rectangle by the given 4x4 matrix, applying a projective transform + /// flattened into 2D space. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed . + public static RectangleF Transform(RectangleF rectangle, Matrix4x4 matrix) + { PointF topLeft = PointF.Transform(rectangle.Location, matrix); - return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); + PointF topRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Top), matrix); + PointF bottomLeft = PointF.Transform(new PointF(rectangle.Left, rectangle.Bottom), matrix); + PointF bottomRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); + + float left = MathF.Min(MathF.Min(topLeft.X, topRight.X), MathF.Min(bottomLeft.X, bottomRight.X)); + float top = MathF.Min(MathF.Min(topLeft.Y, topRight.Y), MathF.Min(bottomLeft.Y, bottomRight.Y)); + float right = MathF.Max(MathF.Max(topLeft.X, topRight.X), MathF.Max(bottomLeft.X, bottomRight.X)); + float bottom = MathF.Max(MathF.Max(topLeft.Y, topRight.Y), MathF.Max(bottomLeft.Y, bottomRight.Y)); + + return FromLTRB(left, top, right, bottom); } /// diff --git a/src/ImageSharp/Primitives/SignedRational.cs b/src/ImageSharp/Primitives/SignedRational.cs index d56ea825e6..0f8e6518ad 100644 --- a/src/ImageSharp/Primitives/SignedRational.cs +++ b/src/ImageSharp/Primitives/SignedRational.cs @@ -42,7 +42,7 @@ public SignedRational(int numerator, int denominator, bool simplify) { if (simplify) { - var rational = new LongRational(numerator, denominator).Simplify(); + LongRational rational = new LongRational(numerator, denominator).Simplify(); this.Numerator = (int)rational.Numerator; this.Denominator = (int)rational.Denominator; @@ -70,7 +70,7 @@ public SignedRational(double value) /// Whether to use the best possible precision when parsing the value. public SignedRational(double value, bool bestPrecision) { - var rational = LongRational.FromDouble(value, bestPrecision); + LongRational rational = LongRational.FromDouble(value, bestPrecision); this.Numerator = (int)rational.Numerator; this.Denominator = (int)rational.Denominator; @@ -142,8 +142,8 @@ public override bool Equals(object? obj) /// public bool Equals(SignedRational other) { - var left = new LongRational(this.Numerator, this.Denominator); - var right = new LongRational(other.Numerator, other.Denominator); + LongRational left = new(this.Numerator, this.Denominator); + LongRational right = new(other.Numerator, other.Denominator); return left.Equals(right); } @@ -151,7 +151,7 @@ public bool Equals(SignedRational other) /// public override int GetHashCode() { - var self = new LongRational(this.Numerator, this.Denominator); + LongRational self = new(this.Numerator, this.Denominator); return self.GetHashCode(); } @@ -182,7 +182,7 @@ public override string ToString() /// The public string ToString(IFormatProvider provider) { - var rational = new LongRational(this.Numerator, this.Denominator); + LongRational rational = new(this.Numerator, this.Denominator); return rational.ToString(provider); } } diff --git a/src/ImageSharp/Primitives/Size.cs b/src/ImageSharp/Primitives/Size.cs index 945b680daa..0e65a81388 100644 --- a/src/ImageSharp/Primitives/Size.cs +++ b/src/ImageSharp/Primitives/Size.cs @@ -85,14 +85,14 @@ public Size(Point point) /// /// The point. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height); + public static implicit operator SizeF(Size size) => new(size.Width, size.Height); /// /// Converts the given into a . /// /// The size. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Point(Size size) => new Point(size.Width, size.Height); + public static explicit operator Point(Size size) => new(size.Width, size.Height); /// /// Computes the sum of adding two sizes. @@ -138,7 +138,7 @@ public Size(Point point) /// Dividend of type . /// Divisor of type . /// Result of type . - public static Size operator /(Size left, int right) => new Size(unchecked(left.Width / right), unchecked(left.Height / right)); + public static Size operator /(Size left, int right) => new(unchecked(left.Width / right), unchecked(left.Height / right)); /// /// Multiplies by a producing . @@ -163,7 +163,7 @@ public Size(Point point) /// Divisor of type . /// Result of type . public static SizeF operator /(Size left, float right) - => new SizeF(left.Width / right, left.Height / right); + => new(left.Width / right, left.Height / right); /// /// Compares two objects for equality. @@ -202,7 +202,7 @@ public Size(Point point) /// The size on the right hand of the operand. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height)); + public static Size Add(Size left, Size right) => new(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height)); /// /// Contracts a by another . @@ -211,7 +211,7 @@ public Size(Point point) /// The size on the right hand of the operand. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height)); + public static Size Subtract(Size left, Size right) => new(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height)); /// /// Converts a to a by performing a ceiling operation on all the dimensions. @@ -219,7 +219,7 @@ public Size(Point point) /// The size. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height))); + public static Size Ceiling(SizeF size) => new(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height))); /// /// Converts a to a by performing a round operation on all the dimensions. @@ -227,7 +227,7 @@ public Size(Point point) /// The size. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height))); + public static Size Round(SizeF size) => new(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height))); /// /// Transforms a size by the given matrix. @@ -237,7 +237,7 @@ public Size(Point point) /// A transformed size. public static SizeF Transform(Size size, Matrix3x2 matrix) { - var v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); + Vector2 v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); return new SizeF(v.X, v.Y); } @@ -248,7 +248,7 @@ public static SizeF Transform(Size size, Matrix3x2 matrix) /// The size. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); + public static Size Truncate(SizeF size) => new(unchecked((int)size.Width), unchecked((int)size.Height)); /// /// Deconstructs this size into two integers. @@ -281,7 +281,7 @@ public void Deconstruct(out int width, out int height) /// Multiplier of type . /// Product of type . private static Size Multiply(Size size, int multiplier) => - new Size(unchecked(size.Width * multiplier), unchecked(size.Height * multiplier)); + new(unchecked(size.Width * multiplier), unchecked(size.Height * multiplier)); /// /// Multiplies by a producing . @@ -290,5 +290,5 @@ private static Size Multiply(Size size, int multiplier) => /// Multiplier of type . /// Product of type SizeF. private static SizeF Multiply(Size size, float multiplier) => - new SizeF(size.Width * multiplier, size.Height * multiplier); + new(size.Width * multiplier, size.Height * multiplier); } diff --git a/src/ImageSharp/Primitives/SizeF.cs b/src/ImageSharp/Primitives/SizeF.cs index 36cf9eb8ef..108ea1eed0 100644 --- a/src/ImageSharp/Primitives/SizeF.cs +++ b/src/ImageSharp/Primitives/SizeF.cs @@ -67,7 +67,7 @@ public SizeF(PointF point) /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -191,7 +191,7 @@ public SizeF(PointF point) /// A transformed size. public static SizeF Transform(SizeF size, Matrix3x2 matrix) { - var v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); + Vector2 v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); return new SizeF(v.X, v.Y); } @@ -201,24 +201,24 @@ public static SizeF Transform(SizeF size, Matrix3x2 matrix) /// /// The out value for the width. /// The out value for the height. - public void Deconstruct(out float width, out float height) + public readonly void Deconstruct(out float width, out float height) { width = this.Width; height = this.Height; } /// - public override int GetHashCode() => HashCode.Combine(this.Width, this.Height); + public override readonly int GetHashCode() => HashCode.Combine(this.Width, this.Height); /// - public override string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; + public override readonly string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; /// - public override bool Equals(object? obj) => obj is SizeF && this.Equals((SizeF)obj); + public override readonly bool Equals(object? obj) => obj is SizeF sizeF && this.Equals(sizeF); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + public readonly bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); /// /// Multiplies by a producing . diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index 4ac9546f39..84d0d2ea1d 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -11,30 +11,15 @@ namespace SixLabors.ImageSharp.Processing; /// public class AffineTransformBuilder { - private readonly List> transformMatrixFactories = new(); + private readonly List> transformMatrixFactories = []; /// /// Initializes a new instance of the class. /// public AffineTransformBuilder() - : this(TransformSpace.Pixel) { } - /// - /// Initializes a new instance of the class. - /// - /// - /// The to use when applying the affine transform. - /// - public AffineTransformBuilder(TransformSpace transformSpace) - => this.TransformSpace = transformSpace; - - /// - /// Gets the to use when applying the affine transform. - /// - public TransformSpace TransformSpace { get; } - /// /// Prepends a rotation matrix using the given rotation angle in degrees /// and the image center point as rotation center. @@ -52,7 +37,7 @@ public AffineTransformBuilder PrependRotationDegrees(float degrees) /// The . public AffineTransformBuilder PrependRotationRadians(float radians) => this.Prepend( - size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)); + size => TransformUtilities.CreateRotationTransformMatrixRadians(radians, size)); /// /// Prepends a rotation matrix using the given rotation in degrees at the given origin. @@ -88,7 +73,7 @@ public AffineTransformBuilder AppendRotationDegrees(float degrees) /// The amount of rotation, in radians. /// The . public AffineTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)); + => this.Append(size => TransformUtilities.CreateRotationTransformMatrixRadians(radians, size)); /// /// Appends a rotation matrix using the given rotation in degrees at the given origin. @@ -172,7 +157,7 @@ public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) /// The Y angle, in radians. /// The . public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)); + => this.Prepend(size => TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -210,7 +195,7 @@ public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) /// The Y angle, in radians. /// The . public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)); + => this.Append(size => TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -301,7 +286,8 @@ public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) /// /// The source image size. /// The . - public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); + public Matrix3x2 BuildMatrix(Size sourceSize) + => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); /// /// Returns the combined transform matrix for a given source rectangle. @@ -343,25 +329,39 @@ public Matrix3x2 BuildMatrix(Rectangle sourceRectangle) /// for linear transforms. /// /// The . - public Size GetTransformedSize(Rectangle sourceRectangle) + public SizeF GetTransformedSize(Rectangle sourceRectangle) { - Size size = sourceRectangle.Size; - - // Translate the origin matrix to cater for source rectangle offsets. - Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location); + Matrix3x2 matrix = this.BuildMatrix(sourceRectangle); + return GetTransformedSize(sourceRectangle, matrix); + } - foreach (Func factory in this.transformMatrixFactories) - { - matrix *= factory(size); - CheckDegenerate(matrix); - } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// The transformation matrix. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix3x2 matrix) + => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); - return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace); + /// + /// Clears all accumulated transform matrices, resetting the builder to its initial state. + /// + /// The . + public AffineTransformBuilder Clear() + { + this.transformMatrixFactories.Clear(); + return this; } private static void CheckDegenerate(Matrix3x2 matrix) { - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 4d95e060dc..63c4895080 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -61,9 +61,7 @@ public Image GetResultImage() /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor) - { - return this.ApplyProcessor(processor, this.GetCurrentBounds()); - } + => this.ApplyProcessor(processor, this.GetCurrentBounds()); /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) @@ -74,11 +72,9 @@ public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectang // interim clone if the first processor in the pipeline is a cloning processor. if (processor is ICloningImageProcessor cloningImageProcessor) { - using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.Configuration, this.source, rectangle)) - { - this.destination = pixelProcessor.CloneAndExecute(); - return this; - } + using ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.Configuration, this.source, rectangle); + this.destination = pixelProcessor.CloneAndExecute(); + return this; } // Not a cloning processor? We need to create a clone to operate on. diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs index e3e6f13ed6..71252e0bb0 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs @@ -44,13 +44,13 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou /// Applies a bokeh blur to the image. /// /// The current image processing context. - /// The 'radius' value representing the size of the area to sample. - /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. - /// The gamma highlight factor to use to emphasize bright spots in the source image /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image /// The . - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, int radius, int components, float gamma) => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs index 6611af742b..73e40b57aa 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs @@ -44,10 +44,10 @@ public static IImageProcessingContext BoxBlur(this IImageProcessingContext sourc /// Applies a box blur to the image. /// /// The current image processing context. - /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'radius' value representing the size of the area to sample. /// /// The to use when mapping the pixels outside of the border, in X direction. /// @@ -55,9 +55,11 @@ public static IImageProcessingContext BoxBlur(this IImageProcessingContext sourc /// The to use when mapping the pixels outside of the border, in Y direction. /// /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) - { - var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY); - return source.ApplyProcessor(processor, rectangle); - } + public static IImageProcessingContext BoxBlur( + this IImageProcessingContext source, + Rectangle rectangle, + int radius, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + => source.ApplyProcessor(new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs new file mode 100644 index 0000000000..2980ff44f0 --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Processing.Extensions.Convolution; + +/// +/// Defines general convolution extensions to apply on an +/// using Mutate/Clone. +/// +public static class ConvolutionExtensions +{ + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The convolution kernel to apply. + /// The . + public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY) + => Convolve(source, kernelXY, false); + + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The convolution kernel to apply. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The . + public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY, bool preserveAlpha) + => Convolve(source, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat); + + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The convolution kernel to apply. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The to use when mapping the pixels outside of the border, in X direction. + /// The to use when mapping the pixels outside of the border, in Y direction. + /// The . + public static IImageProcessingContext Convolve( + this IImageProcessingContext source, + DenseMatrix kernelXY, + bool preserveAlpha, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY)); + + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The rectangle structure that specifies the portion of the image object to alter. + /// The convolution kernel to apply. + /// The . + public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY) + => Convolve(source, rectangle, kernelXY, false); + + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The rectangle structure that specifies the portion of the image object to alter. + /// The convolution kernel to apply. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The . + public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY, bool preserveAlpha) + => Convolve(source, rectangle, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat); + + /// + /// Applies a convolution filter to the image. + /// + /// The current image processing context. + /// The rectangle structure that specifies the portion of the image object to alter. + /// The convolution kernel to apply. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The to use when mapping the pixels outside of the border, in X direction. + /// The to use when mapping the pixels outside of the border, in Y direction. + /// The . + public static IImageProcessingContext Convolve( + this IImageProcessingContext source, + Rectangle rectangle, + DenseMatrix kernelXY, + bool preserveAlpha, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY), rectangle); +} diff --git a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs index b044c3966f..c8fb230559 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs @@ -16,8 +16,8 @@ public static class DetectEdgesExtensions /// /// The current image processing context. /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => - DetectEdges(source, KnownEdgeDetectorKernels.Sobel); + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) + => DetectEdges(source, KnownEdgeDetectorKernels.Sobel); /// /// Detects any edges within the image. @@ -28,10 +28,8 @@ public static IImageProcessingContext DetectEdges(this IImageProcessingContext s /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext DetectEdges( - this IImageProcessingContext source, - Rectangle rectangle) => - DetectEdges(source, KnownEdgeDetectorKernels.Sobel, rectangle); + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) + => DetectEdges(source, rectangle, KnownEdgeDetectorKernels.Sobel); /// /// Detects any edges within the image operating in grayscale mode. @@ -39,10 +37,8 @@ public static IImageProcessingContext DetectEdges( /// The current image processing context. /// The 2D edge detector kernel. /// The . - public static IImageProcessingContext DetectEdges( - this IImageProcessingContext source, - EdgeDetector2DKernel kernel) => - DetectEdges(source, kernel, true); + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel) + => DetectEdges(source, kernel, true); /// /// Detects any edges within the image using a . @@ -57,49 +53,41 @@ public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetector2DKernel kernel, bool grayscale) - { - var processor = new EdgeDetector2DProcessor(kernel, grayscale); - source.ApplyProcessor(processor); - return source; - } + => source.ApplyProcessor(new EdgeDetector2DProcessor(kernel, grayscale)); /// /// Detects any edges within the image operating in grayscale mode. /// /// The current image processing context. - /// The 2D edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 2D edge detector kernel. /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetector2DKernel kernel, - Rectangle rectangle) => - DetectEdges(source, kernel, true, rectangle); + Rectangle rectangle, + EdgeDetector2DKernel kernel) + => DetectEdges(source, rectangle, kernel, true); /// /// Detects any edges within the image using a . /// /// The current image processing context. + /// + /// The structure that specifies the portion of the image object to alter. + /// /// The 2D edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// - /// - /// The structure that specifies the portion of the image object to alter. - /// /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, + Rectangle rectangle, EdgeDetector2DKernel kernel, - bool grayscale, - Rectangle rectangle) - { - var processor = new EdgeDetector2DProcessor(kernel, grayscale); - source.ApplyProcessor(processor, rectangle); - return source; - } + bool grayscale) + => source.ApplyProcessor(new EdgeDetector2DProcessor(kernel, grayscale), rectangle); /// /// Detects any edges within the image operating in grayscale mode. @@ -107,10 +95,8 @@ public static IImageProcessingContext DetectEdges( /// The current image processing context. /// The edge detector kernel. /// The . - public static IImageProcessingContext DetectEdges( - this IImageProcessingContext source, - EdgeDetectorKernel kernel) => - DetectEdges(source, kernel, true); + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel) + => DetectEdges(source, kernel, true); /// /// Detects any edges within the image using a . @@ -125,66 +111,56 @@ public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorKernel kernel, bool grayscale) - { - var processor = new EdgeDetectorProcessor(kernel, grayscale); - source.ApplyProcessor(processor); - return source; - } + => source.ApplyProcessor(new EdgeDetectorProcessor(kernel, grayscale)); /// /// Detects any edges within the image operating in grayscale mode. /// /// The current image processing context. - /// The edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// + /// The edge detector kernel. /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetectorKernel kernel, - Rectangle rectangle) => - DetectEdges(source, kernel, true, rectangle); + Rectangle rectangle, + EdgeDetectorKernel kernel) + => DetectEdges(source, rectangle, kernel, true); /// /// Detects any edges within the image using a . /// /// The current image processing context. + /// + /// The structure that specifies the portion of the image object to alter. + /// /// The edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// - /// - /// The structure that specifies the portion of the image object to alter. - /// /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, + Rectangle rectangle, EdgeDetectorKernel kernel, - bool grayscale, - Rectangle rectangle) - { - var processor = new EdgeDetectorProcessor(kernel, grayscale); - source.ApplyProcessor(processor, rectangle); - return source; - } + bool grayscale) + => source.ApplyProcessor(new EdgeDetectorProcessor(kernel, grayscale), rectangle); /// /// Detects any edges within the image operating in grayscale mode. /// /// The current image processing context. - /// Thecompass edge detector kernel. + /// The compass edge detector kernel. /// The . - public static IImageProcessingContext DetectEdges( - this IImageProcessingContext source, - EdgeDetectorCompassKernel kernel) => - DetectEdges(source, kernel, true); + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel) + => DetectEdges(source, kernel, true); /// /// Detects any edges within the image using a . /// /// The current image processing context. - /// Thecompass edge detector kernel. + /// The compass edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// @@ -193,47 +169,39 @@ public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, bool grayscale) - { - var processor = new EdgeDetectorCompassProcessor(kernel, grayscale); - source.ApplyProcessor(processor); - return source; - } + => source.ApplyProcessor(new EdgeDetectorCompassProcessor(kernel, grayscale)); /// /// Detects any edges within the image operating in grayscale mode. /// /// The current image processing context. - /// Thecompass edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// + /// The compass edge detector kernel. /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, - EdgeDetectorCompassKernel kernel, - Rectangle rectangle) => - DetectEdges(source, kernel, true, rectangle); + Rectangle rectangle, + EdgeDetectorCompassKernel kernel) + => DetectEdges(source, rectangle, kernel, true); /// /// Detects any edges within the image using a . /// /// The current image processing context. - /// Thecompass edge detector kernel. - /// - /// Whether to convert the image to grayscale before performing edge detection. - /// /// /// The structure that specifies the portion of the image object to alter. /// + /// The compass edge detector kernel. + /// + /// Whether to convert the image to grayscale before performing edge detection. + /// /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, + Rectangle rectangle, EdgeDetectorCompassKernel kernel, - bool grayscale, - Rectangle rectangle) - { - var processor = new EdgeDetectorCompassProcessor(kernel, grayscale); - source.ApplyProcessor(processor, rectangle); - return source; - } + bool grayscale) + => source.ApplyProcessor(new EdgeDetectorCompassProcessor(kernel, grayscale), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs index b851482008..d406bf8d10 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs @@ -32,22 +32,25 @@ public static IImageProcessingContext GaussianBlur(this IImageProcessingContext /// Applies a Gaussian blur to the image. /// /// The current image processing context. - /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) + public static IImageProcessingContext GaussianBlur( + this IImageProcessingContext source, + Rectangle rectangle, + float sigma) => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); /// /// Applies a Gaussian blur to the image. /// /// The current image processing context. - /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'sigma' value representing the weight of the blur. /// /// The to use when mapping the pixels outside of the border, in X direction. /// @@ -55,9 +58,11 @@ public static IImageProcessingContext GaussianBlur(this IImageProcessingContext /// The to use when mapping the pixels outside of the border, in Y direction. /// /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) - { - var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY); - return source.ApplyProcessor(processor, rectangle); - } + public static IImageProcessingContext GaussianBlur( + this IImageProcessingContext source, + Rectangle rectangle, + float sigma, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs index 4a94df0963..9470cdbdc0 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs @@ -16,8 +16,8 @@ public static class GaussianSharpenExtensions /// /// The current image processing context. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => - source.ApplyProcessor(new GaussianSharpenProcessor()); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) + => source.ApplyProcessor(new GaussianSharpenProcessor()); /// /// Applies a Gaussian sharpening filter to the image. @@ -25,32 +25,32 @@ public static IImageProcessingContext GaussianSharpen(this IImageProcessingConte /// The current image processing context. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => - source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. /// /// The current image processing context. - /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'sigma' value representing the weight of the blur. /// The . public static IImageProcessingContext GaussianSharpen( this IImageProcessingContext source, - float sigma, - Rectangle rectangle) => + Rectangle rectangle, + float sigma) => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); /// /// Applies a Gaussian sharpening filter to the image. /// /// The current image processing context. - /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// + /// The 'sigma' value representing the weight of the blur. /// /// The to use when mapping the pixels outside of the border, in X direction. /// @@ -58,9 +58,11 @@ public static IImageProcessingContext GaussianSharpen( /// The to use when mapping the pixels outside of the border, in Y direction. /// /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) - { - var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY); - return source.ApplyProcessor(processor, rectangle); - } + public static IImageProcessingContext GaussianSharpen( + this IImageProcessingContext source, + Rectangle rectangle, + float sigma, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs index a08a398b75..bc6fef62a6 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs @@ -20,21 +20,28 @@ public static class MedianBlurExtensions /// Whether the filter is applied to alpha as well as the color channels. /// /// The . - public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha) + public static IImageProcessingContext MedianBlur( + this IImageProcessingContext source, + int radius, + bool preserveAlpha) => source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha)); /// /// Applies a median blur on the image. /// /// The current image processing context. + /// + /// The structure that specifies the portion of the image object to alter. + /// /// The radius of the area to find the median for. /// /// Whether the filter is applied to alpha as well as the color channels. /// - /// - /// The structure that specifies the portion of the image object to alter. - /// /// The . - public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha, Rectangle rectangle) + public static IImageProcessingContext MedianBlur( + this IImageProcessingContext source, + Rectangle rectangle, + int radius, + bool preserveAlpha) => source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index 25e504831d..79fe68c20c 100644 --- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -22,9 +22,27 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, float opacity) + => DrawImage(source, foreground, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + float opacity, + int foregroundRepeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -40,9 +58,29 @@ public static IImageProcessingContext DrawImage( Image foreground, Rectangle foregroundRectangle, float opacity) + => DrawImage(source, foreground, foregroundRectangle, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The rectangle structure that specifies the portion of the image to draw. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Rectangle foregroundRectangle, + float opacity, + int foregroundRepeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -58,7 +96,27 @@ public static IImageProcessingContext DrawImage( Image foreground, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, opacity); + => DrawImage(source, foreground, colorBlending, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The color blending mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + PixelColorBlendingMode colorBlending, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, colorBlending, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -75,7 +133,29 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, foregroundRectangle, colorBlending, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The rectangle structure that specifies the portion of the image to draw. + /// The color blending mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Rectangle foregroundRectangle, + PixelColorBlendingMode colorBlending, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -92,7 +172,29 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity); + => DrawImage(source, foreground, colorBlending, alphaComposition, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The color blending mode. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -111,7 +213,31 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity); + => DrawImage(source, foreground, foregroundRectangle, colorBlending, alphaComposition, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The rectangle structure that specifies the portion of the image to draw. + /// The color blending mode. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Rectangle foregroundRectangle, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -124,7 +250,25 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, options); + => DrawImage(source, foreground, options, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The options, including the blending type and blending amount. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -139,7 +283,27 @@ public static IImageProcessingContext DrawImage( Image foreground, Rectangle foregroundRectangle, GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options); + => DrawImage(source, foreground, foregroundRectangle, options, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The rectangle structure that specifies the portion of the image to draw. + /// The options, including the blending type and blending amount. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Rectangle foregroundRectangle, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -154,9 +318,29 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, float opacity) + => DrawImage(source, foreground, backgroundLocation, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + float opacity, + int foregroundRepeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -174,9 +358,31 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, float opacity) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The rectangle structure that specifies the portion of the image to draw. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + Rectangle foregroundRectangle, + float opacity, + int foregroundRepeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -194,7 +400,29 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, backgroundLocation, colorBlending, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The color blending to apply. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + PixelColorBlendingMode colorBlending, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -213,7 +441,31 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, opacity, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The rectangle structure that specifies the portion of the image to draw. + /// The color blending to apply. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + Rectangle foregroundRectangle, + PixelColorBlendingMode colorBlending, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -228,7 +480,27 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, GraphicsOptions options) - => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + => DrawImage(source, foreground, backgroundLocation, options, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The options containing the blend mode and opacity. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -245,7 +517,48 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, GraphicsOptions options) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options, 0); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The rectangle structure that specifies the portion of the image to draw. + /// The options containing the blend mode and opacity. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + Rectangle foregroundRectangle, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, foregroundRepeatCount); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The color blending to apply. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity) + => DrawImage(source, foreground, backgroundLocation, colorBlending, alphaComposition, opacity, 0); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -256,6 +569,10 @@ public static IImageProcessingContext DrawImage( /// The color blending to apply. /// The alpha composition mode. /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// /// The . public static IImageProcessingContext DrawImage( this IImageProcessingContext source, @@ -263,8 +580,30 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, + float opacity, + int foregroundRepeatCount) + => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity, foregroundRepeatCount)); + + /// + /// Draws the given image together with the currently processing image by blending their pixels. + /// + /// The current image processing context. + /// The image to draw on the currently processing image. + /// The location on the currently processing image at which to draw. + /// The rectangle structure that specifies the portion of the image to draw. + /// The color blending to apply. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Point backgroundLocation, + Rectangle foregroundRectangle, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, float opacity) - => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity)); + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, 0); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -276,6 +615,10 @@ public static IImageProcessingContext DrawImage( /// The color blending to apply. /// The alpha composition mode. /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// /// The . public static IImageProcessingContext DrawImage( this IImageProcessingContext source, @@ -284,8 +627,9 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) => + float opacity, + int foregroundRepeatCount) => source.ApplyProcessor( - new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity), + new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, foregroundRepeatCount), foregroundRectangle); } diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs index 713d4d5b77..676acee0f4 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -42,7 +41,7 @@ public static Buffer2D CalculateIntegralImage(this Image /// The containing all the sums. public static Buffer2D CalculateIntegralImage(this ImageFrame source) where TPixel : unmanaged, IPixel - => source.CalculateIntegralImage(source.Bounds()); + => source.CalculateIntegralImage(source.Bounds); /// /// Apply an image integral. @@ -56,7 +55,7 @@ public static Buffer2D CalculateIntegralImage(this ImageFrame - /// Applies quantization to the image using the . + /// Applies quantization to the image using the . /// /// The current image processing context. /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source) => - Quantize(source, KnownQuantizers.Octree); + Quantize(source, KnownQuantizers.Hexadecatree); /// /// Applies quantization to the image. @@ -29,7 +29,7 @@ public static IImageProcessingContext Quantize(this IImageProcessingContext sour source.ApplyProcessor(new QuantizeProcessor(quantizer)); /// - /// Applies quantization to the image using the . + /// Applies quantization to the image using the . /// /// The current image processing context. /// @@ -37,7 +37,7 @@ public static IImageProcessingContext Quantize(this IImageProcessingContext sour /// /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source, Rectangle rectangle) => - Quantize(source, KnownQuantizers.Octree, rectangle); + Quantize(source, KnownQuantizers.Hexadecatree, rectangle); /// /// Applies quantization to the image. diff --git a/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs index b6db0172dc..96b30d4f8e 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs @@ -30,7 +30,7 @@ public static IImageProcessingContext Pad(this IImageProcessingContext source, i public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height, Color color) { Size size = source.GetCurrentSize(); - var options = new ResizeOptions + ResizeOptions options = new() { // Prevent downsizing. Size = new Size(Math.Max(width, size.Width), Math.Max(height, size.Height)), diff --git a/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs index 01f296d096..4a09639f88 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs @@ -118,7 +118,7 @@ public static IImageProcessingContext Resize( Rectangle targetRectangle, bool compand) { - var options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(width, height), Mode = ResizeMode.Manual, @@ -151,7 +151,7 @@ public static IImageProcessingContext Resize( Rectangle targetRectangle, bool compand) { - var options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(width, height), Mode = ResizeMode.Manual, diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index 60f90b10f2..dd200013a7 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -7,8 +7,8 @@ namespace SixLabors.ImageSharp.Processing; /// -/// Defines extensions that allow the application of composable transform operations on an -/// using Mutate/Clone. +/// Defines extensions that allow the application of composable transform operations +/// on an using Mutate/Clone. /// public static class TransformExtensions { @@ -51,7 +51,7 @@ public static IImageProcessingContext Transform( IResampler sampler) { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = builder.GetTransformedSize(sourceRectangle); + Size targetDimensions = TransformUtilities.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } @@ -113,7 +113,7 @@ public static IImageProcessingContext Transform( IResampler sampler) { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = builder.GetTransformedSize(sourceRectangle); + Size targetDimensions = TransformUtilities.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index b312287b52..fd512557d9 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -20,7 +20,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness /// - public static ColorMatrix AchromatomalyFilter { get; } = new ColorMatrix + public static ColorMatrix AchromatomalyFilter { get; } = new() { M11 = .618F, M12 = .163F, @@ -37,7 +37,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Achromatopsia (Monochrome) color blindness. /// - public static ColorMatrix AchromatopsiaFilter { get; } = new ColorMatrix + public static ColorMatrix AchromatopsiaFilter { get; } = new() { M11 = .299F, M12 = .299F, @@ -54,7 +54,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness. /// - public static ColorMatrix DeuteranomalyFilter { get; } = new ColorMatrix + public static ColorMatrix DeuteranomalyFilter { get; } = new() { M11 = .8F, M12 = .258F, @@ -68,7 +68,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Deuteranopia (Green-Blind) color blindness. /// - public static ColorMatrix DeuteranopiaFilter { get; } = new ColorMatrix + public static ColorMatrix DeuteranopiaFilter { get; } = new() { M11 = .625F, M12 = .7F, @@ -82,7 +82,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Protanomaly (Red-Weak) color blindness. /// - public static ColorMatrix ProtanomalyFilter { get; } = new ColorMatrix + public static ColorMatrix ProtanomalyFilter { get; } = new() { M11 = .817F, M12 = .333F, @@ -96,7 +96,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Protanopia (Red-Blind) color blindness. /// - public static ColorMatrix ProtanopiaFilter { get; } = new ColorMatrix + public static ColorMatrix ProtanopiaFilter { get; } = new() { M11 = .567F, M12 = .558F, @@ -110,7 +110,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness. /// - public static ColorMatrix TritanomalyFilter { get; } = new ColorMatrix + public static ColorMatrix TritanomalyFilter { get; } = new() { M11 = .967F, M21 = .33F, @@ -124,7 +124,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating Tritanopia (Blue-Blind) color blindness. /// - public static ColorMatrix TritanopiaFilter { get; } = new ColorMatrix + public static ColorMatrix TritanopiaFilter { get; } = new() { M11 = .95F, M21 = .05F, @@ -138,7 +138,7 @@ public static class KnownFilterMatrices /// /// Gets an approximated black and white filter /// - public static ColorMatrix BlackWhiteFilter { get; } = new ColorMatrix + public static ColorMatrix BlackWhiteFilter { get; } = new() { M11 = 1.5F, M12 = 1.5F, @@ -190,7 +190,7 @@ public static class KnownFilterMatrices /// /// Gets a filter recreating an old Polaroid camera effect. /// - public static ColorMatrix PolaroidFilter { get; } = new ColorMatrix + public static ColorMatrix PolaroidFilter { get; } = new() { M11 = 1.538F, M12 = -.062F, diff --git a/src/ImageSharp/Processing/KnownQuantizers.cs b/src/ImageSharp/Processing/KnownQuantizers.cs index 6fb3c72e81..b63ba597d1 100644 --- a/src/ImageSharp/Processing/KnownQuantizers.cs +++ b/src/ImageSharp/Processing/KnownQuantizers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -6,14 +6,14 @@ namespace SixLabors.ImageSharp.Processing; /// -/// Contains reusable static instances of known quantizing algorithms +/// Contains reusable static instances of known quantizing algorithms. /// public static class KnownQuantizers { /// - /// Gets the adaptive Octree quantizer. Fast with good quality. + /// Gets the adaptive hexadecatree quantizer. Fast with good quality. /// - public static IQuantizer Octree { get; } = new OctreeQuantizer(); + public static IQuantizer Hexadecatree { get; } = new HexadecatreeQuantizer(); /// /// Gets the Xiaolin Wu's Color Quantizer which generates high quality output. diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs index 73c7c3302d..e17de49d74 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization; /// /// Performs Bradley Adaptive Threshold filter against an image. /// +/// The pixel format. internal class AdaptiveThresholdProcessor : ImageProcessor where TPixel : unmanaged, IPixel { @@ -30,7 +31,7 @@ public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThreshold /// protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); Configuration configuration = this.Configuration; TPixel upper = this.definition.Upper.ToPixel(); @@ -97,19 +98,23 @@ public void Invoke(int y, Span span) Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length); PixelOperations.Instance.ToL8(this.configuration, rowSpan, span); + int startY = this.startY; int maxX = this.bounds.Width - 1; int maxY = this.bounds.Height - 1; + int clusterSize = this.clusterSize; + float thresholdLimit = this.thresholdLimit; + Buffer2D image = this.intImage; for (int x = 0; x < rowSpan.Length; x++) { - int x1 = Math.Clamp(x - this.clusterSize + 1, 0, maxX); - int x2 = Math.Min(x + this.clusterSize + 1, maxX); - int y1 = Math.Clamp(y - this.startY - this.clusterSize + 1, 0, maxY); - int y2 = Math.Min(y - this.startY + this.clusterSize + 1, maxY); + int x1 = Math.Clamp(x - clusterSize + 1, 0, maxX); + int x2 = Math.Min(x + clusterSize + 1, maxX); + int y1 = Math.Clamp(y - startY - clusterSize + 1, 0, maxY); + int y2 = Math.Min(y - startY + clusterSize + 1, maxY); uint count = (uint)((x2 - x1) * (y2 - y1)); - ulong sum = Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], ulong.MaxValue); + ulong sum = Math.Min(image[x2, y2] - image[x1, y2] - image[x2, y1] + image[x1, y1], ulong.MaxValue); - if (span[x].PackedValue * count <= sum * this.thresholdLimit) + if (span[x].PackedValue * count <= sum * thresholdLimit) { rowSpan[x] = this.lower; } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 1c76ea6a45..ad87f36c1c 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -38,8 +38,8 @@ protected override void OnFrameApply(ImageFrame source) Rectangle sourceRectangle = this.SourceRectangle; Configuration configuration = this.Configuration; - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - var operation = new RowOperation( + Rectangle interest = Rectangle.Intersect(sourceRectangle, source.Bounds); + RowOperation operation = new( interest.X, source.PixelBuffer, upper, @@ -169,10 +169,8 @@ private static float GetSaturation(Rgb24 rgb) { return chroma / (max + min); } - else - { - return chroma / (2F - max - min); - } + + return chroma / (2F - max - min); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index aa000a10e7..b641dceb5f 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors; @@ -48,7 +49,6 @@ Image ICloningImageProcessor.CloneAndExecute() Image clone = this.CreateTarget(); this.CheckFrameCount(this.Source, clone); - Configuration configuration = this.Configuration; this.BeforeImageApply(clone); for (int i = 0; i < this.Source.Frames.Count; i++) @@ -77,9 +77,10 @@ void IImageProcessor.Execute() { clone = ((ICloningImageProcessor)this).CloneAndExecute(); - // We now need to move the pixel data/size data from the clone to the source. + // We now need to move the pixel data/size data and any metadata from the clone to the source. this.CheckFrameCount(this.Source, clone); this.Source.SwapOrCopyPixelsBuffersFrom(clone); + this.Source.CopyMetadataFrom(clone); } finally { @@ -132,16 +133,14 @@ protected virtual void BeforeFrameApply(ImageFrame source, ImageFrameThe source image. Cannot be null. /// The cloned/destination image. Cannot be null. protected virtual void AfterFrameApply(ImageFrame source, ImageFrame destination) - { - } + => destination.Metadata.AfterFrameApply(source, destination, Matrix4x4.Identity); /// /// This method is called after the process is applied to prepare the processor. /// /// The cloned/destination image. Cannot be null. protected virtual void AfterImageApply(Image destination) - { - } + => destination.Metadata.AfterImageApply(destination, Matrix4x4.Identity); /// /// Disposes the object and frees resources for the Garbage Collector. @@ -157,7 +156,7 @@ private Image CreateTarget() Size destinationSize = this.GetDestinationSize(); // We will always be creating the clone even for mutate because we may need to resize the canvas. - var destinationFrames = new ImageFrame[source.Frames.Count]; + ImageFrame[] destinationFrames = new ImageFrame[source.Frames.Count]; for (int i = 0; i < destinationFrames.Length; i++) { destinationFrames[i] = new ImageFrame( diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index e4b0a60ab0..7023a3cd98 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; @@ -75,12 +76,12 @@ public BokehBlurProcessor(Configuration configuration, BokehBlurProcessor defini /// protected override void OnFrameApply(ImageFrame source) { - var sourceRectangle = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle sourceRectangle = Rectangle.Intersect(this.SourceRectangle, source.Bounds); // Preliminary gamma highlight pass if (this.gamma == 3F) { - var gammaOperation = new ApplyGamma3ExposureRowOperation(sourceRectangle, source.PixelBuffer, this.Configuration); + ApplyGamma3ExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration); ParallelRowIterator.IterateRows( this.Configuration, sourceRectangle, @@ -88,7 +89,7 @@ protected override void OnFrameApply(ImageFrame source) } else { - var gammaOperation = new ApplyGammaExposureRowOperation(sourceRectangle, source.PixelBuffer, this.Configuration, this.gamma); + ApplyGammaExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration, this.gamma); ParallelRowIterator.IterateRows( this.Configuration, sourceRectangle, @@ -96,7 +97,7 @@ protected override void OnFrameApply(ImageFrame source) } // Create a 0-filled buffer to use to store the result of the component convolutions - using Buffer2D processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean); + using Buffer2D processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size, AllocationOptions.Clean); // Perform the 1D convolutions on all the kernel components and accumulate the results this.OnFrameApplyCore(source, sourceRectangle, this.Configuration, processingBuffer); @@ -104,7 +105,7 @@ protected override void OnFrameApply(ImageFrame source) // Apply the inverse gamma exposure pass, and write the final pixel data if (this.gamma == 3F) { - var operation = new ApplyInverseGamma3ExposureRowOperation(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration); + ApplyInverseGamma3ExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration); ParallelRowIterator.IterateRows( this.Configuration, sourceRectangle, @@ -112,7 +113,7 @@ protected override void OnFrameApply(ImageFrame source) } else { - var operation = new ApplyInverseGammaExposureRowOperation(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, 1 / this.gamma); + ApplyInverseGammaExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, this.gamma); ParallelRowIterator.IterateRows( this.Configuration, sourceRectangle, @@ -134,7 +135,7 @@ private void OnFrameApplyCore( Buffer2D processingBuffer) { // Allocate the buffer with the intermediate convolution results - using Buffer2D firstPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size()); + using Buffer2D firstPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size); // Unlike in the standard 2 pass convolution processor, we use a rectangle of 1x the interest width // to speedup the actual convolution, by applying bulk pixel conversion and clamping calculation. @@ -146,7 +147,7 @@ private void OnFrameApplyCore( // doing two 1D convolutions with the same kernel, we can use a single kernel sampling map as if // we were using a 2D kernel with each dimension being the same as the length of our kernel, and // use the two sampling offset spans resulting from this same map. This saves some extra work. - using var mapXY = new KernelSamplingMap(configuration.MemoryAllocator); + using KernelSamplingMap mapXY = new(configuration.MemoryAllocator); mapXY.BuildSamplingOffsetMap(this.kernelSize, this.kernelSize, sourceRectangle); @@ -161,7 +162,7 @@ private void OnFrameApplyCore( Vector4 parameters = Unsafe.Add(ref paramsRef, (uint)i); // Horizontal convolution - var horizontalOperation = new FirstPassConvolutionRowOperation( + FirstPassConvolutionRowOperation horizontalOperation = new( sourceRectangle, firstPassBuffer, source.PixelBuffer, @@ -175,7 +176,7 @@ private void OnFrameApplyCore( in horizontalOperation); // Vertical 1D convolutions to accumulate the partial results on the target buffer - var verticalOperation = new BokehBlurProcessor.SecondPassConvolutionRowOperation( + BokehBlurProcessor.SecondPassConvolutionRowOperation verticalOperation = new( sourceRectangle, processingBuffer, firstPassBuffer, @@ -237,9 +238,10 @@ public void Invoke(int y, Span span) Span targetBuffer = this.targetValues.DangerousGetRowSpan(y); targetBuffer.Clear(); - // Execute the bulk pixel format conversion for the current row + // Gamma exposure temporarily stores premultiplied numeric intermediates in TPixel without changing their + // representation. Read the native scaled values back directly to avoid an associate/unassociate round trip. Span sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, span); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, span, PixelConversionModifiers.Scale); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(span); ref ComplexVector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); @@ -304,18 +306,14 @@ public int GetRequiredBufferLength(Rectangle bounds) public void Invoke(int y, Span span) { Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y)[this.bounds.X..]; - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Premultiply); - ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); - for (int x = 0; x < this.bounds.Width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, (uint)x); - v.X = MathF.Pow(v.X, this.gamma); - v.Y = MathF.Pow(v.Y, this.gamma); - v.Z = MathF.Pow(v.Z, this.gamma); - } + // Input is premultiplied [0,1] so the LUT is safe here. + GammaCompanding.Expand(span[..this.bounds.Width], this.gamma); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + // The first convolution pass consumes these values as raw premultiplied numerics. Preserve them in the + // native TPixel representation instead of quantizing through an alpha-association conversion. + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan, PixelConversionModifiers.Scale); } } @@ -342,9 +340,7 @@ public ApplyGamma3ExposureRowOperation( /// [MethodImpl(InliningOptions.ShortMethod)] public int GetRequiredBufferLength(Rectangle bounds) - { - return bounds.Width; - } + => bounds.Width; /// [MethodImpl(InliningOptions.ShortMethod)] @@ -352,11 +348,13 @@ public void Invoke(int y, Span span) { Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y)[this.bounds.X..]; - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Premultiply); + PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); Numerics.CubePowOnXYZ(span); - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan); + // The first convolution pass consumes these values as raw premultiplied numerics. Preserve them in the + // native TPixel representation instead of quantizing through an alpha-association conversion. + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan, PixelConversionModifiers.Scale); } } @@ -369,7 +367,7 @@ public void Invoke(int y, Span span) private readonly Buffer2D targetPixels; private readonly Buffer2D sourceValues; private readonly Configuration configuration; - private readonly float inverseGamma; + private readonly float gamma; [MethodImpl(InliningOptions.ShortMethod)] public ApplyInverseGammaExposureRowOperation( @@ -377,36 +375,26 @@ public ApplyInverseGammaExposureRowOperation( Buffer2D targetPixels, Buffer2D sourceValues, Configuration configuration, - float inverseGamma) + float gamma) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourceValues = sourceValues; this.configuration = configuration; - this.inverseGamma = inverseGamma; + this.gamma = gamma; } /// [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { - Vector4 low = Vector4.Zero; - var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); - Span targetPixelSpan = this.targetPixels.DangerousGetRowSpan(y)[this.bounds.X..]; - Span sourceRowSpan = this.sourceValues.DangerousGetRowSpan(y)[this.bounds.X..]; - ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); + Span sourceRowSpan = this.sourceValues.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - for (int x = 0; x < this.bounds.Width; x++) - { - ref Vector4 v = ref Unsafe.Add(ref sourceRef, (uint)x); - Vector4 clamp = Numerics.Clamp(v, low, high); - v.X = MathF.Pow(clamp.X, this.inverseGamma); - v.Y = MathF.Pow(clamp.Y, this.inverseGamma); - v.Z = MathF.Pow(clamp.Z, this.inverseGamma); - } + Numerics.Clamp(MemoryMarshal.Cast(sourceRowSpan), 0, 1F); + GammaCompanding.Compress(sourceRowSpan, this.gamma); - PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan[..this.bounds.Width], targetPixelSpan, PixelConversionModifiers.Premultiply); + PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan, targetPixelSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } @@ -435,17 +423,16 @@ public ApplyInverseGamma3ExposureRowOperation( /// [MethodImpl(InliningOptions.ShortMethod)] - public unsafe void Invoke(int y) + public void Invoke(int y) { Span sourceRowSpan = this.sourceValues.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); - Numerics.Clamp(MemoryMarshal.Cast(sourceRowSpan), 0, float.PositiveInfinity); + Numerics.Clamp(MemoryMarshal.Cast(sourceRowSpan), 0, 1F); Numerics.CubeRootOnXYZ(sourceRowSpan); Span targetPixelSpan = this.targetPixels.DangerousGetRowSpan(y)[this.bounds.X..]; - PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan[..this.bounds.Width], targetPixelSpan, PixelConversionModifiers.Premultiply); + PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan, targetPixelSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index ff3b30e9c1..727e8e96aa 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -68,7 +68,7 @@ public BoxBlurProcessor( /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); + using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); processor.Apply(source); } @@ -80,7 +80,7 @@ protected override void OnFrameApply(ImageFrame source) /// The . private static float[] CreateBoxKernel(int kernelSize) { - var kernel = new float[kernelSize]; + float[] kernel = new float[kernelSize]; kernel.AsSpan().Fill(1F / kernelSize); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index 8a7c424815..4ed6934a20 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; using System.Numerics; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -62,14 +62,14 @@ protected override void OnFrameApply(ImageFrame source) source.CopyTo(targetPixels); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); - using (var map = new KernelSamplingMap(allocator)) + using (KernelSamplingMap map = new(allocator)) { // Since the kernel sizes are identical we can use a single map. map.BuildSamplingOffsetMap(this.KernelY, interest); - var operation = new Convolution2DRowOperation( + Convolution2DRowOperation operation = new( interest, targetPixels, source.PixelBuffer, @@ -79,10 +79,16 @@ protected override void OnFrameApply(ImageFrame source) this.Configuration, this.PreserveAlpha); - ParallelRowIterator.IterateRows, Vector4>( - this.Configuration, - interest, - in operation); + // Convolution is memory-bandwidth-bound with low arithmetic intensity. + // Parallelization degrades performance due to cache line contention from + // overlapping source row reads. See #3111. + using IMemoryOwner buffer = allocator.Allocate(operation.GetRequiredBufferLength(interest)); + Span span = buffer.Memory.Span; + + for (int y = interest.Top; y < interest.Bottom; y++) + { + operation.Invoke(y, span); + } } Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs index 2a4a1abf02..409480871f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs @@ -75,9 +75,11 @@ private void Convolve3(int y, Span span) Span targetYBuffer = span.Slice(boundsWidth, boundsWidth); Span targetXBuffer = span.Slice(boundsWidth * 2, boundsWidth); - var state = new Convolution2DState(in this.kernelMatrixY, in this.kernelMatrixX, this.map); + Convolution2DState state = new(in this.kernelMatrixY, in this.kernelMatrixX, this.map); ref int sampleRowBase = ref state.GetSampleRow((uint)(y - this.bounds.Y)); + // Alpha is preserved separately, so filter straight color and restore the source alpha after combining the kernels. + // Clear the target buffers for each row run. targetYBuffer.Clear(); targetXBuffer.Clear(); @@ -92,7 +94,7 @@ private void Convolve3(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. int sampleY = Unsafe.Add(ref sampleRowBase, kY); sourceRow = this.sourcePixels.DangerousGetRowSpan(sampleY).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); @@ -115,7 +117,7 @@ private void Convolve3(int y, Span span) // Now we need to combine the values and copy the original alpha values // from the source row. sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (nuint x = 0; x < (uint)sourceRow.Length; x++) { @@ -128,7 +130,7 @@ private void Convolve3(int y, Span span) } Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetYBuffer, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetYBuffer, targetRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -141,9 +143,11 @@ private void Convolve4(int y, Span span) Span targetYBuffer = span.Slice(boundsWidth, boundsWidth); Span targetXBuffer = span.Slice(boundsWidth * 2, boundsWidth); - var state = new Convolution2DState(in this.kernelMatrixY, in this.kernelMatrixX, this.map); + Convolution2DState state = new(in this.kernelMatrixY, in this.kernelMatrixX, this.map); ref int sampleRowBase = ref state.GetSampleRow((uint)(y - this.bounds.Y)); + // Alpha participates in this convolution, so filter associated color to keep RGB weighted by coverage throughout the kernel. + // Clear the target buffers for each row run. targetYBuffer.Clear(); targetXBuffer.Clear(); @@ -157,9 +161,8 @@ private void Convolve4(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. int sampleY = Unsafe.Add(ref sampleRowBase, kY); Span sourceRow = this.sourcePixels.DangerousGetRowSpan(sampleY).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); - Numerics.Premultiply(sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); for (uint x = 0; x < (uint)sourceBuffer.Length; x++) @@ -186,11 +189,12 @@ private void Convolve4(int y, Span span) Vector4 vectorX = Unsafe.Add(ref targetBaseX, x); target = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); - } - Numerics.UnPremultiply(targetYBuffer); + // Independent color and alpha gradients can produce RGB greater than alpha. Restore the associated-vector invariant before either storage representation receives the result. + Numerics.ClampRgbToAlpha(ref target); + } Span targetRow = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetYBuffer, targetRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetYBuffer, targetRow, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index cc6e1e5fb2..1e94812fd5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -35,18 +35,48 @@ public Convolution2PassProcessor( Rectangle sourceRectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) + : this(configuration, kernel, kernel, preserveAlpha, source, sourceRectangle, borderWrapModeX, borderWrapModeY) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The 1D convolution kernel. X Direction + /// The 1D convolution kernel. Y Direction + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + /// The to use when mapping the pixels outside of the border, in X direction. + /// The to use when mapping the pixels outside of the border, in Y direction. + public Convolution2PassProcessor( + Configuration configuration, + float[] kernelX, + float[] kernelY, + bool preserveAlpha, + Image source, + Rectangle sourceRectangle, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) : base(configuration, source, sourceRectangle) { - this.Kernel = kernel; + this.KernelX = kernelX; + this.KernelY = kernelY; this.PreserveAlpha = preserveAlpha; this.BorderWrapModeX = borderWrapModeX; this.BorderWrapModeY = borderWrapModeY; } /// - /// Gets the convolution kernel. + /// Gets the convolution kernel. X direction. + /// + public float[] KernelX { get; } + + /// + /// Gets the convolution kernel. Y direction. /// - public float[] Kernel { get; } + public float[] KernelY { get; } /// /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels. @@ -66,23 +96,23 @@ public Convolution2PassProcessor( /// protected override void OnFrameApply(ImageFrame source) { - using Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()); + using Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); // We can create a single sampling map with the size as if we were using the non separated 2D kernel // the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur. - using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator); + using KernelSamplingMap mapXY = new(this.Configuration.MemoryAllocator); - mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY); + mapXY.BuildSamplingOffsetMap(this.KernelX.Length, this.KernelX.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY); // Horizontal convolution - var horizontalOperation = new HorizontalConvolutionRowOperation( + HorizontalConvolutionRowOperation horizontalOperation = new( interest, firstPassPixels, source.PixelBuffer, mapXY, - this.Kernel, + this.KernelX, this.Configuration, this.PreserveAlpha); @@ -92,12 +122,12 @@ protected override void OnFrameApply(ImageFrame source) in horizontalOperation); // Vertical convolution - var verticalOperation = new VerticalConvolutionRowOperation( + VerticalConvolutionRowOperation verticalOperation = new( interest, source.PixelBuffer, firstPassPixels, mapXY, - this.Kernel, + this.KernelY, this.Configuration, this.PreserveAlpha); @@ -140,7 +170,7 @@ public HorizontalConvolutionRowOperation( } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetRequiredBufferLength(Rectangle bounds) => 2 * bounds.Width; @@ -169,12 +199,14 @@ private void Convolve3(int y, Span span) Span sourceBuffer = span[..this.bounds.Width]; Span targetBuffer = span[this.bounds.Width..]; + // Alpha is preserved separately, so filter straight color and restore the source alpha after applying the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); // Get the precalculated source sample row for this kernel row and copy to our buffer. Span sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); @@ -203,9 +235,6 @@ private void Convolve3(int y, Span span) } // Now we need to copy the original alpha values from the source row. - sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); - targetStart = ref MemoryMarshal.GetReference(targetBuffer); while (Unsafe.IsAddressLessThan(ref targetStart, ref targetEnd)) @@ -217,7 +246,7 @@ private void Convolve3(int y, Span span) } Span targetRow = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -231,14 +260,14 @@ private void Convolve4(int y, Span span) Span sourceBuffer = span[..this.bounds.Width]; Span targetBuffer = span[this.bounds.Width..]; + // Alpha participates in this convolution, so filter associated color to keep RGB weighted by coverage throughout the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); // Get the precalculated source sample row for this kernel row and copy to our buffer. Span sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); - - Numerics.Premultiply(sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); @@ -266,10 +295,8 @@ private void Convolve4(int y, Span span) sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, (uint)kernelSize); } - Numerics.UnPremultiply(targetBuffer); - Span targetRow = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } @@ -306,7 +333,7 @@ public VerticalConvolutionRowOperation( } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetRequiredBufferLength(Rectangle bounds) => 2 * bounds.Width; @@ -337,6 +364,8 @@ private void Convolve3(int y, Span span) ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (uint)((y - this.bounds.Y) * kernelSize)); + // Alpha is preserved separately, so filter straight color and restore the source alpha after applying the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); @@ -350,7 +379,7 @@ private void Convolve3(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. sourceRow = this.sourcePixels.DangerousGetRowSpan(sampleRowBase).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); @@ -371,7 +400,7 @@ private void Convolve3(int y, Span span) // Now we need to copy the original alpha values from the source row. sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); { ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); @@ -386,7 +415,7 @@ private void Convolve3(int y, Span span) } Span targetRow = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -402,6 +431,8 @@ private void Convolve4(int y, Span span) ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (uint)((y - this.bounds.Y) * kernelSize)); + // Alpha participates in this convolution, so filter associated color to keep RGB weighted by coverage throughout the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); @@ -415,9 +446,7 @@ private void Convolve4(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. sourceRow = this.sourcePixels.DangerousGetRowSpan(sampleRowBase).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); - - Numerics.Premultiply(sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); @@ -436,10 +465,8 @@ private void Convolve4(int y, Span span) sampleRowBase = ref Unsafe.Add(ref sampleRowBase, 1); } - Numerics.UnPremultiply(targetBuffer); - Span targetRow = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRow, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs new file mode 100644 index 0000000000..995a5164d9 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution; + +/// +/// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image. +/// +public class ConvolutionProcessor : IImageProcessor +{ + /// + /// Initializes a new instance of the class. + /// + /// The 2d gradient operator. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The to use when mapping the pixels outside of the border, in X direction. + /// The to use when mapping the pixels outside of the border, in Y direction. + public ConvolutionProcessor( + in DenseMatrix kernelXY, + bool preserveAlpha, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) + { + this.KernelXY = kernelXY; + this.PreserveAlpha = preserveAlpha; + this.BorderWrapModeX = borderWrapModeX; + this.BorderWrapModeY = borderWrapModeY; + } + + /// + /// Gets the 2d convolution kernel. + /// + public DenseMatrix KernelXY { get; } + + /// + /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels. + /// + public bool PreserveAlpha { get; } + + /// + /// Gets the to use when mapping the pixels outside of the border, in X direction. + /// + public BorderWrappingMode BorderWrapModeX { get; } + + /// + /// Gets the to use when mapping the pixels outside of the border, in Y direction. + /// + public BorderWrappingMode BorderWrapModeY { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, + IPixel + { + if (this.KernelXY.TryGetLinearlySeparableComponents(out float[]? kernelX, out float[]? kernelY)) + { + return new Convolution2PassProcessor( + configuration, + kernelX, + kernelY, + this.PreserveAlpha, + source, + sourceRectangle, + this.BorderWrapModeX, + this.BorderWrapModeY); + } + + return new ConvolutionProcessor( + configuration, + this.KernelXY, + this.PreserveAlpha, + source, + sourceRectangle, + this.BorderWrapModeX, + this.BorderWrapModeY); + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index 4aefa0daef..db9eff55c0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -36,11 +37,8 @@ internal static float[] CreateGaussianBlurKernel(int size, float weight) kernel[i] = gx; } - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[i] /= sum; - } + // Divide every weight by the accumulated Gaussian sum so the kernel has unit response. + TensorPrimitives_.Divide(kernel, sum, kernel); return kernel; } @@ -68,25 +66,15 @@ internal static float[] CreateGaussianSharpenKernel(int size, float weight) // Invert the kernel for sharpening. int midpointRounded = (int)midpoint; - for (int i = 0; i < size; i++) - { - if (i == midpointRounded) - { - // Calculate central value - kernel[i] = (2F * sum) - kernel[i]; - } - else - { - // invert value - kernel[i] = -kernel[i]; - } - } + float midpointValue = kernel[midpointRounded]; + TensorPrimitives_.Negate(kernel, kernel); - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[i] /= sum; - } + // The sharpening kernel negates every Gaussian weight except its center. Restore that original + // center while adding twice the Gaussian sum so the complete kernel retains unit response. + kernel[midpointRounded] = (2F * sum) - midpointValue; + + // Sharpening changes signs but preserves the Gaussian sum, so the same divisor produces unit response. + TensorPrimitives_.Divide(kernel, sum, kernel); return kernel; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index d059ebe030..e599324f95 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -31,10 +31,34 @@ public ConvolutionProcessor( bool preserveAlpha, Image source, Rectangle sourceRectangle) + : this(configuration, kernelXY, preserveAlpha, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The 2d gradient operator. + /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + /// The to use when mapping the pixels outside of the border, in X direction. + /// The to use when mapping the pixels outside of the border, in Y direction. + public ConvolutionProcessor( + Configuration configuration, + in DenseMatrix kernelXY, + bool preserveAlpha, + Image source, + Rectangle sourceRectangle, + BorderWrappingMode borderWrapModeX, + BorderWrappingMode borderWrapModeY) : base(configuration, source, sourceRectangle) { this.KernelXY = kernelXY; this.PreserveAlpha = preserveAlpha; + this.BorderWrapModeX = borderWrapModeX; + this.BorderWrapModeY = borderWrapModeY; } /// @@ -47,21 +71,31 @@ public ConvolutionProcessor( /// public bool PreserveAlpha { get; } + /// + /// Gets the to use when mapping the pixels outside of the border, in X direction. + /// + public BorderWrappingMode BorderWrapModeX { get; } + + /// + /// Gets the to use when mapping the pixels outside of the border, in Y direction. + /// + public BorderWrappingMode BorderWrapModeY { get; } + /// protected override void OnFrameApply(ImageFrame source) { MemoryAllocator allocator = this.Configuration.MemoryAllocator; - using Buffer2D targetPixels = allocator.Allocate2D(source.Size()); + using Buffer2D targetPixels = allocator.Allocate2D(source.Size); source.CopyTo(targetPixels); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); - using (var map = new KernelSamplingMap(allocator)) + using (KernelSamplingMap map = new(allocator)) { - map.BuildSamplingOffsetMap(this.KernelXY, interest); + map.BuildSamplingOffsetMap(this.KernelXY.Rows, this.KernelXY.Columns, interest, this.BorderWrapModeX, this.BorderWrapModeY); - var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha); + RowOperation operation = new(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -121,12 +155,14 @@ public void Invoke(int y, Span span) ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span); Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - var state = new ConvolutionState(in this.kernel, this.map); + ConvolutionState state = new(in this.kernel, this.map); int row = y - this.bounds.Y; ref int sampleRowBase = ref state.GetSampleRow((uint)row); if (this.preserveAlpha) { + // Alpha is preserved separately, so filter straight color and restore the source alpha after applying the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); @@ -137,7 +173,7 @@ public void Invoke(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. int offsetY = Unsafe.Add(ref sampleRowBase, kY); sourceRow = this.sourcePixels.DangerousGetRowSpan(offsetY).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); @@ -157,16 +193,20 @@ public void Invoke(int y, Span span) // Now we need to copy the original alpha values from the source row. sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (nuint x = 0; x < (uint)sourceRow.Length; x++) { ref Vector4 target = ref Unsafe.Add(ref targetBase, x); target.W = Unsafe.Add(ref MemoryMarshal.GetReference(sourceBuffer), x).W; } + + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } else { + // Alpha participates in this convolution, so filter associated color to keep RGB weighted by coverage throughout the kernel. + // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); @@ -176,9 +216,8 @@ public void Invoke(int y, Span span) // Get the precalculated source sample row for this kernel row and copy to our buffer. int offsetY = Unsafe.Add(ref sampleRowBase, kY); Span sourceRow = this.sourcePixels.DangerousGetRowSpan(offsetY).Slice(boundsX, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); - Numerics.Premultiply(sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); for (uint x = 0; x < (uint)sourceBuffer.Length; x++) @@ -195,10 +234,8 @@ public void Invoke(int y, Span span) } } - Numerics.UnPremultiply(targetBuffer); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } - - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 5af4360442..502c344028 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -55,7 +55,7 @@ protected override void BeforeImageApply() /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new Convolution2DProcessor( + using Convolution2DProcessor processor = new( this.Configuration, in this.kernelX, in this.kernelY, diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index cbf893915c..5b62b9b8a3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -58,12 +58,12 @@ protected override void BeforeImageApply() /// protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); // We need a clean copy for each pass to start from using ImageFrame cleanCopy = source.Clone(); - using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[0], true, this.Source, interest)) + using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[0], true, this.Source, interest)) { processor.Apply(source); } @@ -78,12 +78,12 @@ protected override void OnFrameApply(ImageFrame source) { using ImageFrame pass = cleanCopy.Clone(); - using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[i], true, this.Source, interest)) + using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[i], true, this.Source, interest)) { processor.Apply(pass); } - var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest); + RowOperation operation = new(source.PixelBuffer, pass.PixelBuffer, interest); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -122,13 +122,12 @@ public void Invoke(int y) for (nuint x = this.minX; x < this.maxX; x++) { - // Grab the max components of the two pixels + // Compass responses are logical color magnitudes. Comparing associated components would make the edge strength + // depend on the storage alpha representation instead of the filtered colors. 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.FromUnassociatedScaledVector4( + Vector4.Max(currentPassPixel.ToUnassociatedScaledVector4(), currentTargetPixel.ToUnassociatedScaledVector4())); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index c353f46b5f..3139d24bb4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -53,7 +53,7 @@ protected override void BeforeImageApply() /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new ConvolutionProcessor(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle); + using ConvolutionProcessor processor = new(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle); processor.Apply(source); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index 6518375b9e..d762dd336b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -12,24 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution; internal class GaussianBlurProcessor : ImageProcessor where TPixel : unmanaged, IPixel { - /// - /// Initializes a new instance of the class. - /// - /// The configuration which allows altering default behaviour or extending the library. - /// The defining the processor parameters. - /// The source for the current processor instance. - /// The source area to process for the current processor instance. - public GaussianBlurProcessor( - Configuration configuration, - GaussianBlurProcessor definition, - Image source, - Rectangle sourceRectangle) - : base(configuration, source, sourceRectangle) - { - int kernelSize = (definition.Radius * 2) + 1; - this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); - } - /// /// Initializes a new instance of the class. /// @@ -72,7 +54,7 @@ public GaussianBlurProcessor( /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); + using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs index a286201dff..bdb3a4b380 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -12,22 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution; internal class GaussianSharpenProcessor : ImageProcessor where TPixel : unmanaged, IPixel { - /// - /// Initializes a new instance of the class. - /// - /// The configuration which allows altering default behaviour or extending the library. - /// The defining the processor parameters. - /// The source for the current processor instance. - /// The source area to process for the current processor instance. - public GaussianSharpenProcessor( - Configuration configuration, - GaussianSharpenProcessor definition, - Image source, - Rectangle sourceRectangle) - : this(configuration, definition, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat) - { - } - /// /// Initializes a new instance of the class. /// @@ -70,7 +54,7 @@ public GaussianSharpenProcessor( /// protected override void OnFrameApply(ImageFrame source) { - using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); + using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY); processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs index 7653aeaa96..2e42928900 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs @@ -104,7 +104,15 @@ private static void CorrectBorder(Span span, int kernelSize, int min, int m { int affectedSize = (kernelSize >> 1) * kernelSize; ref int spanBase = ref MemoryMarshal.GetReference(span); - if (affectedSize > 0) + if (affectedSize >= span.Length && span.Length > 0) + { + // The bounds are no larger than the kernel radius: every offset can overshoot the + // borders by a full sampling extent or more, which the single-pass head and tail + // corrections below cannot fold back into range (and for bounds strictly smaller + // than the radius they cannot even slice the span). Fold each offset exactly. + CorrectBorderExact(span, min, max, borderMode); + } + else if (affectedSize > 0) { switch (borderMode) { @@ -180,4 +188,58 @@ private static void CorrectBorder(Span span, int kernelSize, int min, int m } } } + + private static void CorrectBorderExact(Span span, int min, int max, BorderWrappingMode borderMode) + { + int extent = max - min + 1; + switch (borderMode) + { + case BorderWrappingMode.Repeat: + Numerics.Clamp(span, min, max); + break; + + case BorderWrappingMode.Mirror: + // Reflection about the half-sample border: ..., min+1, min | min, min+1, ... + int mirrorPeriod = 2 * extent; + for (int i = 0; i < span.Length; i++) + { + int offset = Modulo(span[i] - min, mirrorPeriod); + span[i] = min + (offset < extent ? offset : mirrorPeriod - 1 - offset); + } + + break; + + case BorderWrappingMode.Bounce: + // Reflection about the border sample itself: ..., min+2, min+1 | min, min+1, ... + if (extent == 1) + { + span.Fill(min); + break; + } + + int bouncePeriod = (2 * extent) - 2; + for (int i = 0; i < span.Length; i++) + { + int offset = Modulo(span[i] - min, bouncePeriod); + span[i] = min + (offset < extent ? offset : bouncePeriod - offset); + } + + break; + + case BorderWrappingMode.Wrap: + for (int i = 0; i < span.Length; i++) + { + span[i] = min + Modulo(span[i] - min, extent); + } + + break; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Modulo(int value, int period) + { + int remainder = value % period; + return remainder < 0 ? remainder + period : remainder; + } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs index 0c456eb88e..1dbc666f6f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/EdgeDetectorCompassKernel.cs @@ -152,9 +152,8 @@ public override int GetHashCode() this.NorthEast); internal DenseMatrix[] Flatten() => - new[] - { - this.North, this.NorthWest, this.West, this.SouthWest, + [ + this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast - }; + ]; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs index 7efcbf3a6c..b51ed1819e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/Implementation/LaplacianKernelFactory.cs @@ -19,7 +19,7 @@ public static DenseMatrix CreateKernel(uint length) Guard.MustBeGreaterThanOrEqualTo(length, 3u, nameof(length)); Guard.IsFalse(length % 2 == 0, nameof(length), "The kernel length must be an odd number."); - var kernel = new DenseMatrix((int)length); + DenseMatrix kernel = new((int)length); kernel.Fill(-1); int mid = (int)(length / 2); diff --git a/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs index fe3a29d437..3e75d8b840 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs @@ -29,7 +29,7 @@ protected override void OnFrameApply(ImageFrame source) source.CopyTo(targetPixels); - Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); using KernelSamplingMap map = new(this.Configuration.MemoryAllocator); map.BuildSamplingOffsetMap(kernelSize, kernelSize, interest, this.definition.BorderWrapModeX, this.definition.BorderWrapModeY); diff --git a/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs index ca2cabab55..97ddd3be30 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs @@ -78,7 +78,7 @@ public void Invoke(int y, Span span) int currentYIndex = Unsafe.Add(ref sampleRowBase, (uint)i); Span sourceRow = this.sourcePixels.DangerousGetRowSpan(currentYIndex).Slice(boundsX, boundsWidth); Span sourceVectorRow = sourceVectorBuffer.Slice(i * boundsWidth, boundsWidth); - PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceVectorRow); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceVectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } if (this.preserveAlpha) @@ -130,7 +130,7 @@ public void Invoke(int y, Span span) } Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.configuration, targetBuffer, targetRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs index a680393c8c..12f6e55d51 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; @@ -16,66 +17,62 @@ internal static class BokehBlurKernelDataProvider /// /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new instances /// - private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary Cache = new(); /// /// Gets the kernel scales to adjust the component values in each kernel /// - private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f }; + private static float[] KernelScales { get; } = [1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f]; /// /// Gets the available bokeh blur kernel parameters /// - private static IReadOnlyList KernelComponents { get; } = new[] - { + private static Vector4[][] KernelComponents { get; } = + [ + // 1 component - new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) }, + [new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f)], // 2 components - new[] - { + [ new Vector4(0.886528f, 5.268909f, 0.411259f, -0.548794f), new Vector4(1.960518f, 1.558213f, 0.513282f, 4.56111f) - }, + ], // 3 components - new[] - { + [ new Vector4(2.17649f, 5.043495f, 1.621035f, -2.105439f), new Vector4(1.019306f, 9.027613f, -0.28086f, -0.162882f), new Vector4(2.81511f, 1.597273f, -0.366471f, 10.300301f) - }, + ], // 4 components - new[] - { + [ new Vector4(4.338459f, 1.553635f, -5.767909f, 46.164397f), new Vector4(3.839993f, 4.693183f, 9.795391f, -15.227561f), new Vector4(2.791880f, 8.178137f, -3.048324f, 0.302959f), new Vector4(1.342190f, 12.328289f, 0.010001f, 0.244650f) - }, + ], // 5 components - new[] - { + [ new Vector4(4.892608f, 1.685979f, -22.356787f, 85.91246f), new Vector4(4.71187f, 4.998496f, 35.918936f, -28.875618f), new Vector4(4.052795f, 8.244168f, -13.212253f, -1.578428f), new Vector4(2.929212f, 11.900859f, 0.507991f, 1.816328f), new Vector4(1.512961f, 16.116382f, 0.138051f, -0.01f) - }, + ], // 6 components - new[] - { + [ new Vector4(5.143778f, 2.079813f, -82.326596f, 111.231024f), new Vector4(5.612426f, 6.153387f, 113.878661f, 58.004879f), new Vector4(5.982921f, 9.802895f, 39.479083f, -162.028887f), new Vector4(6.505167f, 11.059237f, -71.286026f, 95.027069f), new Vector4(3.869579f, 14.81052f, 1.405746f, -3.704914f), new Vector4(2.201904f, 19.032909f, -0.152784f, -0.107988f) - } - }; + ] + ]; /// /// Gets the bokeh blur kernel data for the specified parameters. @@ -90,7 +87,7 @@ public static BokehBlurKernelData GetBokehBlurKernelData( int componentsCount) { // Reuse the initialized values from the cache, if possible - var parameters = new BokehBlurParameters(radius, componentsCount); + BokehBlurParameters parameters = new(radius, componentsCount); if (!Cache.TryGetValue(parameters, out BokehBlurKernelData info)) { // Initialize the complex kernels and parameters with the current arguments @@ -112,7 +109,7 @@ public static BokehBlurKernelData GetBokehBlurKernelData( private static (Vector4[] Parameters, float Scale) GetParameters(int componentsCount) { // Prepare the kernel components - int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Count)); + int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Length)); return (KernelComponents[index], KernelScales[index]); } @@ -130,7 +127,7 @@ private static Complex64[][] CreateComplexKernels( int kernelSize, float kernelsScale) { - var kernels = new Complex64[kernelParameters.Length][]; + Complex64[][] kernels = new Complex64[kernelParameters.Length][]; ref Vector4 baseRef = ref MemoryMarshal.GetReference(kernelParameters.AsSpan()); for (int i = 0; i < kernelParameters.Length; i++) { @@ -156,7 +153,7 @@ private static Complex64[] CreateComplex1DKernel( float a, float b) { - var kernel = new Complex64[kernelSize]; + Complex64[] kernel = new Complex64[kernelSize]; ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel.AsSpan()); int r = radius, n = -r; @@ -213,13 +210,11 @@ private static void NormalizeKernels(Complex64[][] kernels, Vector4[] kernelPara for (int i = 0; i < kernelsSpan.Length; i++) { ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, (uint)i); - int length = kernelsRef.Length; - ref Complex64 valueRef = ref MemoryMarshal.GetArrayDataReference(kernelsRef); - for (int j = 0; j < length; j++) - { - Unsafe.Add(ref valueRef, (uint)j) *= scalar; - } + // Complex64 stores each value as adjacent real and imaginary floats. Multiplying that flattened + // float span scales both parts independently, which is exactly complex multiplication by a real scalar. + Span values = MemoryMarshal.Cast(kernelsRef.AsSpan()); + TensorPrimitives_.Multiply(values, scalar, values); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErroDither.KnownTypes.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.KnownTypes.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Dithering/ErroDither.KnownTypes.cs rename to src/ImageSharp/Processing/Processors/Dithering/ErrorDither.KnownTypes.cs diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index 3c56d0243b..36cc5564a9 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -143,7 +143,7 @@ public void ApplyPaletteDither( 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; } @@ -168,8 +168,8 @@ internal TPixel Dither( return transformed; } - // Calculate the error - Vector4 error = (source.ToVector4() - transformed.ToVector4()) * scale; + // Palette error is a logical color difference, so it must be diffused independently of the pixel storage representation. + Vector4 error = (source.ToUnassociatedScaledVector4() - transformed.ToUnassociatedScaledVector4()) * scale; int offset = this.offset; DenseMatrix matrix = this.matrix; @@ -200,10 +200,19 @@ internal TPixel Dither( } ref TPixel pixel = ref rowSpan[targetX]; - Vector4 result = pixel.ToVector4(); + Vector4 result = pixel.ToUnassociatedScaledVector4(); + + // Do not diffuse error into fully transparent pixels. They carry no visible color + // (a decoder shows whatever is behind them), so perturbing them is meaningless and, + // for indexed transparency, nudges them off the exact transparent color so they are + // matched to the nearest opaque palette entry instead of being kept transparent. + if (result.W <= 0) + { + continue; + } result += error * coefficient; - pixel.FromVector4(result); + pixel = TPixel.FromUnassociatedScaledVector4(result); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs index ac2921b98d..3217601270 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs @@ -21,7 +21,7 @@ public interface IDither /// The source image. /// The destination quantized frame. /// The region of interest bounds. - void ApplyQuantizationDither( + public void ApplyQuantizationDither( ref TFrameQuantizer quantizer, ImageFrame source, IndexedImageFrame destination, @@ -38,7 +38,7 @@ void ApplyQuantizationDither( /// The palette dithering processor. /// The source image. /// The region of interest bounds. - void ApplyPaletteDither( + public void ApplyPaletteDither( in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) diff --git a/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs index e406d82c69..347e2f0ef6 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs @@ -15,22 +15,22 @@ public interface IPaletteDitherImageProcessor /// /// Gets the configuration instance to use when performing operations. /// - Configuration Configuration { get; } + public Configuration Configuration { get; } /// /// Gets the dithering palette. /// - ReadOnlyMemory Palette { get; } + public ReadOnlyMemory Palette { get; } /// /// Gets the dithering scale used to adjust the amount of dither. Range 0..1. /// - float DitherScale { get; } + public float DitherScale { get; } /// /// Returns the color from the dithering palette corresponding to the given color. /// /// The color to match. /// The match. - TPixel GetPaletteColor(TPixel color); + public TPixel GetPaletteColor(TPixel color); } diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index 1a02e7a221..cec4874f67 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -32,7 +32,7 @@ public OrderedDither(uint length) // We don't want to adjust the original matrix generation code as that // creates known, easy to test values. // https://en.wikipedia.org/wiki/Ordered_dithering#Algorithm - var thresholdMatrix = new DenseMatrix((int)length); + DenseMatrix thresholdMatrix = new((int)length); float m2 = length * length; for (int y = 0; y < length; y++) { @@ -181,8 +181,17 @@ internal TPixel Dither( float scale) where TPixel : unmanaged, IPixel { - Unsafe.SkipInit(out Rgba32 rgba); - source.ToRgba32(ref rgba); + Rgba32 rgba = source.ToRgba32(); + + // Leave fully transparent pixels untouched. They carry no visible color (a decoder shows + // whatever is behind them), so perturbing them is meaningless and, for indexed transparency, + // nudges them off the exact transparent color so they are matched to the nearest opaque + // palette entry instead of being kept transparent. + if (rgba.A == 0) + { + return source; + } + Unsafe.SkipInit(out Rgba32 attempt); float factor = spread * this.thresholdMatrix[y % this.modulusY, x % this.modulusX] * scale; @@ -192,10 +201,7 @@ internal TPixel Dither( attempt.B = (byte)Numerics.Clamp(rgba.B + factor, byte.MinValue, byte.MaxValue); attempt.A = (byte)Numerics.Clamp(rgba.A + factor, byte.MinValue, byte.MaxValue); - TPixel result = default; - result.FromRgba32(attempt); - - return result; + return TPixel.FromRgba32(attempt); } /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs index 7f8b347243..ec82f01d7a 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs @@ -29,7 +29,7 @@ public static DenseMatrix CreateDitherMatrix(uint length) while (length > bayerLength); // Create our Bayer matrix that matches the given exponent and dimensions - var matrix = new DenseMatrix((int)length); + DenseMatrix matrix = new((int)length); uint i = 0; for (int y = 0; y < length; y++) { diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 982cc7d46c..0d4680e21f 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -46,7 +46,7 @@ public PaletteDitherProcessor(Configuration configuration, PaletteDitherProcesso /// protected override void OnFrameApply(ImageFrame source) { - Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); this.dither.ApplyPaletteDither(in this.ditherProcessor, source, interest); } @@ -80,7 +80,7 @@ protected override void Dispose(bool disposing) Justification = "https://github.com/dotnet/roslyn-analyzers/issues/6151")] internal readonly struct DitherProcessor : IPaletteDitherImageProcessor, IDisposable { - private readonly EuclideanPixelMap pixelMap; + private readonly PixelMap pixelMap; [MethodImpl(InliningOptions.ShortMethod)] public DitherProcessor( @@ -89,7 +89,7 @@ public DitherProcessor( float ditherScale) { this.Configuration = configuration; - this.pixelMap = new EuclideanPixelMap(configuration, palette); + this.pixelMap = PixelMapFactory.Create(configuration, palette, ColorMatchingMode.Coarse); this.Palette = palette; this.DitherScale = ditherScale; } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs index 6ecf16fc6b..675d1a3119 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -19,13 +19,15 @@ public class DrawImageProcessor : IImageProcessor /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. + /// The number of times the foreground frames are allowed to loop. 0 means infinitely. public DrawImageProcessor( Image foreground, Point backgroundLocation, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, - float opacity) - : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity) + float opacity, + int foregroundRepeatCount) + : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, foregroundRepeatCount) { } @@ -38,13 +40,15 @@ public DrawImageProcessor( /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. + /// The number of times the foreground frames are allowed to loop. 0 means infinitely. public DrawImageProcessor( Image foreground, Point backgroundLocation, Rectangle foregroundRectangle, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, - float opacity) + float opacity, + int foregroundRepeatCount) { this.ForeGround = foreground; this.BackgroundLocation = backgroundLocation; @@ -52,6 +56,7 @@ public DrawImageProcessor( this.ColorBlendingMode = colorBlendingMode; this.AlphaCompositionMode = alphaCompositionMode; this.Opacity = opacity; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -84,6 +89,11 @@ public DrawImageProcessor( /// public float Opacity { get; } + /// + /// Gets the number of times the foreground frames are allowed to loop. 0 means infinitely. + /// + public int ForegroundRepeatCount { get; } + /// public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixelBg : unmanaged, IPixel @@ -122,6 +132,7 @@ public void Visit(Image image) this.definition.ForegroundRectangle, this.definition.ColorBlendingMode, this.definition.AlphaCompositionMode, - this.definition.Opacity); + this.definition.Opacity, + this.definition.ForegroundRepeatCount); } } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index d5499d5865..f2faee4f9c 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -17,6 +18,12 @@ internal class DrawImageProcessor : ImageProcessor where TPixelBg : unmanaged, IPixel where TPixelFg : unmanaged, IPixel { + /// + /// Counts how many times has been called for this processor instance. + /// Used to select the current foreground frame. + /// + private int foregroundFrameCounter; + /// /// Initializes a new instance of the class. /// @@ -28,6 +35,10 @@ internal class DrawImageProcessor : ImageProcessor /// The blending mode to use when drawing the image. /// The alpha blending mode to use when drawing the image. /// The opacity of the image to blend. Must be between 0 and 1. + /// + /// The number of times the foreground frames are allowed to loop while applying this processor across successive frames. + /// A value of 0 means loop indefinitely. + /// public DrawImageProcessor( Configuration configuration, Image foregroundImage, @@ -36,9 +47,11 @@ public DrawImageProcessor( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, - float opacity) + float opacity, + int foregroundRepeatCount) : base(configuration, backgroundImage, backgroundImage.Bounds) { + Guard.MustBeGreaterThanOrEqualTo(foregroundRepeatCount, 0, nameof(foregroundRepeatCount)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.ForegroundImage = foregroundImage; @@ -46,6 +59,7 @@ public DrawImageProcessor( this.Opacity = opacity; this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.BackgroundLocation = backgroundLocation; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -73,6 +87,12 @@ public DrawImageProcessor( /// public Point BackgroundLocation { get; } + /// + /// Gets the number of times the foreground frames are allowed to loop while applying this processor across + /// successive frames. A value of 0 means loop indefinitely. + /// + public int ForegroundRepeatCount { get; } + /// protected override void OnFrameApply(ImageFrame source) { @@ -112,29 +132,37 @@ protected override void OnFrameApply(ImageFrame source) } // Sanitize the dimensions so that we don't try and sample outside the image. - Rectangle backgroundRectangle = Rectangle.Intersect(new(left, top, width, height), this.SourceRectangle); + Rectangle backgroundRectangle = Rectangle.Intersect(new Rectangle(left, top, width, height), this.SourceRectangle); Configuration configuration = this.Configuration; + int currentFrameIndex = this.foregroundFrameCounter % this.ForegroundImage.Frames.Count; - DrawImageProcessor.RowOperation operation = + RowOperation operation = new( configuration, source.PixelBuffer, - this.ForegroundImage.Frames.RootFrame.PixelBuffer, + this.ForegroundImage.Frames[currentFrameIndex].PixelBuffer, backgroundRectangle, foregroundRectangle, this.Blender, this.Opacity); - ParallelRowIterator.IterateRows( + ParallelRowIterator.IterateRows( configuration, - new(0, 0, foregroundRectangle.Width, foregroundRectangle.Height), + new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height), in operation); + + // The repeat count only affects how the foreground frame advances across successive background frames. + // When exhausted, the selected foreground frame stops advancing. + if (this.ForegroundRepeatCount is 0 || this.foregroundFrameCounter / this.ForegroundImage.Frames.Count < this.ForegroundRepeatCount) + { + this.foregroundFrameCounter++; + } } /// /// A implementing the draw logic for . /// - private readonly struct RowOperation : IRowOperation + private readonly struct RowOperation : IRowOperation { private readonly Buffer2D background; private readonly Buffer2D foreground; @@ -163,13 +191,20 @@ public RowOperation( this.opacity = opacity; } + /// + public int GetRequiredBufferLength(Rectangle bounds) + + // By using a dedicated vector span we can avoid per-row pool allocations in PixelBlender.Blend + // We need 3 Vector4 values per pixel to store the background, foreground, and result pixels for blending. + => 3 * bounds.Width; + /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public void Invoke(int y, Span span) { Span background = this.background.DangerousGetRowSpan(y + this.backgroundRectangle.Top).Slice(this.backgroundRectangle.Left, this.backgroundRectangle.Width); Span foreground = this.foreground.DangerousGetRowSpan(y + this.foregroundRectangle.Top).Slice(this.foregroundRectangle.Left, this.foregroundRectangle.Width); - this.blender.Blend(this.configuration, background, background, foreground, this.opacity); + this.blender.Blend(this.configuration, background, background, foreground, this.opacity, span); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 1491fe073b..ddb6d15457 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -38,7 +38,7 @@ protected override void OnFrameApply(ImageFrame source) int levels = Math.Clamp(this.definition.Levels, 1, 255); int brushSize = Math.Clamp(this.definition.BrushSize, 1, Math.Min(source.Width, source.Height)); - using Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()); + using Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size); source.CopyTo(targetPixels); @@ -119,13 +119,15 @@ public void Invoke(in RowInterval rows) Span redBinSpan = binsSpan[this.levels..]; Span blueBinSpan = redBinSpan[this.levels..]; Span greenBinSpan = blueBinSpan[this.levels..]; + PixelOperations pixelOperations = PixelOperations.Instance; for (int y = rows.Min; y < rows.Max; y++) { Span sourceRowPixelSpan = this.source.DangerousGetRowSpan(y); Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(this.bounds.X, this.bounds.Width); - PixelOperations.Instance.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span, PixelConversionModifiers.Scale); + // Intensity bins and channel averages describe logical colors, so they must not depend on the pixel storage association. + pixelOperations.ToVector4(this.configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (int x = this.bounds.X; x < this.bounds.Right; x++) { @@ -149,7 +151,7 @@ public void Invoke(in RowInterval rows) int offsetX = x + fxr; offsetX = Numerics.Clamp(offsetX, 0, maxX); - Vector4 vector = sourceOffsetRow[offsetX].ToScaledVector4(); + Vector4 vector = sourceOffsetRow[offsetX].ToUnassociatedScaledVector4(); float sourceRed = vector.X; float sourceBlue = vector.Z; @@ -180,7 +182,7 @@ public void Invoke(in RowInterval rows) Span targetRowAreaPixelSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - PixelOperations.Instance.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan, PixelConversionModifiers.Scale); + pixelOperations.FromVector4Destructive(this.configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs index 847a211a5a..06cfa49b3d 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs @@ -36,14 +36,12 @@ public PixelRowDelegateProcessor(PixelRowOperation pixelRowOperation, PixelConve /// public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel - { - return new PixelRowDelegateProcessor( + => new PixelRowDelegateProcessor( new PixelRowDelegate(this.PixelRowOperation), configuration, this.Modifiers, source, sourceRectangle); - } /// /// A implementing the row processing logic for . @@ -54,9 +52,7 @@ public IImageProcessor CreatePixelSpecificProcessor(Configuratio [MethodImpl(InliningOptions.ShortMethod)] public PixelRowDelegate(PixelRowOperation pixelRowOperation) - { - this.pixelRowOperation = pixelRowOperation; - } + => this.pixelRowOperation = pixelRowOperation; /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index f59b95050e..d38ffc801e 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -48,8 +48,8 @@ public PixelRowDelegateProcessor( /// protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowOperation(interest.X, source.PixelBuffer, this.Configuration, this.modifiers, this.rowDelegate); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); + RowOperation operation = new(interest.X, source.PixelBuffer, this.Configuration, this.modifiers, this.rowDelegate); ParallelRowIterator.IterateRows( this.Configuration, @@ -96,7 +96,7 @@ public void Invoke(int y, Span span) 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/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs index 68000ba3e8..c828b95b62 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs @@ -32,7 +32,7 @@ public PixelateProcessor(Configuration configuration, PixelateProcessor definiti /// protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); int size = this.Size; Guard.MustBeBetweenOrEqualTo(size, 0, interest.Width, nameof(size)); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 5ad245e3ce..8b287b727c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -27,15 +27,13 @@ internal class FilterProcessor : ImageProcessor /// The source area to process for the current processor instance. public FilterProcessor(Configuration configuration, FilterProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) - { - this.definition = definition; - } + => this.definition = definition; /// protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); - var operation = new RowOperation(interest.X, source.PixelBuffer, this.definition.Matrix, this.Configuration); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); + RowOperation operation = new(interest.X, source.PixelBuffer, this.definition.Matrix, this.Configuration); ParallelRowIterator.IterateRows( this.Configuration, @@ -76,11 +74,14 @@ public int GetRequiredBufferLength(Rectangle bounds) public void Invoke(int y, Span span) { Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale); + PixelOperations pixelOperations = PixelOperations.Instance; + + // Color matrices transform logical color components, so associated storage must be unassociated before applying the matrix. + pixelOperations.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); - 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); + pixelOperations.FromVector4Destructive(this.configuration, span, rowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 4a02642fd6..c8a381d590 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/OpaqueProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs index 93e600106a..bffe6cf631 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs @@ -23,9 +23,9 @@ public OpaqueProcessor( protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); - var operation = new OpaqueRowOperation(this.Configuration, source.PixelBuffer, interest); + OpaqueRowOperation operation = new(this.Configuration, source.PixelBuffer, interest); ParallelRowIterator.IterateRows(this.Configuration, interest, in operation); } @@ -56,7 +56,10 @@ public int GetRequiredBufferLength(Rectangle bounds) public void Invoke(int y, Span span) { Span targetRowSpan = this.target.DangerousGetRowSpan(y)[this.bounds.X..]; - PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Scale); + PixelOperations pixelOperations = PixelOperations.Instance; + + // Opacity changes alpha without changing logical color, so associated storage must be unassociated before replacing alpha. + pixelOperations.ToVector4(this.configuration, targetRowSpan[..span.Length], span, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); ref Vector4 baseRef = ref MemoryMarshal.GetReference(span); for (int x = 0; x < this.bounds.Width; x++) @@ -65,7 +68,7 @@ public void Invoke(int y, Span span) v.W = 1F; } - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan, PixelConversionModifiers.Scale); + pixelOperations.FromVector4Destructive(this.configuration, span, targetRowSpan[..span.Length], PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index c1b79d10a1..84c0364faf 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/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 2fa79220e5..3510fe7a8e 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors; @@ -95,19 +96,17 @@ protected virtual void BeforeFrameApply(ImageFrame source) protected abstract void OnFrameApply(ImageFrame source); /// - /// This method is called after the process is applied to prepare the processor. + /// This method is called after the process is applied to each frame. /// /// The source image. Cannot be null. protected virtual void AfterFrameApply(ImageFrame source) - { - } + => source.Metadata.AfterFrameApply(source, source, Matrix4x4.Identity); /// - /// This method is called after the process is applied to prepare the processor. + /// This method is called after the process is applied to the complete image. /// protected virtual void AfterImageApply() - { - } + => this.Source.Metadata.AfterImageApply(this.Source, Matrix4x4.Identity); /// /// Disposes the object and frees resources for the Garbage Collector. diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 0baa87a611..b18a52bec2 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -64,11 +64,11 @@ protected override void OnFrameApply(ImageFrame source) 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 @@ protected override void OnFrameApply(ImageFrame source) 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), @@ -144,8 +144,11 @@ private static void ProcessCornerTile( for (int dx = xStart; dx < xEnd; dx++) { 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)); + Vector4 vector = pixel.ToUnassociatedScaledVector4(); + int luminance = ColorNumerics.GetBT709Luminance(vector, luminanceLevels); + float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, luminance); + vector.X = vector.Y = vector.Z = luminanceEqualized; + pixel = TPixel.FromUnassociatedScaledVector4(vector); } } } @@ -190,8 +193,10 @@ private static void ProcessBorderColumn( for (int dx = xStart; dx < xEnd; dx++) { 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)); + Vector4 vector = pixel.ToUnassociatedScaledVector4(); + float luminanceEqualized = InterpolateBetweenTwoTiles(vector, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); + vector.X = vector.Y = vector.Z = luminanceEqualized; + pixel = TPixel.FromUnassociatedScaledVector4(vector); } tileY++; @@ -242,8 +247,10 @@ private static void ProcessBorderRow( for (int dx = x; dx < xLimit; dx++) { 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)); + Vector4 vector = pixel.ToUnassociatedScaledVector4(); + float luminanceEqualized = InterpolateBetweenTwoTiles(vector, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); + vector.X = vector.Y = vector.Z = luminanceEqualized; + pixel = TPixel.FromUnassociatedScaledVector4(vector); tileX++; } } @@ -256,7 +263,7 @@ private static void ProcessBorderRow( /// /// Bilinear interpolation between four adjacent tiles. /// - /// The pixel to remap the grey value from. + /// The unassociated scaled vector to remap the grey value from. /// The pre-computed lookup tables to remap the grey values for each tiles. /// The number of tiles in the x-direction. /// The number of tiles in the y-direction. @@ -273,7 +280,7 @@ private static void ProcessBorderRow( /// A re-mapped grey value. [MethodImpl(InliningOptions.ShortMethod)] private static float InterpolateBetweenFourTiles( - TPixel sourcePixel, + Vector4 sourceVector, CdfTileData cdfData, int tileCountX, int tileCountY, @@ -285,7 +292,7 @@ private static float InterpolateBetweenFourTiles( int tileHeight, int luminanceLevels) { - int luminance = GetLuminance(sourcePixel, luminanceLevels); + int luminance = ColorNumerics.GetBT709Luminance(sourceVector, luminanceLevels); float tx = tileX / (float)(tileWidth - 1); float ty = tileY / (float)(tileHeight - 1); @@ -304,7 +311,7 @@ private static float InterpolateBetweenFourTiles( /// /// Linear interpolation between two tiles. /// - /// The pixel to remap the grey value from. + /// The unassociated scaled vector to remap the grey value from. /// The CDF lookup map. /// X position inside the first tile. /// Y position inside the first tile. @@ -319,7 +326,7 @@ private static float InterpolateBetweenFourTiles( /// A re-mapped grey value. [MethodImpl(InliningOptions.ShortMethod)] private static float InterpolateBetweenTwoTiles( - TPixel sourcePixel, + Vector4 sourceVector, CdfTileData cdfData, int tileX1, int tileY1, @@ -329,7 +336,7 @@ private static float InterpolateBetweenTwoTiles( int tileWidth, int luminanceLevels) { - int luminance = GetLuminance(sourcePixel, luminanceLevels); + int luminance = ColorNumerics.GetBT709Luminance(sourceVector, luminanceLevels); float tx = tilePos / (float)(tileWidth - 1); float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); @@ -404,10 +411,7 @@ public void Invoke(in RowInterval rows) { 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++) @@ -422,20 +426,22 @@ public void Invoke(in RowInterval rows) for (int dx = x; dx < xEnd; dx++) { ref TPixel pixel = ref rowSpan[dx]; + Vector4 vector = pixel.ToUnassociatedScaledVector4(); float luminanceEqualized = InterpolateBetweenFourTiles( - pixel, + vector, this.cdfData, this.tileCount, this.tileCount, tileX, tileY, cdfX, - cdfYY, + cdfY, this.tileWidth, this.tileHeight, this.luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + vector.X = vector.Y = vector.Z = luminanceEqualized; + pixel = TPixel.FromUnassociatedScaledVector4(vector); tileX++; } @@ -494,7 +500,7 @@ public CdfTileData( 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 +511,7 @@ public CdfTileData( 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 d2bec0b49b..624820d2f2 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -194,7 +194,7 @@ private static void CopyPixelRow( int idx = 0; for (int dx = x; dx < x + tileWidth; dx++) { - rowPixels[idx] = source[Numerics.Abs(dx), y].ToVector4(); + rowPixels[idx] = source[Numerics.Abs(dx), y].ToUnassociatedScaledVector4(); idx++; } @@ -209,11 +209,11 @@ private static void CopyPixelRow( if (dx >= source.Width) { int diff = dx - source.Width; - rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); + rowPixels[idx] = source[dx - diff - 1, y].ToUnassociatedScaledVector4(); } else { - rowPixels[idx] = source[dx, y].ToVector4(); + rowPixels[idx] = source[dx, y].ToUnassociatedScaledVector4(); } idx++; @@ -242,7 +242,7 @@ private static void CopyPixelRowFast( int y, int tileWidth, Configuration configuration) - => PixelOperations.Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); + => PixelOperations.Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(start: x, length: tileWidth), rowPixels, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); /// /// Adds a column of grey values to the histogram. @@ -256,7 +256,7 @@ private static void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int his { for (nuint idx = 0; idx < (uint)length; idx++) { - int luminance = ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + int luminance = ColorNumerics.GetBT709Luminance(Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); Unsafe.Add(ref histogramBase, (uint)luminance)++; } } @@ -273,7 +273,7 @@ private static void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref in { for (nuint idx = 0; idx < (uint)length; idx++) { - int luminance = ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + int luminance = ColorNumerics.GetBT709Luminance(Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); Unsafe.Add(ref histogramBase, (uint)luminance)--; } } @@ -381,9 +381,11 @@ public void Invoke(int x) float numberOfPixelsMinusCdfMin = this.swInfos.PixelInTile - cdfMin; // Map the current pixel to the new equalized value. - int luminance = GetLuminance(this.source[x, y], this.processor.LuminanceLevels); + Vector4 vector = this.source[x, y].ToUnassociatedScaledVector4(); + int luminance = ColorNumerics.GetBT709Luminance(vector, 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)); + vector.X = vector.Y = vector.Z = luminanceEqualized; + this.targetPixels[x, y] = TPixel.FromUnassociatedScaledVector4(vector); // Remove top most row from the histogram, mirroring rows which exceeds the borders. if (this.useFastPath) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs index 6f4493f951..9acca6d409 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs @@ -28,9 +28,9 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor /// Indicating whether to clip the histogram bins at a specific value. /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + /// Whether to apply a synchronized luminance value to each color channel. /// The source for the current processor instance. /// The source area to process for the current processor instance. - /// Whether to apply a synchronized luminance value to each color channel. public AutoLevelProcessor( Configuration configuration, int luminanceLevels, @@ -40,9 +40,7 @@ public AutoLevelProcessor( Image source, Rectangle sourceRectangle) : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) - { - this.SyncChannels = syncChannels; - } + => this.SyncChannels = syncChannels; /// /// Gets a value indicating whether to apply a synchronized luminance value to each color channel. @@ -54,12 +52,12 @@ protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels. - var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); + GrayscaleLevelsRowOperation grayscaleOperation = new(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); ParallelRowIterator.IterateRows, Vector4>( this.Configuration, interest, @@ -83,7 +81,7 @@ ref MemoryMarshal.GetReference(histogram), if (this.SyncChannels) { - var cdfOperation = new SynchronizedChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); + SynchronizedChannelsRowOperation cdfOperation = new(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -91,7 +89,7 @@ ref MemoryMarshal.GetReference(histogram), } else { - var cdfOperation = new SeperateChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); + SeperateChannelsRowOperation cdfOperation = new(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -136,27 +134,27 @@ public SynchronizedChannelsRowOperation( [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y, Span span) { - Span vectorBuffer = span.Slice(0, this.bounds.Width); + Span vectorBuffer = span[..this.bounds.Width]; ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); - var sourceAccess = new PixelAccessor(this.source); + PixelAccessor sourceAccess = new(this.source); int levels = this.luminanceLevels; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; Span pixelRow = sourceAccess.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer); + PixelOperations operations = PixelOperations.Instance; + operations.ToVector4(this.configuration, pixelRow, vectorBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (int x = 0; x < this.bounds.Width; x++) { - var vector = Unsafe.Add(ref vectorRef, (uint)x); - int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); + Vector4 vector = Unsafe.Add(ref vectorRef, (uint)x); + int luminance = ColorNumerics.GetBT709Luminance(vector, levels); float scaledLuminance = Unsafe.Add(ref cdfBase, (uint)luminance) / noOfPixelsMinusCdfMin; float scalingFactor = scaledLuminance * levels / luminance; - Vector4 scaledVector = new Vector4(scalingFactor * vector.X, scalingFactor * vector.Y, scalingFactor * vector.Z, vector.W); - Unsafe.Add(ref vectorRef, (uint)x) = scaledVector; + Unsafe.Add(ref vectorRef, (uint)x) = new Vector4(scalingFactor * vector.X, scalingFactor * vector.Y, scalingFactor * vector.Z, vector.W); } - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); + operations.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } @@ -197,19 +195,24 @@ public SeperateChannelsRowOperation( [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y, Span span) { - Span vectorBuffer = span.Slice(0, this.bounds.Width); + Span vectorBuffer = span[..this.bounds.Width]; ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); - var sourceAccess = new PixelAccessor(this.source); + PixelAccessor sourceAccess = new(this.source); int levelsMinusOne = this.luminanceLevels - 1; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; - Span pixelRow = sourceAccess.GetRowSpan(y); - PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer); + Span pixelRow = sourceAccess.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + PixelOperations operations = PixelOperations.Instance; + operations.ToVector4(this.configuration, pixelRow, vectorBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (int x = 0; x < this.bounds.Width; x++) { - var vector = Unsafe.Add(ref vectorRef, (uint)x) * levelsMinusOne; + Vector4 vector = Unsafe.Add(ref vectorRef, (uint)x); + float alpha = vector.W; + + // Alpha is not a histogram component and must not be scaled into the CDF index range. + vector *= levelsMinusOne; uint originalX = (uint)MathF.Round(vector.X); float scaledX = Unsafe.Add(ref cdfBase, originalX) / noOfPixelsMinusCdfMin; @@ -217,10 +220,10 @@ public void Invoke(int y, Span span) float scaledY = Unsafe.Add(ref cdfBase, originalY) / noOfPixelsMinusCdfMin; uint originalZ = (uint)MathF.Round(vector.Z); float scaledZ = Unsafe.Add(ref cdfBase, originalZ) / noOfPixelsMinusCdfMin; - Unsafe.Add(ref vectorRef, (uint)x) = new Vector4(scaledX, scaledY, scaledZ, vector.W); + Unsafe.Add(ref vectorRef, (uint)x) = new Vector4(scaledX, scaledY, scaledZ, alpha); } - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); + operations.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index e7433899bf..3c07d05c6b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -46,12 +46,12 @@ protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels. - var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); + GrayscaleLevelsRowOperation grayscaleOperation = new(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); ParallelRowIterator.IterateRows, Vector4>( this.Configuration, interest, @@ -74,7 +74,7 @@ ref MemoryMarshal.GetReference(histogram), float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; // Apply the cdf to each pixel of the image - var cdfOperation = new CdfApplicationRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); + CdfApplicationRowOperation cdfOperation = new(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); ParallelRowIterator.IterateRows( this.Configuration, interest, @@ -118,24 +118,25 @@ public CdfApplicationRowOperation( [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y, Span span) { - Span vectorBuffer = span.Slice(0, this.bounds.Width); + Span vectorBuffer = span[..this.bounds.Width]; ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); int levels = this.luminanceLevels; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; - Span pixelRow = this.source.DangerousGetRowSpan(y); - PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer); + Span pixelRow = this.source.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + PixelOperations operations = PixelOperations.Instance; + operations.ToVector4(this.configuration, pixelRow, vectorBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (int x = 0; x < this.bounds.Width; x++) { - var vector = Unsafe.Add(ref vectorRef, (uint)x); - int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); + Vector4 vector = Unsafe.Add(ref vectorRef, (uint)x); + int luminance = ColorNumerics.GetBT709Luminance(vector, levels); float luminanceEqualized = Unsafe.Add(ref cdfBase, (uint)luminance) / noOfPixelsMinusCdfMin; Unsafe.Add(ref vectorRef, (uint)x) = new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W); } - PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); + operations.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs index 0a8690ba70..c7e40b420f 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs @@ -51,13 +51,13 @@ public void Invoke(int y, Span span) ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); int levels = this.luminanceLevels; - Span pixelRow = this.source.DangerousGetRowSpan(y); - PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer); + Span pixelRow = this.source.DangerousGetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); for (int x = 0; x < this.bounds.Width; x++) { - var vector = Unsafe.Add(ref vectorRef, (uint)x); - int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); + Vector4 vector = Unsafe.Add(ref vectorRef, (uint)x); + int luminance = ColorNumerics.GetBT709Luminance(vector, levels); Interlocked.Increment(ref Unsafe.Add(ref histogramBase, (uint)luminance)); } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs index 6ac36f3ce0..b624d5b0e8 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Normalization; @@ -115,10 +116,7 @@ public void ClipHistogram(Span histogram, int clipLimit) int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; if (addToEachBin > 0) { - for (nuint i = 0; i < (uint)histogram.Length; i++) - { - Unsafe.Add(ref histogramBase, i) += addToEachBin; - } + TensorPrimitives_.Add(histogram, addToEachBin, histogram); } int residual = sumOverClip - (addToEachBin * this.LuminanceLevels); @@ -142,7 +140,8 @@ public void ClipHistogram(Span histogram, int clipLimit) public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) { // TODO: We need a bulk per span equivalent. - Vector4 vector = sourcePixel.ToVector4(); - return ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels); + // Histogram bins describe logical color, so storage alpha association must not scale luminance. + Vector4 vector = sourcePixel.ToUnassociatedScaledVector4(); + return ColorNumerics.GetBT709Luminance(vector, luminanceLevels); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index 1e43458253..9ed314ee38 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -35,7 +35,7 @@ protected override void OnFrameApply(ImageFrame source) TPixel color = this.definition.Color.ToPixel(); GraphicsOptions graphicsOptions = this.definition.GraphicsOptions; - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); Configuration configuration = this.Configuration; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; @@ -48,7 +48,7 @@ protected override void OnFrameApply(ImageFrame source) PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions); - var operation = new RowOperation(configuration, interest, blender, amount, colors, source.PixelBuffer); + RowOperation operation = new(configuration, interest, blender, amount, colors, source.PixelBuffer); ParallelRowIterator.IterateRows( configuration, interest, diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 19ce6c417d..d73e7bea1c 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -40,7 +40,7 @@ protected override void OnFrameApply(ImageFrame source) TPixel glowColor = this.definition.GlowColor.ToPixel(); float blendPercent = this.definition.GraphicsOptions.BlendPercentage; - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); Vector2 center = Rectangle.Center(interest); float finalRadius = this.definition.Radius.Calculate(interest.Size); @@ -54,7 +54,7 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(glowColor); - var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer); + RowOperation operation = new(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer); ParallelRowIterator.IterateRows( configuration, interest, diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index a327deec1c..b08cd898e8 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -40,7 +40,7 @@ protected override void OnFrameApply(ImageFrame source) TPixel vignetteColor = this.definition.VignetteColor.ToPixel(); float blendPercent = this.definition.GraphicsOptions.BlendPercentage; - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds); Vector2 center = Rectangle.Center(interest); float finalRadiusX = this.definition.RadiusX.Calculate(interest.Size); @@ -62,7 +62,7 @@ protected override void OnFrameApply(ImageFrame source) using IMemoryOwner rowColors = allocator.Allocate(interest.Width); rowColors.GetSpan().Fill(vignetteColor); - var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer); + RowOperation operation = new(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source.PixelBuffer); ParallelRowIterator.IterateRows( configuration, interest, diff --git a/src/ImageSharp/Processing/Processors/Quantization/ColorMatchingMode.cs b/src/ImageSharp/Processing/Processors/Quantization/ColorMatchingMode.cs new file mode 100644 index 0000000000..c520d7c54b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/ColorMatchingMode.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Defines the precision level used when matching colors during quantization. +/// +public enum ColorMatchingMode +{ + /// + /// Uses a coarse caching strategy optimized for performance at the expense of exact matches. + /// This provides the fastest matching but may yield approximate results. + /// + Coarse, + + /// + /// Performs exact color matching using a bounded exact-match cache with eviction. + /// This preserves exact color matching while accelerating repeated colors. + /// + Exact +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel,TCache}.cs b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel,TCache}.cs new file mode 100644 index 0000000000..e2e7206e09 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel,TCache}.cs @@ -0,0 +1,260 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Gets the closest color to the supplied color based upon the Euclidean distance. +/// +/// The pixel format. +/// The cache type. +/// +/// This class is not thread safe and should not be accessed in parallel. +/// Doing so will result in non-idempotent results. +/// +internal sealed class EuclideanPixelMap : PixelMap + where TPixel : unmanaged, IPixel + where TCache : struct, IColorIndexCache +{ + private Rgba32[] rgbaPalette; + + // Do not make readonly. It's a mutable struct. +#pragma warning disable IDE0044 // Add readonly modifier + private TCache cache; +#pragma warning restore IDE0044 // Add readonly modifier + + private readonly Configuration configuration; + + /// + /// Initializes a new instance of the class. + /// + /// Specifies the settings and resources for the pixel map's operations. + /// Defines the color palette used for pixel mapping. + public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory palette) + { + this.configuration = configuration; + this.Palette = palette; + this.rgbaPalette = new Rgba32[palette.Length]; + this.cache = TCache.Create(configuration.MemoryAllocator); + PixelOperations.Instance.ToRgba32(configuration, this.Palette.Span, this.rgbaPalette); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetClosestColor(TPixel color, out TPixel match) + { + ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.Palette.Span); + Rgba32 rgba = color.ToRgba32(); + + if (this.cache.TryGetValue(rgba, out short index)) + { + match = Unsafe.Add(ref paletteRef, (ushort)index); + return index; + } + + return this.GetClosestColorSlow(rgba, ref paletteRef, out match); + } + + /// + public override void Clear(ReadOnlyMemory palette) + { + this.Palette = palette; + this.rgbaPalette = new Rgba32[palette.Length]; + PixelOperations.Instance.ToRgba32(this.configuration, this.Palette.Span, this.rgbaPalette); + this.cache.Clear(); + } + + [MethodImpl(InliningOptions.ColdPath)] + private int GetClosestColorSlow(Rgba32 rgba, ref TPixel paletteRef, out TPixel match) + { + ReadOnlySpan rgbaPalette = this.rgbaPalette; + ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPalette); + int index = 0; + int leastDistance = int.MaxValue; + int i = 0; + + if (Vector128.IsHardwareAccelerated && rgbaPalette.Length >= 4) + { + // Duplicate the query color so one 128-bit register can be subtracted from + // two packed RGBA candidates at a time after widening. + Vector128 pixel = Vector128.Create( + rgba.R, + rgba.G, + rgba.B, + rgba.A, + rgba.R, + rgba.G, + rgba.B, + rgba.A); + + int vectorizedLength = rgbaPalette.Length & ~0x03; + + for (; i < vectorizedLength; i += 4) + { + // Load four packed Rgba32 values (16 bytes) and widen them into two vectors: + // [c0.r, c0.g, c0.b, c0.a, c1.r, ...] and [c2.r, c2.g, c2.b, c2.a, c3.r, ...]. + Vector128 packed = Vector128.LoadUnsafe(ref Unsafe.As(ref Unsafe.Add(ref rgbaPaletteRef, i))); + Vector128 lowerDiff = Vector128.WidenLower(packed).AsInt16() - pixel; + Vector128 upperDiff = Vector128.WidenUpper(packed).AsInt16() - pixel; + + // MultiplyAddAdjacent collapses channel squares into RG + BA partial sums, + // so each pair of int lanes still corresponds to one candidate color. + Vector128 lowerPairs = Vector128_.MultiplyAddAdjacent(lowerDiff, lowerDiff); + Vector128 upperPairs = Vector128_.MultiplyAddAdjacent(upperDiff, upperDiff); + + // Sum the two partials for candidates i and i + 1. + ref int lowerRef = ref Unsafe.As, int>(ref lowerPairs); + int distance = lowerRef + Unsafe.Add(ref lowerRef, 1); + if (distance < leastDistance) + { + index = i; + leastDistance = distance; + if (distance == 0) + { + goto Found; + } + } + + distance = Unsafe.Add(ref lowerRef, 2) + Unsafe.Add(ref lowerRef, 3); + if (distance < leastDistance) + { + index = i + 1; + leastDistance = distance; + if (distance == 0) + { + goto Found; + } + } + + // Sum the two partials for candidates i + 2 and i + 3. + ref int upperRef = ref Unsafe.As, int>(ref upperPairs); + distance = upperRef + Unsafe.Add(ref upperRef, 1); + if (distance < leastDistance) + { + index = i + 2; + leastDistance = distance; + if (distance == 0) + { + goto Found; + } + } + + distance = Unsafe.Add(ref upperRef, 2) + Unsafe.Add(ref upperRef, 3); + if (distance < leastDistance) + { + index = i + 3; + leastDistance = distance; + if (distance == 0) + { + goto Found; + } + } + } + } + + for (; i < rgbaPalette.Length; i++) + { + int distance = DistanceSquared(rgba, Unsafe.Add(ref rgbaPaletteRef, i)); + if (distance < leastDistance) + { + index = i; + leastDistance = distance; + if (distance == 0) + { + goto Found; + } + } + } + + Found: + + // Now I have the index, pop it into the cache for next time + _ = this.cache.TryAdd(rgba, (short)index); + match = Unsafe.Add(ref paletteRef, (uint)index); + + return index; + } + + /// + /// Returns the Euclidean distance squared between two specified points. + /// + /// The first point. + /// The second point. + /// The distance squared. + [MethodImpl(InliningOptions.ShortMethod)] + private static int DistanceSquared(Rgba32 a, Rgba32 b) + { + int deltaR = a.R - b.R; + int deltaG = a.G - b.G; + int deltaB = a.B - b.B; + int deltaA = a.A - b.A; + return (deltaR * deltaR) + (deltaG * deltaG) + (deltaB * deltaB) + (deltaA * deltaA); + } + + /// + public override void Dispose() => this.cache.Dispose(); +} + +/// +/// Represents a map of colors to indices. +/// +/// The pixel format. +internal abstract class PixelMap : IDisposable + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the color palette of this . + /// + public ReadOnlyMemory Palette { get; private protected set; } + + /// + /// Returns the closest color in the palette and the index of that pixel. + /// + /// The color to match. + /// The matched color. + /// + /// The index. + /// + public abstract int GetClosestColor(TPixel color, out TPixel match); + + /// + /// Clears the map, resetting it to use the given palette. + /// + /// The color palette to map from. + public abstract void Clear(ReadOnlyMemory palette); + + /// + public abstract void Dispose(); +} + +/// +/// A factory for creating instances. +/// +internal static class PixelMapFactory +{ + /// + /// Creates a new instance. + /// + /// The pixel format. + /// The configuration. + /// The color palette to map from. + /// The color matching mode. + /// + /// The . + /// + public static PixelMap Create( + Configuration configuration, + ReadOnlyMemory palette, + ColorMatchingMode colorMatchingMode) + where TPixel : unmanaged, IPixel => colorMatchingMode switch + { + ColorMatchingMode.Exact => new EuclideanPixelMap(configuration, palette), + _ => new EuclideanPixelMap(configuration, palette), + }; +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs deleted file mode 100644 index 72148374aa..0000000000 --- a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Quantization; - -/// -/// Gets the closest color to the supplied color based upon the Euclidean distance. -/// -/// The pixel format. -/// -/// This class is not thread safe and should not be accessed in parallel. -/// Doing so will result in non-idempotent results. -/// -internal sealed class EuclideanPixelMap : IDisposable - where TPixel : unmanaged, IPixel -{ - private Rgba32[] rgbaPalette; - private int transparentIndex; - private readonly TPixel transparentMatch; - - /// - /// Do not make this readonly! Struct value would be always copied on non-readonly method calls. - /// - private ColorDistanceCache cache; - private readonly Configuration configuration; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The color palette to map from. - public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory palette) - : this(configuration, palette, -1) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The color palette to map from. - /// An explicit index at which to match transparent pixels. - public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory palette, int transparentIndex = -1) - { - this.configuration = configuration; - this.Palette = palette; - this.rgbaPalette = new Rgba32[palette.Length]; - this.cache = new ColorDistanceCache(configuration.MemoryAllocator); - PixelOperations.Instance.ToRgba32(configuration, this.Palette.Span, this.rgbaPalette); - - this.transparentIndex = transparentIndex; - Unsafe.SkipInit(out this.transparentMatch); - this.transparentMatch.FromRgba32(default); - } - - /// - /// Gets the color palette of this . - /// The palette memory is owned by the palette source that created it. - /// - public ReadOnlyMemory Palette { get; private set; } - - /// - /// Returns the closest color in the palette and the index of that pixel. - /// The palette contents must match the one used in the constructor. - /// - /// The color to match. - /// The matched color. - /// The index. - [MethodImpl(InliningOptions.ShortMethod)] - 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); - - // Check if the color is in the lookup table - if (!this.cache.TryGetValue(rgba, out short index)) - { - return this.GetClosestColorSlow(rgba, ref paletteRef, out match); - } - - match = Unsafe.Add(ref paletteRef, (ushort)index); - return index; - } - - /// - /// Clears the map, resetting it to use the given palette. - /// - /// The color palette to map from. - public void Clear(ReadOnlyMemory palette) - { - this.Palette = palette; - this.rgbaPalette = new Rgba32[palette.Length]; - PixelOperations.Instance.ToRgba32(this.configuration, this.Palette.Span, this.rgbaPalette); - this.transparentIndex = -1; - this.cache.Clear(); - } - - /// - /// Allows setting the transparent index after construction. - /// - /// An explicit index at which to match transparent pixels. - public void SetTransparentIndex(int index) - { - if (index != this.transparentIndex) - { - this.cache.Clear(); - } - - this.transparentIndex = index; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private int GetClosestColorSlow(Rgba32 rgba, ref TPixel paletteRef, out TPixel match) - { - // Loop through the palette and find the nearest match. - int index = 0; - - if (this.transparentIndex >= 0 && rgba == default) - { - // We have explicit instructions. No need to search. - index = this.transparentIndex; - this.cache.Add(rgba, (byte)index); - match = this.transparentMatch; - return index; - } - - float leastDistance = float.MaxValue; - for (int i = 0; i < this.rgbaPalette.Length; i++) - { - Rgba32 candidate = this.rgbaPalette[i]; - float distance = DistanceSquared(rgba, candidate); - - // If it's an exact match, exit the loop - if (distance == 0) - { - index = i; - break; - } - - if (distance < leastDistance) - { - // Less than... assign. - index = i; - leastDistance = distance; - } - } - - // Now I have the index, pop it into the cache for next time - this.cache.Add(rgba, (byte)index); - match = Unsafe.Add(ref paletteRef, (uint)index); - return index; - } - - /// - /// Returns the Euclidean distance squared between two specified points. - /// - /// The first point. - /// The second point. - /// The distance squared. - [MethodImpl(InliningOptions.ShortMethod)] - private static float DistanceSquared(Rgba32 a, Rgba32 b) - { - float deltaR = a.R - b.R; - float deltaG = a.G - b.G; - float deltaB = a.B - b.B; - float deltaA = a.A - b.A; - return (deltaR * deltaR) + (deltaG * deltaG) + (deltaB * deltaB) + (deltaA * deltaA); - } - - public void Dispose() => this.cache.Dispose(); - - /// - /// A cache for storing color distance matching results. - /// - /// - /// - /// The granularity of the cache has been determined based upon the current - /// suite of test images and provides the lowest possible memory usage while - /// providing enough match accuracy. - /// Entry count is currently limited to 2335905 entries (4MB). - /// - /// - private unsafe struct ColorDistanceCache : IDisposable - { - private const int IndexRBits = 5; - private const int IndexGBits = 5; - private const int IndexBBits = 5; - private const int IndexABits = 6; - private const int IndexRCount = (1 << IndexRBits) + 1; - private const int IndexGCount = (1 << IndexGBits) + 1; - private const int IndexBCount = (1 << IndexBBits) + 1; - private const int IndexACount = (1 << IndexABits) + 1; - private const int RShift = 8 - IndexRBits; - private const int GShift = 8 - IndexGBits; - private const int BShift = 8 - IndexBBits; - private const int AShift = 8 - IndexABits; - private const int Entries = IndexRCount * IndexGCount * IndexBCount * IndexACount; - private MemoryHandle tableHandle; - private readonly IMemoryOwner table; - private readonly short* tablePointer; - - public ColorDistanceCache(MemoryAllocator allocator) - { - this.table = allocator.Allocate(Entries); - this.table.GetSpan().Fill(-1); - this.tableHandle = this.table.Memory.Pin(); - this.tablePointer = (short*)this.tableHandle.Pointer; - } - - [MethodImpl(InliningOptions.ShortMethod)] - public readonly void Add(Rgba32 rgba, byte index) - { - int idx = GetPaletteIndex(rgba); - this.tablePointer[idx] = index; - } - - [MethodImpl(InliningOptions.ShortMethod)] - public readonly bool TryGetValue(Rgba32 rgba, out short match) - { - int idx = GetPaletteIndex(rgba); - match = this.tablePointer[idx]; - return match > -1; - } - - /// - /// Clears the cache resetting each entry to empty. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly void Clear() => this.table.GetSpan().Fill(-1); - - [MethodImpl(InliningOptions.ShortMethod)] - private static int GetPaletteIndex(Rgba32 rgba) - { - int rIndex = rgba.R >> RShift; - int gIndex = rgba.G >> GShift; - int bIndex = rgba.B >> BShift; - int aIndex = rgba.A >> AShift; - - return (aIndex * (IndexRCount * IndexGCount * IndexBCount)) + - (rIndex * (IndexGCount * IndexBCount)) + - (gIndex * IndexBCount) + bIndex; - } - - public void Dispose() - { - if (this.table != null) - { - this.tableHandle.Dispose(); - this.table.Dispose(); - } - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer.cs new file mode 100644 index 0000000000..6b2f5a0131 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Quantizes images by grouping colors in an adaptive 16-way tree and reducing those groups into a palette. +/// +/// +/// Each level routes colors using one bit of RGB and, when useful, one bit of alpha. Fully opaque mid-tone colors +/// use RGB-only routing so more branch resolution is spent on visible color detail, while transparent, dark, and +/// light colors use alpha-aware routing so opacity changes can form their own palette buckets. +/// +public class HexadecatreeQuantizer : IQuantizer +{ + /// + /// Initializes a new instance of the class + /// using the default . + /// + public HexadecatreeQuantizer() + : this(new QuantizerOptions()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The quantizer options that control palette size, dithering, and transparency behavior. + public HexadecatreeQuantizer(QuantizerOptions options) + { + Guard.NotNull(options, nameof(options)); + this.Options = options; + } + + /// + public QuantizerOptions Options { get; } + + /// + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + where TPixel : unmanaged, IPixel + => this.CreatePixelSpecificQuantizer(configuration, this.Options); + + /// + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + where TPixel : unmanaged, IPixel + => new HexadecatreeQuantizer(configuration, options); +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer{TPixel}.cs new file mode 100644 index 0000000000..d669f885a4 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/HexadecatreeQuantizer{TPixel}.cs @@ -0,0 +1,684 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Quantizes an image by building an adaptive 16-way color tree and reducing it to the requested palette size. +/// +/// +/// +/// Each level routes colors using one bit of RGB and, when useful, one bit of alpha, giving the tree up to 16 children +/// per node and letting transparency participate directly in palette construction. +/// +/// +/// Fully opaque mid-tone colors use RGB-only routing so more branch resolution is spent on visible color detail. +/// Transparent, dark, and light colors use alpha-aware routing so opacity changes can form distinct palette buckets. +/// +/// +/// The pixel format. +#pragma warning disable CA1001 // Types that own disposable fields should be disposable +// See https://github.com/dotnet/roslyn-analyzers/issues/6151 +public struct HexadecatreeQuantizer : IQuantizer +#pragma warning restore CA1001 // Types that own disposable fields should be disposable + where TPixel : unmanaged, IPixel +{ + private readonly int maxColors; + private readonly int bitDepth; + private readonly Hexadecatree tree; + private readonly IMemoryOwner paletteOwner; + private ReadOnlyMemory palette; + private PixelMap? pixelMap; + private readonly bool isDithering; + private bool isDisposed; + + /// + /// Initializes a new instance of the struct. + /// + /// The configuration that provides memory allocation and pixel conversion services. + /// The quantizer options that control palette size, dithering, and transparency behavior. + [MethodImpl(InliningOptions.ShortMethod)] + public HexadecatreeQuantizer(Configuration configuration, QuantizerOptions options) + { + this.Configuration = configuration; + this.Options = options; + + this.maxColors = this.Options.MaxColors; + this.bitDepth = Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.maxColors), 1, 8); + this.tree = new Hexadecatree(configuration, this.bitDepth, this.maxColors, this.Options.TransparencyThreshold); + this.paletteOwner = configuration.MemoryAllocator.Allocate(this.maxColors, AllocationOptions.Clean); + this.pixelMap = default; + this.palette = default; + this.isDithering = this.Options.Dither is not null; + this.isDisposed = false; + } + + /// + public Configuration Configuration { get; } + + /// + public QuantizerOptions Options { get; } + + /// + public ReadOnlyMemory Palette + { + get + { + if (this.palette.IsEmpty) + { + this.ResolvePalette(); + QuantizerUtilities.CheckPaletteState(in this.palette); + } + + return this.palette; + } + } + + /// + public readonly void AddPaletteColors(in Buffer2DRegion pixelRegion) + { + PixelRowDelegate pixelRowDelegate = new(this.tree); + QuantizerUtilities.AddPaletteColors, TPixel, Rgba32, PixelRowDelegate>( + ref Unsafe.AsRef(in this), + in pixelRegion, + in pixelRowDelegate); + } + + /// + /// Materializes the final palette from the accumulated tree and prepares the dither lookup map when needed. + /// + private void ResolvePalette() + { + short paletteIndex = 0; + Span paletteSpan = this.paletteOwner.GetSpan(); + + this.tree.Palettize(paletteSpan, ref paletteIndex); + ReadOnlyMemory result = this.paletteOwner.Memory[..paletteSpan.Length]; + + if (this.isDithering) + { + // Dithered colors often no longer land on a color that was seen during palette construction, + // so the quantization pass switches to nearest-palette matching once the palette is finalized. + this.pixelMap = PixelMapFactory.Create(this.Configuration, result, this.Options.ColorMatchingMode); + } + + this.palette = result; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) + => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly byte GetQuantizedColor(TPixel color, out TPixel match) + { + if (this.isDithering) + { + // Dithering introduces adjusted colors that were never inserted into the tree, so tree lookup + // is only reliable for the non-dithered path. + return (byte)this.pixelMap!.GetClosestColor(color, out match); + } + + ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.palette.Span); + int index = this.tree.GetPaletteIndex(color); + match = Unsafe.Add(ref paletteRef, (nuint)index); + return (byte)index; + } + + /// + public void Dispose() + { + if (!this.isDisposed) + { + this.isDisposed = true; + this.paletteOwner.Dispose(); + this.pixelMap?.Dispose(); + this.pixelMap = null; + this.tree.Dispose(); + } + } + + /// + /// Forwards source rows into the tree without creating an intermediate buffer. + /// + private readonly struct PixelRowDelegate : IQuantizingPixelRowDelegate + { + private readonly Hexadecatree tree; + + /// + /// Initializes a new instance of the struct. + /// + /// The destination tree that should accumulate each visited row. + public PixelRowDelegate(Hexadecatree tree) => this.tree = tree; + + /// + public void Invoke(ReadOnlySpan row, int rowIndex) => this.tree.AddColors(row); + } + + /// + /// Stores the adaptive 16-way partition tree used to accumulate colors and emit palette entries. + /// + /// + /// The tree uses a fixed node arena for predictable allocation behavior, keeps per-level reducible node lists so + /// deeper buckets can be merged until the palette fits, and caches the previously inserted leaf so repeated colors + /// can be accumulated cheaply. + /// + internal sealed class Hexadecatree : IDisposable + { + // Pooled buffer for OctreeNodes. + private readonly IMemoryOwner nodesOwner; + + // One reducible-node head per level. + // Each entry stores a node index, or -1 when that level currently + // has no reducible nodes. + private readonly short[] reducibleNodes; + + // Maximum number of allowable colors. + private readonly int maxColors; + + // Maximum significant bits. + private readonly int maxColorBits; + + // Keep the fractional byte-domain threshold so values such as .5 remain distinct from alpha 127. + private readonly float transparencyThreshold255; + + // Instead of a reference to the root, we store the index of the root node. + // Index 0 is reserved for the root. + private readonly short rootIndex; + + // Running index for node allocation. Start at 1 so that index 0 is reserved for the root. + private short nextNode = 1; + + // Previously quantized node (index; -1 if none) and its color. + private int previousNode; + private Rgba32 previousColor; + + // Free list for reclaimed node indices. + private readonly Stack freeIndices = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The configuration that provides the backing memory allocator. + /// The number of levels to descend before forcing leaves. + /// The maximum number of palette entries the reduced tree may retain. + /// The alpha threshold below which generated palette entries become fully transparent. + public Hexadecatree( + Configuration configuration, + int maxColorBits, + int maxColors, + float transparencyThreshold) + { + this.maxColorBits = maxColorBits; + this.maxColors = maxColors; + this.transparencyThreshold255 = transparencyThreshold * 255F; + this.Leaves = 0; + this.previousNode = -1; + this.previousColor = default; + + // Allocate a conservative buffer for nodes. + const int capacity = 4096; + this.nodesOwner = configuration.MemoryAllocator.Allocate(capacity, AllocationOptions.Clean); + + // Create the reducible nodes array (one per level 0 .. maxColorBits-1). + this.reducibleNodes = new short[this.maxColorBits]; + this.reducibleNodes.AsSpan().Fill(-1); + + // Reserve index 0 for the root. + this.rootIndex = 0; + ref Node root = ref this.Nodes[this.rootIndex]; + root.Initialize(0, this.maxColorBits, this, this.rootIndex); + } + + /// + /// Gets or sets the number of leaf nodes currently representing palette buckets. + /// + public int Leaves { get; set; } + + /// + /// Gets the underlying node arena. + /// + internal Span Nodes => this.nodesOwner.Memory.Span; + + /// + /// Adds a row of colors to the tree. + /// + /// The colors to accumulate. + public void AddColors(ReadOnlySpan row) + { + for (int x = 0; x < row.Length; x++) + { + this.AddColor(row[x]); + } + } + + /// + /// Adds a single color sample to the tree. + /// + /// The color to accumulate. + private void AddColor(Rgba32 color) + { + // Once the node arena is full and there are no recycled slots available, keep collapsing + // reducible leaves until the tree is small enough to make forward progress again. + if (this.nextNode >= this.Nodes.Length && this.freeIndices.Count == 0) + { + while (this.Leaves > this.maxColors) + { + this.Reduce(); + } + } + + // Scanlines often contain long runs of the same color. Caching the previous leaf lets those + // repeats skip the tree walk and just bump the accumulated sums in place. + if (this.previousColor.Equals(color)) + { + if (this.previousNode == -1) + { + this.previousColor = color; + Node.AddColor(this.rootIndex, color, this.maxColorBits, 0, this); + } + else + { + Node.Increment(this.previousNode, color, this); + } + } + else + { + this.previousColor = color; + Node.AddColor(this.rootIndex, color, this.maxColorBits, 0, this); + } + } + + /// + /// Reduces the tree to the requested palette size and emits the final palette entries. + /// + /// The destination palette span. + /// The running palette index. + public void Palettize(Span palette, ref short paletteIndex) + { + while (this.Leaves > this.maxColors) + { + this.Reduce(); + } + + this.Nodes[this.rootIndex].ConstructPalette(this, palette, ref paletteIndex); + } + + /// + /// Gets the palette index selected by the tree for the supplied color. + /// + /// The color to resolve. + /// The palette index represented by the best matching leaf in the reduced tree. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetPaletteIndex(TPixel color) + => this.Nodes[this.rootIndex].GetPaletteIndex(color.ToRgba32(), 0, this); + + /// + /// Records the most recently touched leaf so repeated colors can bypass another descent. + /// + /// The leaf node index. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TrackPrevious(int nodeIndex) + => this.previousNode = nodeIndex; + + /// + /// Collapses the deepest currently reducible node into a single leaf. + /// + private void Reduce() + { + int index = this.maxColorBits - 1; + while ((index > 0) && (this.reducibleNodes[index] == -1)) + { + index--; + } + + ref Node node = ref this.Nodes[this.reducibleNodes[index]]; + this.reducibleNodes[index] = node.NextReducibleIndex; + node.Reduce(this); + + // If the last inserted leaf was merged away, the next repeated color must walk the tree again. + this.previousNode = -1; + } + + /// + /// Allocates a node index from the free list or from the unused tail of the arena. + /// + /// The allocated node index, or -1 if no node can be allocated. + internal short AllocateNode() + { + if (this.freeIndices.Count > 0) + { + return this.freeIndices.Pop(); + } + + if (this.nextNode >= this.Nodes.Length) + { + return -1; + } + + short newIndex = this.nextNode; + this.nextNode++; + return newIndex; + } + + /// + /// Returns a node index to the free list. + /// + /// The node index to recycle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FreeNode(short index) + { + this.freeIndices.Push(index); + this.Leaves--; + } + + /// + public void Dispose() => this.nodesOwner.Dispose(); + + /// + /// Represents one node in the hexadecatree node arena. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Node + { + public bool Leaf; + public long PixelCount; + public long Red; + public long Green; + public long Blue; + public long Alpha; + public short PaletteIndex; + public short NextReducibleIndex; + private InlineArray16 children; + + /// + /// Gets the 16 child slots for this node. + /// + [UnscopedRef] + public Span Children => this.children; + + /// + /// Initializes a node either as a leaf or as a reducible interior node. + /// + /// The depth of the node being initialized. + /// The maximum tree depth. + /// The owning tree. + /// The node index in the arena. + public void Initialize(int level, int colorBits, Hexadecatree tree, short index) + { + this.Leaf = level == colorBits; + this.Red = 0; + this.Green = 0; + this.Blue = 0; + this.Alpha = 0; + this.PixelCount = 0; + this.PaletteIndex = 0; + this.NextReducibleIndex = -1; + this.Children.Fill(-1); + + if (this.Leaf) + { + tree.Leaves++; + } + else + { + // Track reducible nodes per level so palette reduction can always collapse the deepest + // buckets first without scanning the entire arena. + this.NextReducibleIndex = tree.reducibleNodes[level]; + tree.reducibleNodes[level] = index; + } + } + + /// + /// Descends the tree for the supplied color, allocating nodes as needed until a leaf is reached. + /// + /// The current node index. + /// The color being accumulated. + /// The maximum tree depth. + /// The current depth. + /// The owning tree. + public static void AddColor(int nodeIndex, Rgba32 color, int colorBits, int level, Hexadecatree tree) + { + ref Node node = ref tree.Nodes[nodeIndex]; + if (node.Leaf) + { + Increment(nodeIndex, color, tree); + tree.TrackPrevious(nodeIndex); + return; + } + + int index = GetColorIndex(color, level); + Span children = node.Children; + short childIndex = children[index]; + + if (childIndex == -1) + { + childIndex = tree.AllocateNode(); + if (childIndex == -1) + { + // If the arena is exhausted and no node can be reclaimed yet, fall back to + // accumulating into the current node instead of failing the insert outright. + Increment(nodeIndex, color, tree); + tree.TrackPrevious(nodeIndex); + return; + } + + ref Node child = ref tree.Nodes[childIndex]; + child.Initialize(level + 1, colorBits, tree, childIndex); + children[index] = childIndex; + } + + // Keep descending until we reach the leaf bucket that should accumulate this sample. + AddColor(childIndex, color, colorBits, level + 1, tree); + } + + /// + /// Adds the supplied color sample to an existing node's running sums. + /// + /// The node index to update. + /// The color sample being accumulated. + /// The owning tree. + public static void Increment(int nodeIndex, Rgba32 color, Hexadecatree tree) + { + ref Node node = ref tree.Nodes[nodeIndex]; + node.PixelCount++; + node.Red += color.R; + node.Green += color.G; + node.Blue += color.B; + node.Alpha += color.A; + } + + /// + /// Merges all child nodes into this node and turns it into a leaf. + /// + /// The owning tree. + public void Reduce(Hexadecatree tree) + { + // If already a leaf, do nothing. + if (this.Leaf) + { + return; + } + + // Allocation fallback can accumulate samples on an interior node. Seed the merge + // with this node's own sums so reduction preserves those samples with its children. + long pixelCount = this.PixelCount; + long sumRed = this.Red; + long sumGreen = this.Green; + long sumBlue = this.Blue; + long sumAlpha = this.Alpha; + Span children = this.Children; + + for (int i = 0; i < children.Length; i++) + { + short childIndex = children[i]; + if (childIndex != -1) + { + ref Node child = ref tree.Nodes[childIndex]; + long pixels = child.PixelCount; + sumRed += child.Red; + sumGreen += child.Green; + sumBlue += child.Blue; + sumAlpha += child.Alpha; + pixelCount += pixels; + + children[i] = -1; + tree.FreeNode(childIndex); + } + } + + if (pixelCount > 0) + { + this.Red = sumRed; + this.Green = sumGreen; + this.Blue = sumBlue; + this.Alpha = sumAlpha; + this.PixelCount = pixelCount; + } + else + { + this.Red = this.Green = this.Blue = this.Alpha = 0; + this.PixelCount = 0; + } + + this.Leaf = true; + tree.Leaves++; + } + + /// + /// Traverses the reduced tree and emits one palette color per leaf. + /// + /// The owning tree. + /// The destination palette span. + /// The running palette index. + public void ConstructPalette(Hexadecatree tree, Span palette, ref short paletteIndex) + { + if (this.Leaf) + { + Vector4 sum = new(this.Red, this.Green, this.Blue, this.Alpha); + Vector4 offset = new(this.PixelCount >> 1); + Vector4 vector = Vector4.Clamp( + (sum + offset) / this.PixelCount, + Vector4.Zero, + new Vector4(255)); + + if (vector.W < tree.transparencyThreshold255) + { + vector = Vector4.Zero; + } + + palette[paletteIndex] = TPixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W)); + this.PaletteIndex = paletteIndex++; + } + else + { + Span children = this.Children; + for (int i = 0; i < children.Length; i++) + { + int childIndex = children[i]; + if (childIndex != -1) + { + tree.Nodes[childIndex].ConstructPalette(tree, palette, ref paletteIndex); + } + } + } + } + + /// + /// Resolves the palette index represented by this node for the supplied color. + /// + /// The color to resolve. + /// The current tree depth. + /// The owning tree. + /// The palette index for the best reachable leaf, or -1 if no leaf can be reached. + public int GetPaletteIndex(Rgba32 color, int level, Hexadecatree tree) + { + if (this.Leaf) + { + return this.PaletteIndex; + } + + int colorIndex = GetColorIndex(color, level); + Span children = this.Children; + int childIndex = children[colorIndex]; + if (childIndex != -1) + { + return tree.Nodes[childIndex].GetPaletteIndex(color, level + 1, tree); + } + + // After reductions the exact branch can disappear, so fall back to the first reachable descendant leaf. + for (int i = 0; i < children.Length; i++) + { + childIndex = children[i]; + if (childIndex != -1) + { + int childPaletteIndex = tree.Nodes[childIndex].GetPaletteIndex(color, level + 1, tree); + if (childPaletteIndex != -1) + { + return childPaletteIndex; + } + } + } + + return -1; + } + + /// + /// Computes the child slot for a color at the supplied tree level. + /// + /// The color being routed. + /// The tree depth whose bit plane should be sampled. + /// The child slot index for the color at the supplied level. + /// + /// For fully opaque mid-tone colors the tree ignores alpha and routes on RGB only, preserving more branch + /// resolution for visible color detail. For transparent, dark, and light colors it includes alpha as the + /// most significant routing bit so opacity changes can form their own branches. + /// + public static int GetColorIndex(Rgba32 color, int level) + { + // Sample one bit plane per level, starting at the most significant bit and moving downward. + int shift = 7 - level; + byte mask = (byte)(1 << shift); + + // Use BT.709 luminance as a cheap brightness estimate for deciding whether alpha carries + // useful information at this level for fully opaque colors. + int luminance = ColorNumerics.Get8BitBT709Luminance(color.R, color.G, color.B); + + // Scale the brightness thresholds with depth so deeper levels become stricter about when + // to spend a branch bit on alpha instead of RGB detail. + int darkThreshold = 128 >> level; + int lightThreshold = 255 - (128 >> level); + + if (color.A == 255 && luminance > darkThreshold && luminance < lightThreshold) + { + // Fully opaque mid-tone colors route on RGB only, which preserves more visible color + // resolution because alpha would contribute no extra separation here. + int rBits = ((color.R & mask) >> shift) << 2; + int gBits = ((color.G & mask) >> shift) << 1; + int bBits = (color.B & mask) >> shift; + return rBits | gBits | bBits; + } + else + { + // Transparent, dark, and light colors include alpha as the high routing bit so opacity + // changes can form distinct buckets alongside RGB differences. + int aBits = ((color.A & mask) >> shift) << 3; + int rBits = ((color.R & mask) >> shift) << 2; + int gBits = ((color.G & mask) >> shift) << 1; + int bBits = (color.B & mask) >> shift; + return aBits | rBits | gBits | bBits; + } + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/IColorIndexCache.cs b/src/ImageSharp/Processing/Processors/Quantization/IColorIndexCache.cs new file mode 100644 index 0000000000..76598e0046 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/IColorIndexCache.cs @@ -0,0 +1,396 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Represents a cache used for efficiently retrieving palette indices for colors. +/// +internal interface IColorIndexCache : IDisposable +{ + /// + /// Adds a color to the cache. + /// + /// The color to add. + /// The index of the color in the palette. + /// + /// if the color was added; otherwise, . + /// + public bool TryAdd(Rgba32 color, short value); + + /// + /// Gets the index of the color in the palette. + /// + /// The color to get the index for. + /// The index of the color in the palette. + /// + /// if the color is in the palette; otherwise, . + /// + public bool TryGetValue(Rgba32 color, out short value); + + /// + /// Clears the cache. + /// + public void Clear(); +} + +/// +/// Represents a cache used for efficiently retrieving palette indices for colors. +/// +/// The type of the cache. +internal interface IColorIndexCache : IColorIndexCache + where T : struct, IColorIndexCache +{ + /// + /// Creates a new instance of the cache. + /// + /// The memory allocator to use. + /// + /// The new instance of the cache. + /// + public static abstract T Create(MemoryAllocator allocator); +} + +/// +/// +/// CoarseCache is a fast, low-memory lookup structure for caching palette indices associated with RGBA values, +/// using a quantized representation of 5,5,5,6 (RGB: 5 bits each, Alpha: 6 bits). +/// +/// +/// The cache quantizes the RGB channels to 5 bits each, resulting in 32 levels per channel and a total of 32³ = 32,768 buckets. +/// Each bucket is represented by an , which holds a small, inline array of alpha entries. +/// Each alpha entry stores the alpha value quantized to 6 bits (0–63) along with a palette index (a 16-bit value). +/// +/// +/// Performance Characteristics: +/// - Lookup: O(1) for computing the bucket index from the RGB channels, plus a small constant time (up to 8 iterations) +/// to search through the alpha entries in the bucket. +/// - Insertion: O(1) for bucket index computation and a quick linear search over a very small (fixed) number of entries. +/// +/// +/// Memory Characteristics: +/// - The cache consists of 32,768 buckets. +/// - Each is implemented using an inline array with a capacity of 8 entries. +/// - Each bucket occupies approximately 1 byte (Count) + (8 entries × 3 bytes each) ≈ 25 bytes. +/// - Overall, the buckets occupy roughly 32,768 × 25 bytes = 819,200 bytes (≈ 800 KB). +/// +/// +/// This design provides nearly constant-time lookup and insertion with minimal memory usage, +/// making it ideal for applications such as color distance caching in images with a limited palette (up to 256 entries). +/// +/// +internal unsafe struct CoarseCache : IColorIndexCache +{ + // Use 5 bits per channel for R, G, and B: 32 levels each. + // Total buckets = 32^3 = 32768. + private const int RgbBits = 5; + private const int RgbShift = 8 - RgbBits; // 3 + private const int BucketCount = 1 << (RgbBits * 3); // 32768 + private readonly IMemoryOwner bucketsOwner; + private readonly AlphaBucket* buckets; + private MemoryHandle bucketHandle; + + private CoarseCache(MemoryAllocator allocator) + { + this.bucketsOwner = allocator.Allocate(BucketCount, AllocationOptions.Clean); + this.bucketHandle = this.bucketsOwner.Memory.Pin(); + this.buckets = (AlphaBucket*)this.bucketHandle.Pointer; + } + + /// + public static CoarseCache Create(MemoryAllocator allocator) => new(allocator); + + /// + public readonly bool TryAdd(Rgba32 color, short paletteIndex) + { + int bucketIndex = GetBucketIndex(color.R, color.G, color.B); + byte quantAlpha = QuantizeAlpha(color.A); + this.buckets[bucketIndex].Add(quantAlpha, paletteIndex); + return true; + } + + /// + public readonly bool TryGetValue(Rgba32 color, out short paletteIndex) + { + int bucketIndex = GetBucketIndex(color.R, color.G, color.B); + byte quantAlpha = QuantizeAlpha(color.A); + return this.buckets[bucketIndex].TryGetValue(quantAlpha, out paletteIndex); + } + + /// + public readonly void Clear() + { + Span bucketsSpan = this.bucketsOwner.GetSpan(); + bucketsSpan.Clear(); + } + + /// + public void Dispose() + { + this.bucketHandle.Dispose(); + this.bucketsOwner.Dispose(); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int GetBucketIndex(byte r, byte g, byte b) + { + int qr = r >> RgbShift; + int qg = g >> RgbShift; + int qb = b >> RgbShift; + + // Combine the quantized channels into a single index. + return (qr << (RgbBits << 1)) | (qg << RgbBits) | qb; + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static byte QuantizeAlpha(byte a) => (byte)(a >> 2); + + public struct AlphaEntry + { + // Store the alpha value quantized to 6 bits (0..63). + public byte QuantizedAlpha; + public short PaletteIndex; + } + + public struct AlphaBucket + { + // Fixed capacity for alpha entries in this bucket. + // We choose a capacity of 8 for several reasons: + // + // 1. The alpha channel is quantized to 6 bits, so there are 64 possible distinct values. + // In the worst-case, a given RGB bucket might encounter up to 64 different alpha values. + // + // 2. However, in practice (based on probability theory and typical image data), + // the number of unique alpha values that actually occur for a given quantized RGB + // bucket is usually very small. If you randomly sample 8 values out of 64, + // the probability that these samples are all unique is high if the distribution + // of alpha values is skewed or if only a few alpha values are used. + // + // 3. Statistically, for many real-world images, most RGB buckets will have only a couple + // of unique alpha values. Allocating 8 slots per bucket provides a good trade-off: + // it captures the common-case scenario while keeping overall memory usage low. + // + // 4. Even if more than 8 unique alpha values occur in a bucket, + // our design overwrites the first entry. This behavior gives us some "wriggle room" + // while preserving the most frequently encountered or most recent values. + public const int Capacity = 8; + public byte Count; + private InlineArray8 entries; + + [MethodImpl(InliningOptions.ShortMethod)] + public bool TryGetValue(byte quantizedAlpha, out short paletteIndex) + { + for (int i = 0; i < this.Count; i++) + { + ref AlphaEntry entry = ref this.entries[i]; + if (entry.QuantizedAlpha == quantizedAlpha) + { + paletteIndex = entry.PaletteIndex; + return true; + } + } + + paletteIndex = -1; + return false; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Add(byte quantizedAlpha, short paletteIndex) + { + // Check for an existing entry with the same quantized alpha. + for (int i = 0; i < this.Count; i++) + { + ref AlphaEntry entry = ref this.entries[i]; + if (entry.QuantizedAlpha == quantizedAlpha) + { + // Update palette index if found. + entry.PaletteIndex = paletteIndex; + return; + } + } + + // If there's room, add a new entry. + if (this.Count < Capacity) + { + ref AlphaEntry newEntry = ref this.entries[this.Count]; + newEntry.QuantizedAlpha = quantizedAlpha; + newEntry.PaletteIndex = paletteIndex; + this.Count++; + } + else + { + // Bucket is full. Overwrite the first entry to give us some wriggle room. + this.entries[0].QuantizedAlpha = quantizedAlpha; + this.entries[0].PaletteIndex = paletteIndex; + } + } + } +} + +/// +/// A fixed-size exact-match cache that stores packed RGBA keys with 4-way set associativity. +/// +/// +/// The cache holds 512 total entries split across 128 sets. Entries are evicted within a set +/// using round-robin replacement, but cached values are returned only when the full packed RGBA +/// key matches, preserving exact quantization results with predictable memory usage. +/// The overall memory usage is approximately 4–5 KB. Both lookup and insertion operations are, +/// on average, O(1) since each lookup probes at most four candidate entries within the selected set. +/// This guarantees highly efficient and predictable performance for small, fixed-size color palettes. +/// +internal unsafe struct AccurateCache : IColorIndexCache +{ + public const int Capacity = 512; + private const int Ways = 4; + private const int SetCount = Capacity / Ways; + private const int SetMask = SetCount - 1; + + private readonly IMemoryOwner keysOwner; + private MemoryHandle keysHandle; + private uint* keys; + + private readonly IMemoryOwner valuesOwner; + private MemoryHandle valuesHandle; + private ushort* values; + + private readonly IMemoryOwner nextVictimOwner; + private MemoryHandle nextVictimHandle; + private byte* nextVictim; + + private AccurateCache(MemoryAllocator allocator) + { + this.keysOwner = allocator.Allocate(Capacity, AllocationOptions.Clean); + this.keysHandle = this.keysOwner.Memory.Pin(); + this.keys = (uint*)this.keysHandle.Pointer; + + this.valuesOwner = allocator.Allocate(Capacity, AllocationOptions.Clean); + this.valuesHandle = this.valuesOwner.Memory.Pin(); + this.values = (ushort*)this.valuesHandle.Pointer; + + this.nextVictimOwner = allocator.Allocate(SetCount, AllocationOptions.Clean); + this.nextVictimHandle = this.nextVictimOwner.Memory.Pin(); + this.nextVictim = (byte*)this.nextVictimHandle.Pointer; + } + + /// + public static AccurateCache Create(MemoryAllocator allocator) => new(allocator); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool TryAdd(Rgba32 color, short value) + { + uint key = color.PackedValue; + int set = GetSetIndex(key); + int start = set * Ways; + int empty = -1; + + uint* keys = this.keys; + ushort* values = this.values; + ushort storedValue = (ushort)(value + 1); + + for (int i = start; i < start + Ways; i++) + { + ushort candidate = values[i]; + if (candidate == 0) + { + empty = i; + continue; + } + + if (keys[i] == key) + { + values[i] = storedValue; + return true; + } + } + + int slot = empty >= 0 ? empty : start + this.nextVictim[set]; + keys[slot] = key; + values[slot] = storedValue; + + if (empty < 0) + { + this.nextVictim[set] = (byte)((this.nextVictim[set] + 1) & (Ways - 1)); + } + + return true; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly bool TryGetValue(Rgba32 color, out short value) + { + uint key = color.PackedValue; + int start = GetSetIndex(key) * Ways; + + uint* keys = this.keys; + ushort* values = this.values; + + for (int i = start; i < start + Ways; i++) + { + ushort candidate = values[i]; + if (candidate != 0 && keys[i] == key) + { + value = (short)(candidate - 1); + return true; + } + } + + value = -1; + return false; + } + + /// + /// Clears the cache. + /// + public readonly void Clear() + { + this.valuesOwner.GetSpan().Clear(); + this.nextVictimOwner.GetSpan().Clear(); + } + + public void Dispose() + { + this.keysHandle.Dispose(); + this.keysOwner.Dispose(); + this.valuesHandle.Dispose(); + this.valuesOwner.Dispose(); + this.nextVictimHandle.Dispose(); + this.nextVictimOwner.Dispose(); + this.keys = null; + this.values = null; + this.nextVictim = null; + } + + /// + /// Maps a packed RGBA key to one of the cache sets used by . + /// + /// The packed key. + /// The zero-based set index for the key. + /// + /// + /// The cache is 4-way set-associative, so this hash only needs to choose one of + /// sets before probing up to four candidate entries. + /// + /// + /// is laid out as R | (G << 8) | (B << 16) | (A << 24). + /// The XOR-fold mixes neighboring bytes into the low bits, and the final mask selects the + /// set. With the current 128-set layout that makes the selected set effectively depend on + /// the low 7 bits of R ^ G ^ B. Alpha still participates in the later exact key + /// comparison, but not in set selection. + /// + /// + /// Collisions are expected and acceptable here. Correctness comes from the full packed-key + /// comparison during probing; this hash only aims to spread keys cheaply enough that each + /// access touches at most one 4-entry set. + /// + /// + [MethodImpl(InliningOptions.ShortMethod)] + private static int GetSetIndex(uint key) + => (int)(((key >> 16) ^ (key >> 8) ^ key) & SetMask); +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs index 9d5b606040..02dce8ca48 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs @@ -13,7 +13,7 @@ public interface IQuantizer /// /// Gets the quantizer options defining quantization rules. /// - QuantizerOptions Options { get; } + public QuantizerOptions Options { get; } /// /// Creates the generic frame quantizer. @@ -21,7 +21,7 @@ public interface IQuantizer /// The to configure internal operations. /// The pixel format. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel; /// @@ -31,6 +31,6 @@ IQuantizer CreatePixelSpecificQuantizer(Configuration configurat /// The to configure internal operations. /// The options to create the quantizer with. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs index 35bbb1289e..1e6420eaa9 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs @@ -16,26 +16,26 @@ public interface IQuantizer : IDisposable /// /// Gets the configuration. /// - Configuration Configuration { get; } + public Configuration Configuration { get; } /// /// Gets the quantizer options defining quantization rules. /// - QuantizerOptions Options { get; } + public QuantizerOptions Options { get; } /// /// Gets the quantized color palette. /// /// - /// The palette has not been built via . + /// The palette has not been built via . /// - ReadOnlyMemory Palette { get; } + public ReadOnlyMemory Palette { get; } /// /// Adds colors to the quantized palette from the given pixel source. /// /// The of source pixels to register. - void AddPaletteColors(Buffer2DRegion pixelRegion); + public void AddPaletteColors(in Buffer2DRegion pixelRegion); /// /// Quantizes an image frame and return the resulting output pixels. @@ -46,10 +46,10 @@ public interface IQuantizer : IDisposable /// A representing a quantized version of the source frame pixels. /// /// - /// Only executes the second (quantization) step. The palette has to be built by calling . - /// To run both steps, use . + /// Only executes the second (quantization) step. The palette has to be built by calling . + /// To run both steps, use . /// - IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds); + public IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds); /// /// Returns the index and color from the quantized palette corresponding to the given color. @@ -57,7 +57,7 @@ public interface IQuantizer : IDisposable /// The color to match. /// The matched color. /// The index. - byte GetQuantizedColor(TPixel color, out TPixel match); + public byte GetQuantizedColor(TPixel color, out TPixel match); // TODO: Enable bulk operations. // void GetQuantizedColors(ReadOnlySpan colors, ReadOnlySpan palette, Span indices, Span matches); diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizingPixelRowDelegate{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizingPixelRowDelegate{TPixel}.cs new file mode 100644 index 0000000000..3cf4c93d62 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizingPixelRowDelegate{TPixel}.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization; + +/// +/// Defines a delegate for processing a row of pixels in an image for quantization. +/// +/// Represents a pixel type that can be processed in a quantizing operation. +internal interface IQuantizingPixelRowDelegate + where TPixel : unmanaged, IPixel +{ + /// + /// Processes a row of pixels for quantization. + /// + /// The row of pixels to process. + /// The index of the row being processed. + public void Invoke(ReadOnlySpan row, int rowIndex); +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs deleted file mode 100644 index 0a1032bf0d..0000000000 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Quantization; - -/// -/// Allows the quantization of images pixels using Octrees. -/// -/// -public class OctreeQuantizer : IQuantizer -{ - /// - /// Initializes a new instance of the class - /// using the default . - /// - public OctreeQuantizer() - : this(new QuantizerOptions()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The quantizer options defining quantization rules. - public OctreeQuantizer(QuantizerOptions options) - { - Guard.NotNull(options, nameof(options)); - this.Options = options; - } - - /// - public QuantizerOptions Options { get; } - - /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) - where TPixel : unmanaged, IPixel - => this.CreatePixelSpecificQuantizer(configuration, this.Options); - - /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) - where TPixel : unmanaged, IPixel - => new OctreeQuantizer(configuration, options); -} diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs deleted file mode 100644 index fe422882bc..0000000000 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Quantization; - -/// -/// Encapsulates methods to calculate the color palette if an image using an Octree pattern. -/// -/// -/// The pixel format. -[SuppressMessage( - "Design", - "CA1001:Types that own disposable fields should be disposable", - Justification = "https://github.com/dotnet/roslyn-analyzers/issues/6151")] -public struct OctreeQuantizer : IQuantizer - where TPixel : unmanaged, IPixel -{ - private readonly int maxColors; - private readonly int bitDepth; - private readonly Octree octree; - private readonly IMemoryOwner paletteOwner; - private ReadOnlyMemory palette; - private EuclideanPixelMap? pixelMap; - private readonly bool isDithering; - private bool isDisposed; - - /// - /// Initializes a new instance of the struct. - /// - /// The configuration which allows altering default behaviour or extending the library. - /// The quantizer options defining quantization rules. - [MethodImpl(InliningOptions.ShortMethod)] - public OctreeQuantizer(Configuration configuration, QuantizerOptions options) - { - this.Configuration = configuration; - this.Options = options; - - this.maxColors = this.Options.MaxColors; - this.bitDepth = Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.maxColors), 1, 8); - this.octree = new Octree(this.bitDepth); - this.paletteOwner = configuration.MemoryAllocator.Allocate(this.maxColors, AllocationOptions.Clean); - this.pixelMap = default; - this.palette = default; - this.isDithering = this.Options.Dither is not null; - this.isDisposed = false; - } - - /// - public Configuration Configuration { get; } - - /// - public QuantizerOptions Options { get; } - - /// - public readonly ReadOnlyMemory Palette - { - get - { - QuantizerUtilities.CheckPaletteState(in this.palette); - return this.palette; - } - } - - /// - public void AddPaletteColors(Buffer2DRegion pixelRegion) - { - using (IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(pixelRegion.Width)) - { - Span bufferSpan = buffer.GetSpan(); - - // Loop through each row - for (int y = 0; y < pixelRegion.Height; y++) - { - Span row = pixelRegion.DangerousGetRowSpan(y); - PixelOperations.Instance.ToRgba32(this.Configuration, row, bufferSpan); - - for (int x = 0; x < bufferSpan.Length; x++) - { - Rgba32 rgba = bufferSpan[x]; - - // Add the color to the Octree - this.octree.AddColor(rgba); - } - } - } - - int paletteIndex = 0; - Span paletteSpan = this.paletteOwner.GetSpan(); - - // On very rare occasions, (blur.png), the quantizer does not preserve a - // transparent entry when palletizing the captured colors. - // To workaround this we ensure the palette ends with the default color - // for higher bit depths. Lower bit depths will correctly reduce the palette. - // TODO: Investigate more evenly reduced palette reduction. - int max = this.maxColors; - if (this.bitDepth >= 4) - { - max--; - } - - this.octree.Palletize(paletteSpan, max, ref paletteIndex); - ReadOnlyMemory result = this.paletteOwner.Memory[..paletteSpan.Length]; - - // When called multiple times by QuantizerUtilities.BuildPalette - // this prevents memory churn caused by reallocation. - if (this.pixelMap is null) - { - this.pixelMap = new EuclideanPixelMap(this.Configuration, result); - } - else - { - this.pixelMap.Clear(result); - } - - this.palette = result; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) - => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly byte GetQuantizedColor(TPixel color, out TPixel match) - { - // Octree only maps the RGB component of a color - // so cannot tell the difference between a fully transparent - // pixel and a black one. - if (this.isDithering || color.Equals(default)) - { - return (byte)this.pixelMap!.GetClosestColor(color, out match); - } - - ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.palette.Span); - byte index = (byte)this.octree.GetPaletteIndex(color); - match = Unsafe.Add(ref paletteRef, index); - return index; - } - - /// - public void Dispose() - { - if (!this.isDisposed) - { - this.isDisposed = true; - this.paletteOwner.Dispose(); - this.pixelMap?.Dispose(); - this.pixelMap = null; - } - } - - /// - /// Class which does the actual quantization. - /// - private sealed class Octree - { - /// - /// The root of the Octree - /// - private readonly OctreeNode root; - - /// - /// Maximum number of significant bits in the image - /// - private readonly int maxColorBits; - - /// - /// Store the last node quantized - /// - private OctreeNode? previousNode; - - /// - /// Cache the previous color quantized - /// - private Rgba32 previousColor; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The maximum number of significant bits in the image - /// - public Octree(int maxColorBits) - { - this.maxColorBits = maxColorBits; - this.Leaves = 0; - this.ReducibleNodes = new OctreeNode[9]; - this.root = new OctreeNode(0, this.maxColorBits, this); - this.previousColor = default; - this.previousNode = null; - } - - /// - /// Gets or sets the number of leaves in the tree - /// - public int Leaves - { - [MethodImpl(InliningOptions.ShortMethod)] - get; - - [MethodImpl(InliningOptions.ShortMethod)] - set; - } - - /// - /// Gets the array of reducible nodes - /// - private OctreeNode?[] ReducibleNodes - { - [MethodImpl(InliningOptions.ShortMethod)] - get; - } - - /// - /// Add a given color value to the Octree - /// - /// The color to add. - public void AddColor(Rgba32 color) - { - // Check if this request is for the same color as the last - if (this.previousColor.Equals(color)) - { - // If so, check if I have a previous node setup. - // This will only occur if the first color in the image - // happens to be black, with an alpha component of zero. - if (this.previousNode is null) - { - this.previousColor = color; - this.root.AddColor(ref color, this.maxColorBits, 0, this); - } - else - { - // Just update the previous node - this.previousNode.Increment(ref color); - } - } - else - { - this.previousColor = color; - this.root.AddColor(ref color, this.maxColorBits, 0, this); - } - } - - /// - /// Convert the nodes in the Octree to a palette with a maximum of colorCount colors - /// - /// The palette to fill. - /// The maximum number of colors - /// The palette index, used to calculate the final size of the palette. - [MethodImpl(InliningOptions.ShortMethod)] - public void Palletize(Span palette, int colorCount, ref int paletteIndex) - { - while (this.Leaves > colorCount) - { - this.Reduce(); - } - - this.root.ConstructPalette(palette, ref paletteIndex); - } - - /// - /// Get the palette index for the passed color - /// - /// The color to match. - /// - /// The index. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public int GetPaletteIndex(TPixel color) - { - Unsafe.SkipInit(out Rgba32 rgba); - color.ToRgba32(ref rgba); - return this.root.GetPaletteIndex(ref rgba, 0); - } - - /// - /// Keep track of the previous node that was quantized - /// - /// - /// The node last quantized - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void TrackPrevious(OctreeNode node) => this.previousNode = node; - - /// - /// Reduce the depth of the tree - /// - private void Reduce() - { - // Find the deepest level containing at least one reducible node - int index = this.maxColorBits - 1; - while ((index > 0) && (this.ReducibleNodes[index] is null)) - { - index--; - } - - // Reduce the node most recently added to the list at level 'index' - OctreeNode node = this.ReducibleNodes[index]!; - this.ReducibleNodes[index] = node.NextReducible; - - // Decrement the leaf count after reducing the node - this.Leaves -= node.Reduce(); - - // And just in case I've reduced the last color to be added, and the next color to - // be added is the same, invalidate the previousNode... - this.previousNode = null; - } - - /// - /// Class which encapsulates each node in the tree - /// - public sealed class OctreeNode - { - /// - /// Pointers to any child nodes - /// - private readonly OctreeNode?[]? children; - - /// - /// Flag indicating that this is a leaf node - /// - private bool leaf; - - /// - /// Number of pixels in this node - /// - private int pixelCount; - - /// - /// Red component - /// - private int red; - - /// - /// Green Component - /// - private int green; - - /// - /// Blue component - /// - private int blue; - - /// - /// The index of this node in the palette - /// - private int paletteIndex; - - /// - /// Initializes a new instance of the class. - /// - /// The level in the tree = 0 - 7. - /// The number of significant color bits in the image. - /// The tree to which this node belongs. - public OctreeNode(int level, int colorBits, Octree octree) - { - // Construct the new node - this.leaf = level == colorBits; - - this.red = this.green = this.blue = 0; - this.pixelCount = 0; - - // If a leaf, increment the leaf count - if (this.leaf) - { - octree.Leaves++; - this.NextReducible = null; - this.children = null; - } - else - { - // Otherwise add this to the reducible nodes - this.NextReducible = octree.ReducibleNodes[level]; - octree.ReducibleNodes[level] = this; - this.children = new OctreeNode[8]; - } - } - - /// - /// Gets the next reducible node - /// - public OctreeNode? NextReducible - { - [MethodImpl(InliningOptions.ShortMethod)] - get; - } - - /// - /// Add a color into the tree - /// - /// The color to add. - /// The number of significant color bits. - /// The level in the tree. - /// The tree to which this node belongs. - public void AddColor(ref Rgba32 color, int colorBits, int level, Octree octree) - { - // Update the color information if this is a leaf - if (this.leaf) - { - this.Increment(ref color); - - // Setup the previous node - octree.TrackPrevious(this); - } - else - { - // Go to the next level down in the tree - int index = GetColorIndex(ref color, level); - - OctreeNode? child = this.children![index]; - if (child is null) - { - // Create a new child node and store it in the array - child = new OctreeNode(level + 1, colorBits, octree); - this.children[index] = child; - } - - // Add the color to the child node - child.AddColor(ref color, colorBits, level + 1, octree); - } - } - - /// - /// Reduce this node by removing all of its children - /// - /// The number of leaves removed - public int Reduce() - { - this.red = this.green = this.blue = 0; - int childNodes = 0; - - // Loop through all children and add their information to this node - for (int index = 0; index < 8; index++) - { - OctreeNode? child = this.children![index]; - if (child != null) - { - this.red += child.red; - this.green += child.green; - this.blue += child.blue; - this.pixelCount += child.pixelCount; - ++childNodes; - this.children[index] = null; - } - } - - // Now change this to a leaf node - this.leaf = true; - - // Return the number of nodes to decrement the leaf count by - return childNodes - 1; - } - - /// - /// Traverse the tree, building up the color palette - /// - /// The palette - /// The current palette index - [MethodImpl(InliningOptions.ColdPath)] - public void ConstructPalette(Span palette, ref int index) - { - if (this.leaf) - { - // Set the color of the palette entry - Vector3 vector = Vector3.Clamp( - new Vector3(this.red, this.green, this.blue) / this.pixelCount, - 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; - - // Consume the next palette index - this.paletteIndex = index++; - } - else - { - // Loop through children looking for leaves - for (int i = 0; i < 8; i++) - { - this.children![i]?.ConstructPalette(palette, ref index); - } - } - } - - /// - /// Return the palette index for the passed color - /// - /// The pixel data. - /// The level. - /// - /// The representing the index of the pixel in the palette. - /// - [MethodImpl(InliningOptions.ColdPath)] - public int GetPaletteIndex(ref Rgba32 pixel, int level) - { - if (this.leaf) - { - return this.paletteIndex; - } - - int colorIndex = GetColorIndex(ref pixel, level); - OctreeNode? child = this.children![colorIndex]; - - int index = 0; - if (child != null) - { - index = child.GetPaletteIndex(ref pixel, level + 1); - } - else - { - // Check other children. - for (int i = 0; i < this.children.Length; i++) - { - child = this.children[i]; - if (child != null) - { - int childIndex = child.GetPaletteIndex(ref pixel, level + 1); - if (childIndex != 0) - { - return childIndex; - } - } - } - } - - return index; - } - - /// - /// Gets the color index at the given level. - /// - /// The color. - /// The node level. - /// The index. - [MethodImpl(InliningOptions.ShortMethod)] - private static int GetColorIndex(ref Rgba32 color, int level) - { - int shift = 7 - level; - byte mask = (byte)(1 << shift); - - return ((color.R & mask) >> shift) - | (((color.G & mask) >> shift) << 1) - | (((color.B & mask) >> shift) << 2); - } - - /// - /// Increment the color count and add to the color information - /// - /// The pixel to add. - [MethodImpl(InliningOptions.ShortMethod)] - public void Increment(ref Rgba32 color) - { - this.pixelCount++; - this.red += color.R; - this.green += color.G; - this.blue += color.B; - } - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index acd179ffcc..a49691515a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -11,7 +11,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; public class PaletteQuantizer : IQuantizer { private readonly ReadOnlyMemory colorPalette; - private readonly int transparentIndex; + private readonly int transparencyIndex; + private readonly Color transparentColor; /// /// Initializes a new instance of the class. @@ -25,27 +26,33 @@ public PaletteQuantizer(ReadOnlyMemory palette) /// /// Initializes a new instance of the class. /// - /// The color palette. + /// The color palette to use. /// The quantizer options defining quantization rules. public PaletteQuantizer(ReadOnlyMemory palette, QuantizerOptions options) - : this(palette, options, -1) + : this(palette, options, -1, default) { } /// /// Initializes a new instance of the class. /// - /// The color palette. + /// The color palette to use. /// The quantizer options defining quantization rules. - /// An explicit index at which to match transparent pixels. - internal PaletteQuantizer(ReadOnlyMemory palette, QuantizerOptions options, int transparentIndex) + /// The index of the color in the palette that should be considered as transparent. + /// The color that should be considered as transparent. + internal PaletteQuantizer( + ReadOnlyMemory palette, + QuantizerOptions options, + int transparencyIndex, + Color transparentColor) { Guard.MustBeGreaterThan(palette.Length, 0, nameof(palette)); Guard.NotNull(options, nameof(options)); this.colorPalette = palette; this.Options = options; - this.transparentIndex = transparentIndex; + this.transparencyIndex = transparencyIndex; + this.transparentColor = transparentColor; } /// @@ -62,9 +69,10 @@ public IQuantizer CreatePixelSpecificQuantizer(Configuration con { 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()); - return new PaletteQuantizer(configuration, options, palette, this.transparentIndex); + // 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.transparencyIndex, this.transparentColor.ToPixel()); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs index 3df80ea9b7..4fd044ab40 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -17,10 +17,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; "Design", "CA1001:Types that own disposable fields should be disposable", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/6151")] -internal readonly struct PaletteQuantizer : IQuantizer +internal struct PaletteQuantizer : IQuantizer where TPixel : unmanaged, IPixel { - private readonly EuclideanPixelMap pixelMap; + private readonly PixelMap pixelMap; + private int transparencyIndex; + private TPixel transparentColor; /// /// Initializes a new instance of the struct. @@ -28,20 +30,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; /// The configuration which allows altering default behavior or extending the library. /// The quantizer options defining quantization rules. /// The palette to use. - /// An explicit index at which to match transparent pixels. [MethodImpl(InliningOptions.ShortMethod)] + public PaletteQuantizer(Configuration configuration, QuantizerOptions options, ReadOnlyMemory palette) + : this(configuration, options, palette, -1, default) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.NotNull(options, nameof(options)); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The configuration which allows altering default behavior or extending the library. + /// The quantizer options defining quantization rules. + /// The palette to use. + /// The index of the color in the palette that should be considered as transparent. + /// The color that should be considered as transparent. public PaletteQuantizer( Configuration configuration, QuantizerOptions options, ReadOnlyMemory palette, - int transparentIndex) + int transparencyIndex, + TPixel transparentColor) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); this.Configuration = configuration; this.Options = options; - this.pixelMap = new EuclideanPixelMap(configuration, palette, transparentIndex); + this.pixelMap = PixelMapFactory.Create(this.Configuration, palette, options.ColorMatchingMode); + this.transparencyIndex = transparencyIndex; + this.transparentColor = transparentColor; } /// @@ -51,30 +70,38 @@ public PaletteQuantizer( public QuantizerOptions Options { get; } /// - public ReadOnlyMemory Palette => this.pixelMap.Palette; - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) - => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); + public readonly ReadOnlyMemory Palette => this.pixelMap.Palette; /// [MethodImpl(InliningOptions.ShortMethod)] - public void AddPaletteColors(Buffer2DRegion pixelRegion) + public readonly void AddPaletteColors(in Buffer2DRegion pixelRegion) { } - /// - /// Allows setting the transparent index after construction. - /// - /// An explicit index at which to match transparent pixels. - public void SetTransparentIndex(int index) => this.pixelMap.SetTransparentIndex(index); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) + => QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); /// [MethodImpl(InliningOptions.ShortMethod)] public readonly byte GetQuantizedColor(TPixel color, out TPixel match) - => (byte)this.pixelMap.GetClosestColor(color, out match); + { + if (this.transparencyIndex >= 0 && color.Equals(this.transparentColor)) + { + match = this.transparentColor; + return (byte)this.transparencyIndex; + } + + return (byte)this.pixelMap.GetClosestColor(color, out match); + } + + public void SetTransparencyIndex(int transparencyIndex, TPixel transparentColor) + { + this.transparencyIndex = transparencyIndex; + this.transparentColor = transparentColor; + } /// - public void Dispose() => this.pixelMap.Dispose(); + public readonly void Dispose() => this.pixelMap.Dispose(); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index da2580fedf..16bb412c76 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -32,7 +32,7 @@ public QuantizeProcessor(Configuration configuration, IQuantizer quantizer, Imag /// protected override void OnFrameApply(ImageFrame source) { - Rectangle interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle); + Rectangle interest = Rectangle.Intersect(source.Bounds, this.SourceRectangle); Configuration configuration = this.Configuration; using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration); diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs index 2bf4c6d56d..3b515e372d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs @@ -21,15 +21,30 @@ public static class QuantizerConstants public const int MaxColors = 256; /// - /// The minumim dithering scale used to adjust the amount of dither. + /// The minimum dithering scale used to adjust the amount of dither. /// public const float MinDitherScale = 0; /// - /// The max dithering scale used to adjust the amount of dither. + /// The maximum dithering scale used to adjust the amount of dither. /// public const float MaxDitherScale = 1F; + /// + /// The default threshold at which to consider a pixel transparent. + /// + public const float DefaultTransparencyThreshold = 64 / 255F; + + /// + /// The minimum threshold at which to consider a pixel transparent. + /// + public const float MinTransparencyThreshold = 0F; + + /// + /// The maximum threshold at which to consider a pixel transparent. + /// + public const float MaxTransparencyThreshold = 1F; + /// /// Gets the default dithering algorithm to use. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs index a6bb265a81..16dfd5b330 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -8,10 +9,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; /// /// Defines options for quantization. /// -public class QuantizerOptions +public class QuantizerOptions : IDeepCloneable { +#pragma warning disable IDE0032 // Use auto property private float ditherScale = QuantizerConstants.MaxDitherScale; private int maxColors = QuantizerConstants.MaxColors; + private float threshold = QuantizerConstants.DefaultTransparencyThreshold; +#pragma warning restore IDE0032 // Use auto property + + /// + /// Initializes a new instance of the class. + /// + public QuantizerOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options to clone. + private QuantizerOptions(QuantizerOptions options) + { + this.Dither = options.Dither; + this.DitherScale = options.DitherScale; + this.MaxColors = options.MaxColors; + this.TransparencyThreshold = options.TransparencyThreshold; + this.ColorMatchingMode = options.ColorMatchingMode; + this.TransparentColorMode = options.TransparentColorMode; + } /// /// Gets or sets the algorithm to apply to the output image. @@ -38,4 +63,30 @@ public int MaxColors get => this.maxColors; set => this.maxColors = Numerics.Clamp(value, QuantizerConstants.MinColors, QuantizerConstants.MaxColors); } + + /// + /// Gets or sets the color matching mode used for matching pixel values to palette colors. + /// Defaults to . + /// + public ColorMatchingMode ColorMatchingMode { get; set; } = ColorMatchingMode.Coarse; + + /// + /// Gets or sets the threshold at which to consider a pixel transparent. Range 0..1. + /// Defaults to . + /// + public float TransparencyThreshold + { + get => this.threshold; + set => this.threshold = Numerics.Clamp(value, QuantizerConstants.MinTransparencyThreshold, QuantizerConstants.MaxTransparencyThreshold); + } + + /// + /// Gets or sets the transparent color mode used for handling transparent colors + /// when not using thresholding. + /// Defaults to . + /// + public TransparentColorMode TransparentColorMode { get; set; } = TransparentColorMode.Preserve; + + /// + public QuantizerOptions DeepClone() => new(this); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs index 10ffe68dca..1e3453294f 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs @@ -1,7 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers; +using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -13,6 +18,130 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; /// public static class QuantizerUtilities { + /// + /// Performs a deep clone the instance and optionally mutates the clone. + /// + /// The instance to clone. + /// An optional delegate to mutate the cloned instance. + /// The cloned instance. + public static QuantizerOptions DeepClone(this QuantizerOptions options, Action? mutate) + { + QuantizerOptions clone = options.DeepClone(); + mutate?.Invoke(clone); + return clone; + } + + /// + /// Determines if transparent pixels can be replaced based on the specified color mode and pixel type. + /// + /// The type of the pixel. + /// The alpha threshold used to determine if a pixel is transparent. + /// Returns true if transparent pixels can be replaced; otherwise, false. + public static bool ShouldReplacePixelsByAlphaThreshold(float threshold) + where TPixel : unmanaged, IPixel + => threshold > 0 && TPixel.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; + + /// + /// Replaces pixels in a span with fully transparent pixels based on an alpha threshold. + /// + /// A span of color vectors that will be checked for transparency and potentially modified. + /// The alpha threshold used to determine if a pixel is transparent. + public static void ReplacePixelsByAlphaThreshold(Span source, float threshold) + { + if (Vector512.IsHardwareAccelerated && source.Length >= 4) + { + Vector512 threshold512 = Vector512.Create(threshold); + Span> source512 = MemoryMarshal.Cast>(source); + for (int i = 0; i < source512.Length; i++) + { + ref Vector512 v = ref source512[i]; + + // Do `vector < threshold` + Vector512 mask = Vector512.LessThan(v, threshold512); + + // Replicate the result for W to all elements (is AllBitsSet if the W was less than threshold and Zero otherwise) + mask = Vector512.Shuffle(mask, Vector512.Create(3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15)); + + // Use the mask to select the replacement vector + // (replacement & mask) | (v512 & ~mask) + v = Vector512.ConditionalSelect(mask, Vector512.Zero, v); + } + + int m = Numerics.Modulo4(source.Length); + if (m != 0) + { + for (int i = source.Length - m; i < source.Length; i++) + { + if (source[i].W < threshold) + { + source[i] = Vector4.Zero; + } + } + } + } + else if (Vector256.IsHardwareAccelerated && source.Length >= 2) + { + Vector256 threshold256 = Vector256.Create(threshold); + Span> source256 = MemoryMarshal.Cast>(source); + for (int i = 0; i < source256.Length; i++) + { + ref Vector256 v = ref source256[i]; + + // Do `vector < threshold` + Vector256 mask = Vector256.LessThan(v, threshold256); + + // Replicate the result for W to all elements (is AllBitsSet if the W was less than threshold and Zero otherwise) + mask = Vector256.Shuffle(mask, Vector256.Create(3, 3, 3, 3, 7, 7, 7, 7)); + + // Use the mask to select the replacement vector + // (replacement & mask) | (v256 & ~mask) + v = Vector256.ConditionalSelect(mask, Vector256.Zero, v); + } + + int m = Numerics.Modulo2(source.Length); + if (m != 0) + { + for (int i = source.Length - m; i < source.Length; i++) + { + if (source[i].W < threshold) + { + source[i] = Vector4.Zero; + } + } + } + } + else if (Vector128.IsHardwareAccelerated) + { + Vector128 threshold128 = Vector128.Create(threshold); + + for (int i = 0; i < source.Length; i++) + { + ref Vector4 v = ref source[i]; + Vector128 v128 = v.AsVector128(); + + // Do `vector < threshold` + Vector128 mask = Vector128.LessThan(v128, threshold128); + + // Replicate the result for W to all elements (is AllBitsSet if the W was less than threshold and Zero otherwise) + mask = Vector128.Shuffle(mask, Vector128.Create(3, 3, 3, 3)); + + // Use the mask to select the replacement vector + // (replacement & mask) | (v128 & ~mask) + v = Vector128.ConditionalSelect(mask, Vector128.Zero, v128).AsVector4(); + } + } + else + { + for (int i = 0; i < source.Length; i++) + { + if (source[i].W < threshold) + { + source[i] = Vector4.Zero; + } + } + } + } + /// /// Helper method for throwing an exception when a frame quantizer palette has /// been requested but not built yet. @@ -20,12 +149,13 @@ public static class QuantizerUtilities /// The pixel format. /// The frame quantizer palette. /// - /// The palette has not been built via + /// The palette has not been built via /// + [MethodImpl(InliningOptions.ColdPath)] public static void CheckPaletteState(in ReadOnlyMemory palette) where TPixel : unmanaged, IPixel { - if (palette.Equals(default)) + if (palette.IsEmpty) { throw new InvalidOperationException("Frame Quantizer palette has not been built."); } @@ -50,11 +180,10 @@ public static IndexedImageFrame BuildPaletteAndQuantizeFrame( Guard.NotNull(quantizer, nameof(quantizer)); Guard.NotNull(source, nameof(source)); - Rectangle interest = Rectangle.Intersect(source.Bounds(), bounds); + Rectangle interest = Rectangle.Intersect(source.Bounds, bounds); Buffer2DRegion region = source.PixelBuffer.GetRegion(interest); - // Collect the palette. Required before the second pass runs. - quantizer.AddPaletteColors(region); + quantizer.AddPaletteColors(in region); return quantizer.QuantizeFrame(source, bounds); } @@ -77,7 +206,7 @@ public static IndexedImageFrame QuantizeFrame( where TPixel : unmanaged, IPixel { Guard.NotNull(source, nameof(source)); - Rectangle interest = Rectangle.Intersect(source.Bounds(), bounds); + Rectangle interest = Rectangle.Intersect(source.Bounds, bounds); IndexedImageFrame destination = new( quantizer.Configuration, @@ -114,7 +243,7 @@ public static void BuildPalette( { foreach (Buffer2DRegion region in pixelSamplingStrategy.EnumeratePixelRegions(source)) { - quantizer.AddPaletteColors(region); + quantizer.AddPaletteColors(in region); } } @@ -133,11 +262,72 @@ public static void BuildPalette( { foreach (Buffer2DRegion region in pixelSamplingStrategy.EnumeratePixelRegions(source)) { - quantizer.AddPaletteColors(region); + quantizer.AddPaletteColors(in region); + } + } + + internal static void AddPaletteColors( + ref TFrameQuantizer quantizer, + in Buffer2DRegion source, + in TDelegate rowDelegate) + where TFrameQuantizer : struct, IQuantizer + where TPixel : unmanaged, IPixel + where TPixel2 : unmanaged, IPixel + where TDelegate : struct, IQuantizingPixelRowDelegate + { + Configuration configuration = quantizer.Configuration; + float threshold = quantizer.Options.TransparencyThreshold; + TransparentColorMode mode = quantizer.Options.TransparentColorMode; + + using IMemoryOwner delegateRowOwner = configuration.MemoryAllocator.Allocate(source.Width); + Span delegateRow = delegateRowOwner.Memory.Span; + + bool replaceByThreshold = ShouldReplacePixelsByAlphaThreshold(threshold); + bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels(mode); + + if (replaceByThreshold || replaceTransparent) + { + using IMemoryOwner vectorRowOwner = configuration.MemoryAllocator.Allocate(source.Width); + Span vectorRow = vectorRowOwner.Memory.Span; + + if (replaceByThreshold) + { + for (int y = 0; y < source.Height; y++) + { + Span sourceRow = source.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + ReplacePixelsByAlphaThreshold(vectorRow, threshold); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, delegateRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + rowDelegate.Invoke(delegateRow, y); + } + } + else + { + for (int y = 0; y < source.Height; y++) + { + Span sourceRow = source.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + EncodingUtilities.ReplaceTransparentPixels(vectorRow); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, delegateRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + rowDelegate.Invoke(delegateRow, y); + } + } + } + else + { + for (int y = 0; y < source.Height; y++) + { + Span sourceRow = source.DangerousGetRowSpan(y); + PixelOperations.Instance.To(configuration, sourceRow, delegateRow); + rowDelegate.Invoke(delegateRow, y); + } } } - [MethodImpl(InliningOptions.ShortMethod)] private static void SecondPass( ref TFrameQuantizer quantizer, ImageFrame source, @@ -146,28 +336,111 @@ private static void SecondPass( where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { + float threshold = quantizer.Options.TransparencyThreshold; + bool replaceByThreshold = ShouldReplacePixelsByAlphaThreshold(threshold); + + TransparentColorMode mode = quantizer.Options.TransparentColorMode; + bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels(mode); + IDither? dither = quantizer.Options.Dither; Buffer2D sourceBuffer = source.PixelBuffer; + Buffer2DRegion region = sourceBuffer.GetRegion(bounds); + + Configuration configuration = quantizer.Configuration; + using IMemoryOwner vectorOwner = configuration.MemoryAllocator.Allocate(region.Width); + Span vectorRow = vectorOwner.Memory.Span; if (dither is null) { - int offsetY = bounds.Top; - int offsetX = bounds.Left; + using IMemoryOwner quantizingRowOwner = configuration.MemoryAllocator.Allocate(region.Width); + Span quantizingRow = quantizingRowOwner.Memory.Span; - for (int y = 0; y < destination.Height; y++) + // This is NOT a clone so we DO NOT write back to the source. + if (replaceByThreshold || replaceTransparent) { - ReadOnlySpan sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY); + if (replaceByThreshold) + { + for (int y = 0; y < region.Height; y++) + { + Span sourceRow = region.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + ReplacePixelsByAlphaThreshold(vectorRow, threshold); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, quantizingRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Span destinationRow = destination.GetWritablePixelRowSpanUnsafe(y); + for (int x = 0; x < destinationRow.Length; x++) + { + destinationRow[x] = quantizer.GetQuantizedColor(quantizingRow[x], out TPixel _); + } + } + } + else + { + for (int y = 0; y < region.Height; y++) + { + Span sourceRow = region.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + EncodingUtilities.ReplaceTransparentPixels(vectorRow); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, quantizingRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Span destinationRow = destination.GetWritablePixelRowSpanUnsafe(y); + for (int x = 0; x < destinationRow.Length; x++) + { + destinationRow[x] = quantizer.GetQuantizedColor(quantizingRow[x], out TPixel _); + } + } + } + + return; + } + + for (int y = 0; y < region.Height; y++) + { + ReadOnlySpan sourceRow = region.DangerousGetRowSpan(y); Span destinationRow = destination.GetWritablePixelRowSpanUnsafe(y); for (int x = 0; x < destinationRow.Length; x++) { - destinationRow[x] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x + offsetX], out TPixel _); + destinationRow[x] = quantizer.GetQuantizedColor(sourceRow[x], out TPixel _); } } return; } + // This is a clone so we write back to the source. + if (replaceByThreshold || replaceTransparent) + { + if (replaceByThreshold) + { + for (int y = 0; y < region.Height; y++) + { + Span sourceRow = region.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + ReplacePixelsByAlphaThreshold(vectorRow, threshold); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, sourceRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + } + } + else + { + for (int y = 0; y < region.Height; y++) + { + Span sourceRow = region.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + EncodingUtilities.ReplaceTransparentPixels(vectorRow); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorRow, sourceRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + } + } + } + dither.ApplyQuantizationDither(ref quantizer, source, destination, bounds); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs index 604cae6681..fa1763367c 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; /// /// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4. /// -public class WebSafePaletteQuantizer : PaletteQuantizer +public sealed class WebSafePaletteQuantizer : PaletteQuantizer { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs index 023ee7f2e0..cd7b80e81d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; /// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. /// The hex codes were collected and defined by Nicholas Rougeux /// -public class WernerPaletteQuantizer : PaletteQuantizer +public sealed class WernerPaletteQuantizer : PaletteQuantizer { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index f6928c3dd4..b4b95adc1b 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -43,30 +43,10 @@ internal struct WuQuantizer : IQuantizer // The following two variables determine the amount of bits to preserve when calculating the histogram. // Reducing the value of these numbers the granularity of the color maps produced, making it much faster // and using much less memory but potentially less accurate. Current results are very good though! - - /// - /// The index bits. 6 in original code. - /// private const int IndexBits = 5; - - /// - /// The index alpha bits. 3 in original code. - /// private const int IndexAlphaBits = 5; - - /// - /// The index count. - /// private const int IndexCount = (1 << IndexBits) + 1; - - /// - /// The index alpha count. - /// private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1; - - /// - /// The table length. Now 1185921. originally 2471625. - /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; private readonly IMemoryOwner momentsOwner; @@ -75,14 +55,14 @@ internal struct WuQuantizer : IQuantizer private ReadOnlyMemory palette; private int maxColors; private readonly Box[] colorCube; - private EuclideanPixelMap? pixelMap; + private PixelMap? pixelMap; private readonly bool isDithering; private bool isDisposed; /// /// Initializes a new instance of the struct. /// - /// The configuration which allows altering default behaviour or extending the library. + /// The configuration which allows altering default behavior or extending the library. /// The quantizer options defining quantization rules. [MethodImpl(InliningOptions.ShortMethod)] public WuQuantizer(Configuration configuration, QuantizerOptions options) @@ -101,7 +81,7 @@ public WuQuantizer(Configuration configuration, QuantizerOptions options) this.isDisposed = false; this.pixelMap = default; this.palette = default; - this.isDithering = this.isDithering = this.Options.Dither is not null; + this.isDithering = this.Options.Dither is not null; } /// @@ -111,75 +91,91 @@ public WuQuantizer(Configuration configuration, QuantizerOptions options) public QuantizerOptions Options { get; } /// - public readonly ReadOnlyMemory Palette + public ReadOnlyMemory Palette { get { - QuantizerUtilities.CheckPaletteState(in this.palette); + if (this.palette.IsEmpty) + { + this.ResolvePalette(); + QuantizerUtilities.CheckPaletteState(in this.palette); + } + return this.palette; } } /// - public void AddPaletteColors(Buffer2DRegion pixelRegion) + public readonly void AddPaletteColors(in Buffer2DRegion pixelRegion) + { + PixelRowDelegate pixelRowDelegate = new(ref Unsafe.AsRef(in this)); + QuantizerUtilities.AddPaletteColors, TPixel, Rgba32, PixelRowDelegate>( + ref Unsafe.AsRef(in this), + in pixelRegion, + in pixelRowDelegate); + } + + /// + /// Once all histogram data has been accumulated, this method computes the moments, + /// splits the color cube, and resolves the final palette from the accumulated histogram. + /// + private void ResolvePalette() { - // TODO: Something is destroying the existing palette when adding new colors. - // When the QuantizingImageEncoder.PixelSamplingStrategy is DefaultPixelSamplingStrategy - // this leads to performance issues + the palette is not preserved. - // https://github.com/SixLabors/ImageSharp/issues/2498 - this.Build3DHistogram(pixelRegion); + // Calculate the cumulative moments from the accumulated histogram. this.Get3DMoments(this.memoryAllocator); + + // Partition the histogram into color cubes. this.BuildCube(); - // Slice again since maxColors has been updated since the buffer was created. + // Compute the palette colors from the resolved cubes. Span paletteSpan = this.paletteOwner.GetSpan()[..this.maxColors]; ReadOnlySpan momentsSpan = this.momentsOwner.GetSpan(); + + float transparencyThreshold = this.Options.TransparencyThreshold; for (int k = 0; k < paletteSpan.Length; k++) { this.Mark(ref this.colorCube[k], (byte)k); - Moment moment = Volume(ref this.colorCube[k], momentsSpan); - if (moment.Weight > 0) { - ref TPixel color = ref paletteSpan[k]; - color.FromScaledVector4(moment.Normalize()); + Vector4 normalized = moment.Normalize(); + if (normalized.W < transparencyThreshold) + { + normalized = Vector4.Zero; + } + + // Histogram moments are accumulated from straight-alpha Rgba32 values, so the normalized palette color is unassociated. + paletteSpan[k] = TPixel.FromUnassociatedScaledVector4(normalized); } } - ReadOnlyMemory result = this.paletteOwner.Memory[..paletteSpan.Length]; - if (this.isDithering) + // Update the palette to the new computed colors. + this.palette = this.paletteOwner.Memory[..paletteSpan.Length]; + + // Create the pixel map if dithering is enabled. + if (this.isDithering && this.pixelMap is null) { - // When called multiple times by QuantizerUtilities.BuildPalette - // this prevents memory churn caused by reallocation. - if (this.pixelMap is null) - { - this.pixelMap = new EuclideanPixelMap(this.Configuration, result); - } - else - { - this.pixelMap.Clear(result); - } + this.pixelMap = PixelMapFactory.Create(this.Configuration, this.palette, this.Options.ColorMatchingMode); } - - this.palette = result; } /// [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) { + // Due to the addition of new colors by dithering that are not part of the original histogram, + // the color cube might not match the correct color. + // In this case, we must use the pixel map to get the closest color. if (this.isDithering) { 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; @@ -190,7 +186,7 @@ public readonly byte GetQuantizedColor(TPixel color, out TPixel match) ReadOnlySpan tagSpan = this.tagsOwner.GetSpan(); byte index = tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.palette.Span); - match = Unsafe.Add(ref paletteRef, index); + match = Unsafe.Add(ref paletteRef, (nuint)index); return index; } @@ -361,31 +357,19 @@ private static Moment Top(ref Box cube, int direction, int position, ReadOnlySpa /// /// Builds a 3-D color histogram of counts, r/g/b, c^2. /// - /// The source pixel data. - private readonly void Build3DHistogram(Buffer2DRegion source) + /// The source pixel data. + private readonly void Build3DHistogram(ReadOnlySpan pixels) { - Span momentSpan = this.momentsOwner.GetSpan(); - - // Build up the 3-D color histogram - using IMemoryOwner buffer = this.memoryAllocator.Allocate(source.Width); - Span bufferSpan = buffer.GetSpan(); - - for (int y = 0; y < source.Height; y++) + Span moments = this.momentsOwner.GetSpan(); + for (int x = 0; x < pixels.Length; x++) { - Span row = source.DangerousGetRowSpan(y); - PixelOperations.Instance.ToRgba32(this.Configuration, row, bufferSpan); - - for (int x = 0; x < bufferSpan.Length; x++) - { - Rgba32 rgba = bufferSpan[x]; - - int r = (rgba.R >> (8 - IndexBits)) + 1; - int g = (rgba.G >> (8 - IndexBits)) + 1; - int b = (rgba.B >> (8 - IndexBits)) + 1; - int a = (rgba.A >> (8 - IndexAlphaBits)) + 1; + Rgba32 rgba = pixels[x]; + int r = (rgba.R >> (8 - IndexBits)) + 1; + int g = (rgba.G >> (8 - IndexBits)) + 1; + int b = (rgba.B >> (8 - IndexBits)) + 1; + int a = (rgba.A >> (8 - IndexAlphaBits)) + 1; - momentSpan[GetPaletteIndex(r, g, b, a)] += rgba; - } + moments[GetPaletteIndex(r, g, b, a)] += rgba; } } @@ -549,7 +533,7 @@ private readonly float Maximize(ref Box cube, int direction, int first, int last /// 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); @@ -897,4 +881,13 @@ public override readonly int GetHashCode() return hash.ToHashCode(); } } + + private readonly struct PixelRowDelegate : IQuantizingPixelRowDelegate + { + private readonly WuQuantizer quantizer; + + public PixelRowDelegate(ref WuQuantizer quantizer) => this.quantizer = quantizer; + + public void Invoke(ReadOnlySpan row, int rowIndex) => this.quantizer.Build3DHistogram(row); + } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 1d82dd12ad..dd992df8cc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -16,6 +17,7 @@ internal class CropProcessor : TransformProcessor where TPixel : unmanaged, IPixel { private readonly Rectangle cropRectangle; + private readonly Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. @@ -26,10 +28,21 @@ internal class CropProcessor : TransformProcessor /// The source area to process for the current processor instance. public CropProcessor(Configuration configuration, CropProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) - => this.cropRectangle = definition.CropRectangle; + { + this.cropRectangle = definition.CropRectangle; + + // Calculate the transform matrix from the crop operation to allow us + // to update any metadata that represents pixel coordinates in the source image. + this.transformMatrix = new ProjectiveTransformBuilder() + .AppendTranslation(new PointF(-this.cropRectangle.X, -this.cropRectangle.Y)) + .BuildMatrix(sourceRectangle); + } + + /// + protected override Size GetDestinationSize() => new(this.cropRectangle.Width, this.cropRectangle.Height); /// - protected override Size GetDestinationSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); + protected override Matrix4x4 GetTransformMatrix() => this.transformMatrix; /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) @@ -40,22 +53,20 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index 2eda61e41d..b9973eea35 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -36,7 +35,7 @@ protected override void BeforeImageApply() // TODO: This is clunky. We should add behavior enum to ExtractFrame. // All frames have be the same size so we only need to calculate the correct dimensions for the first frame - using (Image temp = new(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() })) + using (Image temp = new(this.Configuration, this.Source.Metadata.DeepClone(), [this.Source.Frames.RootFrame.Clone()])) { Configuration configuration = this.Source.Configuration; @@ -97,13 +96,16 @@ private static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, int height = bitmap.Height; Point topLeft = default; Point bottomRight = default; + + // BinaryThresholdProcessor produces opaque black and white, so association no longer affects the components. Scaled vectors still normalize pixel formats whose native vectors use another range. Func, int, int, float, bool> delegateFunc = channel switch { - RgbaComponent.R => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon, - RgbaComponent.G => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon, - RgbaComponent.B => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon, - _ => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon, + RgbaComponent.R => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToUnassociatedScaledVector4().X - b) > Constants.Epsilon, + RgbaComponent.G => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToUnassociatedScaledVector4().Y - b) > Constants.Epsilon, + RgbaComponent.B => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToUnassociatedScaledVector4().Z - b) > Constants.Epsilon, + _ => (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToUnassociatedScaledVector4().W - b) > Constants.Epsilon, }; + int GetMinY(ImageFrame pixels) { for (int y = 0; y < height; y++) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs index 321ecf9684..3c11c32b44 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs @@ -11,12 +11,12 @@ public interface ISwizzler /// /// Gets the size of the image after transformation. /// - Size DestinationSize { get; } + public Size DestinationSize { get; } /// /// Applies the swizzle transformation to a given point. /// /// Point to transform. /// The transformed point. - Point Transform(Point point); + public Point Transform(Point point); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs index 30c279e593..afd1b70b72 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs @@ -21,7 +21,7 @@ public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targe Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler); - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index c5c2a778eb..61cde6e2a7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -18,6 +18,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR { private readonly Size destinationSize; private readonly Matrix3x2 transformMatrix; + private readonly Matrix4x4 transformMatrix4x4; private readonly IResampler resampler; private ImageFrame? source; private ImageFrame? destination; @@ -34,6 +35,7 @@ public AffineTransformProcessor(Configuration configuration, AffineTransformProc { this.destinationSize = definition.DestinationSize; this.transformMatrix = definition.TransformMatrix; + this.transformMatrix4x4 = new(this.transformMatrix); this.resampler = definition.Sampler; } @@ -47,6 +49,9 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame + protected override Matrix4x4 GetTransformMatrix() => this.transformMatrix4x4; + /// public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler @@ -61,47 +66,49 @@ public void ApplyTransform(in TResampler sampler) if (matrix.Equals(Matrix3x2.Identity)) { // The clone will be blank here copy all the pixel data over - var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds); Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest); - Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest); + Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest); for (int y = 0; y < sourceBuffer.Height; y++) { - sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y)); + sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y)); } return; } - // Convert from screen to world space. + // All matrices are defined in normalized coordinate space so we need to convert to pixel space. + // After normalization we need to invert the matrix for correct sampling. + matrix = TransformUtilities.NormalizeToPixel(matrix); Matrix3x2.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) { - var nnOperation = new NNAffineOperation( + NNAffineOperation nnOperation = new( source.PixelBuffer, - Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + Rectangle.Intersect(this.SourceRectangle, source.Bounds), destination.PixelBuffer, matrix); ParallelRowIterator.IterateRows( configuration, - destination.Bounds(), + destination.Bounds, in nnOperation); return; } - var operation = new AffineOperation( + AffineOperation operation = new( configuration, source.PixelBuffer, - Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + Rectangle.Intersect(this.SourceRectangle, source.Bounds), destination.PixelBuffer, in sampler, matrix); ParallelRowIterator.IterateRowIntervals, Vector4>( configuration, - destination.Bounds(), + destination.Bounds, in operation); } @@ -128,17 +135,17 @@ public NNAffineOperation( [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { - Span destRow = this.destination.DangerousGetRowSpan(y); + Span destinationRowSpan = this.destination.DangerousGetRowSpan(y); - for (int x = 0; x < destRow.Length; x++) + for (int x = 0; x < destinationRowSpan.Length; x++) { - var point = Vector2.Transform(new Vector2(x, y), this.matrix); + Vector2 point = Vector2.Transform(new Vector2(x, y), this.matrix); int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); if (this.bounds.Contains(px, py)) { - destRow[x] = this.source.GetElementUnsafe(px, py); + destinationRowSpan[x] = this.source.GetElementUnsafe(px, py); } } } @@ -192,19 +199,18 @@ public void Invoke(in RowInterval rows, Span span) int maxY = this.bounds.Bottom - 1; int minX = this.bounds.X; int maxX = this.bounds.Right - 1; + PixelOperations pixelOperations = PixelOperations.Instance; for (int y = rows.Min; y < rows.Max; y++) { - Span rowSpan = this.destination.DangerousGetRowSpan(y); - PixelOperations.Instance.ToVector4( - this.configuration, - rowSpan, - span, - PixelConversionModifiers.Scale); + Span destinationRowSpan = this.destination.DangerousGetRowSpan(y); + + // The temporary row is reused, so clear values for out-of-bounds samples that skip the resampling loop below. + span.Clear(); for (int x = 0; x < span.Length; x++) { - var point = Vector2.Transform(new Vector2(x, y), matrix); + Vector2 point = Vector2.Transform(new Vector2(x, y), matrix); float pY = point.Y; float pX = point.X; @@ -218,17 +224,18 @@ public void Invoke(in RowInterval rows, Span span) continue; } + // Resampling interpolates color with coverage, so use associated vectors to prevent transparent RGB from bleeding into visible edges. Vector4 sum = Vector4.Zero; for (int yK = top; yK <= bottom; yK++) { + Span sourceRowSpan = this.source.DangerousGetRowSpan(yK); float yWeight = sampler.GetValue(yK - pY); for (int xK = left; xK <= right; xK++) { float xWeight = sampler.GetValue(xK - pX); - Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4(); - Numerics.Premultiply(ref current); + Vector4 current = sourceRowSpan[xK].ToAssociatedScaledVector4(); sum += current * xWeight * yWeight; } } @@ -236,12 +243,7 @@ public void Invoke(in RowInterval rows, Span span) span[x] = sum; } - Numerics.UnPremultiply(span); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - rowSpan, - PixelConversionModifiers.Scale); + pixelOperations.FromVector4Destructive(this.configuration, span, destinationRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs index 14da3ac890..86ba2f0f9a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers; +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -17,16 +18,45 @@ internal class FlipProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly FlipProcessor definition; + private readonly Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. /// - /// The configuration which allows altering default behaviour or extending the library. + /// The configuration which allows altering default behavior or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. public FlipProcessor(Configuration configuration, FlipProcessor definition, Image source, Rectangle sourceRectangle) - : base(configuration, source, sourceRectangle) => this.definition = definition; + : base(configuration, source, sourceRectangle) + { + this.definition = definition; + + // Calculate the transform matrix from the flip operation to allow us + // to update any metadata that represents pixel coordinates in the source image. + ProjectiveTransformBuilder builder = new(); + switch (this.definition.FlipMode) + { + // No default needed as we have already set the pixels. + case FlipMode.Vertical: + + // Flip vertically by scaling the Y axis by -1 and translating the Y coordinate. + builder.AppendScale(new Vector2(1, -1)) + .AppendTranslation(new PointF(0, this.SourceRectangle.Height - 1)); + break; + case FlipMode.Horizontal: + + // Flip horizontally by scaling the X axis by -1 and translating the X coordinate. + builder.AppendScale(new Vector2(-1, 1)) + .AppendTranslation(new PointF(this.SourceRectangle.Width - 1, 0)); + break; + default: + this.transformMatrix = Matrix4x4.Identity; + return; + } + + this.transformMatrix = builder.BuildMatrix(sourceRectangle); + } /// protected override void OnFrameApply(ImageFrame source) @@ -43,6 +73,14 @@ protected override void OnFrameApply(ImageFrame source) } } + /// + protected override void AfterFrameApply(ImageFrame source) + => source.Metadata.AfterFrameApply(source, source, this.transformMatrix); + + /// + protected override void AfterImageApply() + => this.Source.Metadata.AfterImageApply(this.Source, this.transformMatrix); + /// /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image. /// @@ -72,10 +110,10 @@ private static void FlipX(Buffer2D source, Configuration configuration) /// The configuration. private static void FlipY(ImageFrame source, Configuration configuration) { - var operation = new RowOperation(source.PixelBuffer); + RowOperation operation = new(source.PixelBuffer); ParallelRowIterator.IterateRows( configuration, - source.Bounds(), + source.Bounds, in operation); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs new file mode 100644 index 0000000000..1190de4352 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms.Linear; + +/// +/// Represents a solver for systems of linear equations using the Gaussian Elimination method. +/// This class applies Gaussian Elimination to transform the matrix into row echelon form and then performs back substitution to find the solution vector. +/// This implementation is based on: +/// +internal static class GaussianEliminationSolver +{ + /// + /// Solves the system of linear equations represented by the given matrix and result vector using Gaussian Elimination. + /// + /// The square matrix representing the coefficients of the linear equations. + /// The vector representing the constants on the right-hand side of the linear equations. + /// Thrown if the matrix is singular and cannot be solved. + /// + /// The matrix passed to this method must be a square matrix. + /// If the matrix is singular (i.e., has no unique solution), an will be thrown. + /// + public static void Solve(double[][] matrix, double[] result) + { + TransformToRowEchelonForm(matrix, result); + ApplyBackSubstitution(matrix, result); + } + + private static void TransformToRowEchelonForm(double[][] matrix, double[] result) + { + int colCount = matrix.Length; + int rowCount = matrix[0].Length; + int pivotRow = 0; + for (int pivotCol = 0; pivotCol < colCount; pivotCol++) + { + double maxValue = double.Abs(matrix[pivotRow][pivotCol]); + int maxIndex = pivotRow; + for (int r = pivotRow + 1; r < rowCount; r++) + { + double value = double.Abs(matrix[r][pivotCol]); + if (value > maxValue) + { + maxIndex = r; + maxValue = value; + } + } + + if (matrix[maxIndex][pivotCol] == 0) + { + throw new NotSupportedException("Matrix is singular and cannot be solve"); + } + + (matrix[pivotRow], matrix[maxIndex]) = (matrix[maxIndex], matrix[pivotRow]); + (result[pivotRow], result[maxIndex]) = (result[maxIndex], result[pivotRow]); + + for (int r = pivotRow + 1; r < rowCount; r++) + { + double fraction = matrix[r][pivotCol] / matrix[pivotRow][pivotCol]; + for (int c = pivotCol + 1; c < colCount; c++) + { + matrix[r][c] -= matrix[pivotRow][c] * fraction; + } + + result[r] -= result[pivotRow] * fraction; + matrix[r][pivotCol] = 0; + } + + pivotRow++; + } + } + + private static void ApplyBackSubstitution(double[][] matrix, double[] result) + { + int rowCount = matrix[0].Length; + + for (int row = rowCount - 1; row >= 0; row--) + { + result[row] /= matrix[row][row]; + + for (int r = 0; r < row; r++) + { + result[r] -= result[row] * matrix[r][row]; + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs index 9e9507b737..1c457e84c0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs @@ -21,7 +21,7 @@ public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size t Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler); - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index b741dc4ee6..90ca8743c0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -47,6 +47,9 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame + protected override Matrix4x4 GetTransformMatrix() => this.transformMatrix; + /// public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler @@ -61,47 +64,49 @@ public void ApplyTransform(in TResampler sampler) if (matrix.Equals(Matrix4x4.Identity)) { // The clone will be blank here copy all the pixel data over - var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds()); + Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds); Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest); - Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest); + Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest); for (int y = 0; y < sourceBuffer.Height; y++) { - sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y)); + sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y)); } return; } - // Convert from screen to world space. + // All matrices are defined in normalized coordinate space so we need to convert to pixel space. + // After normalization we need to invert the matrix for correct sampling. + matrix = TransformUtilities.NormalizeToPixel(matrix); Matrix4x4.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) { - var nnOperation = new NNProjectiveOperation( + NNProjectiveOperation nnOperation = new( source.PixelBuffer, - Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + Rectangle.Intersect(this.SourceRectangle, source.Bounds), destination.PixelBuffer, matrix); ParallelRowIterator.IterateRows( configuration, - destination.Bounds(), + destination.Bounds, in nnOperation); return; } - var operation = new ProjectiveOperation( + ProjectiveOperation operation = new( configuration, source.PixelBuffer, - Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + Rectangle.Intersect(this.SourceRectangle, source.Bounds), destination.PixelBuffer, in sampler, matrix); ParallelRowIterator.IterateRowIntervals, Vector4>( configuration, - destination.Bounds(), + destination.Bounds, in operation); } @@ -128,17 +133,17 @@ public NNProjectiveOperation( [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { - Span destRow = this.destination.DangerousGetRowSpan(y); + Span destinationRowSpan = this.destination.DangerousGetRowSpan(y); - for (int x = 0; x < destRow.Length; x++) + for (int x = 0; x < destinationRowSpan.Length; x++) { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); if (this.bounds.Contains(px, py)) { - destRow[x] = this.source.GetElementUnsafe(px, py); + destinationRowSpan[x] = this.source.GetElementUnsafe(px, py); } } } @@ -192,19 +197,18 @@ public void Invoke(in RowInterval rows, Span span) int maxY = this.bounds.Bottom - 1; int minX = this.bounds.X; int maxX = this.bounds.Right - 1; + PixelOperations pixelOperations = PixelOperations.Instance; for (int y = rows.Min; y < rows.Max; y++) { - Span rowSpan = this.destination.DangerousGetRowSpan(y); - PixelOperations.Instance.ToVector4( - this.configuration, - rowSpan, - span, - PixelConversionModifiers.Scale); + Span destinationRowSpan = this.destination.DangerousGetRowSpan(y); + + // The temporary row is reused, so clear values for out-of-bounds samples that skip the resampling loop below. + span.Clear(); for (int x = 0; x < span.Length; x++) { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, matrix); float pY = point.Y; float pX = point.X; @@ -218,17 +222,18 @@ public void Invoke(in RowInterval rows, Span span) continue; } + // Resampling interpolates color with coverage, so use associated vectors to prevent transparent RGB from bleeding into visible edges. Vector4 sum = Vector4.Zero; for (int yK = top; yK <= bottom; yK++) { + Span sourceRowSpan = this.source.DangerousGetRowSpan(yK); float yWeight = sampler.GetValue(yK - pY); for (int xK = left; xK <= right; xK++) { float xWeight = sampler.GetValue(xK - pX); - Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4(); - Numerics.Premultiply(ref current); + Vector4 current = sourceRowSpan[xK].ToAssociatedScaledVector4(); sum += current * xWeight * yWeight; } } @@ -236,12 +241,7 @@ public void Invoke(in RowInterval rows, Span span) span[x] = sum; } - Numerics.UnPremultiply(span); - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - rowSpan, - PixelConversionModifiers.Scale); + pixelOperations.FromVector4Destructive(this.configuration, span, destinationRowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs index 0af2b268a1..ea0f1c1137 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs @@ -28,14 +28,14 @@ public RotateProcessor(float degrees, Size sourceSize) /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize, TransformSpace.Pixel), + TransformUtilities.CreateRotationTransformMatrixDegrees(degrees, sourceSize), sampler, sourceSize) => this.Degrees = degrees; // Helper constructor private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) - : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize, TransformSpace.Pixel)) + : base(rotationMatrix, sampler, TransformUtilities.GetTransformedCanvasSize(rotationMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs index 3ffb1ab518..e9d0ecf57d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs @@ -97,7 +97,7 @@ private bool OptimizedApply( if (MathF.Abs(degrees) < Constants.Epsilon) { // The destination will be blank here so copy all the pixel data over - source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); + source.PixelBuffer.CopyTo(destination.PixelBuffer); return true; } @@ -130,40 +130,40 @@ private bool OptimizedApply( /// The configuration. private static void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { - var operation = new Rotate180RowOperation(source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); + Rotate180RowOperation operation = new(source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRows( configuration, - source.Bounds(), + source.Bounds, in operation); } /// - /// Rotates the image 270 degrees clockwise at the centre point. + /// Rotates the image 270 degrees clockwise at the center point. /// /// The source image. /// The destination image. /// The configuration. private static void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { - var operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); + Rotate270RowIntervalOperation operation = new(destination.Bounds, source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRowIntervals( configuration, - source.Bounds(), + source.Bounds, in operation); } /// - /// Rotates the image 90 degrees clockwise at the centre point. + /// Rotates the image 90 degrees clockwise at the center point. /// /// The source image. /// The destination image. /// The configuration. private static void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { - var operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); + Rotate90RowOperation operation = new(destination.Bounds, source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRows( configuration, - source.Bounds(), + source.Bounds, in operation); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs index 0bbc8e0f60..9c23a95325 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs @@ -30,7 +30,7 @@ public SkewProcessor(float degreesX, float degreesY, Size sourceSize) /// The source image size public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize, TransformSpace.Pixel), + TransformUtilities.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize), sampler, sourceSize) { @@ -40,7 +40,7 @@ public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size so // Helper constructor: private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) - : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize, TransformSpace.Pixel)) + : base(skewMatrix, sampler, TransformUtilities.GetTransformedCanvasSize(skewMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 51a739d35e..c0380224c4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,11 +14,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms; /// internal readonly unsafe struct ResizeKernel { + /// + /// The buffer with the convolution factors. + /// Note that when FMA is supported, this is of size 4x that reported in . + /// private readonly float* bufferPtr; /// /// Initializes a new instance of the struct. /// + /// The starting index for the destination row. + /// The pointer to the buffer with the convolution factors. + /// The length of the kernel. [MethodImpl(InliningOptions.ShortMethod)] internal ResizeKernel(int startIndex, float* bufferPtr, int length) { @@ -27,6 +34,15 @@ internal ResizeKernel(int startIndex, float* bufferPtr, int length) this.Length = length; } + /// + /// Gets a value indicating whether vectorization is supported. + /// + public static bool IsHardwareAccelerated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Vector256.IsHardwareAccelerated; + } + /// /// Gets the start index for the destination row. /// @@ -53,7 +69,15 @@ public int Length public Span Values { [MethodImpl(InliningOptions.ShortMethod)] - get => new(this.bufferPtr, this.Length); + get + { + if (Vector256.IsHardwareAccelerated) + { + return new(this.bufferPtr, this.Length * 4); + } + + return new(this.bufferPtr, this.Length); + } } /// @@ -68,73 +92,45 @@ public Vector4 Convolve(Span rowSpan) [MethodImpl(InliningOptions.ShortMethod)] public Vector4 ConvolveCore(ref Vector4 rowStartRef) { - if (Avx2.IsSupported && Fma.IsSupported) + if (IsHardwareAccelerated) { float* bufferStart = this.bufferPtr; - float* bufferEnd = bufferStart + (this.Length & ~3); + ref Vector4 rowEndRef = ref Unsafe.Add(ref rowStartRef, this.Length & ~3); Vector256 result256_0 = Vector256.Zero; Vector256 result256_1 = Vector256.Zero; - ReadOnlySpan maskBytes = new byte[] - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - }; - Vector256 mask = Unsafe.ReadUnaligned>(ref MemoryMarshal.GetReference(maskBytes)); - while (bufferStart < bufferEnd) + while (Unsafe.IsAddressLessThan(ref rowStartRef, ref rowEndRef)) { - // It is important to use a single expression here so that the JIT will correctly use vfmadd231ps - // for the FMA operation, and execute it directly on the target register and reading directly from - // memory for the first parameter. This skips initializing a SIMD register, and an extra copy. - // The code below should compile in the following assembly on .NET 5 x64: - // - // vmovsd xmm2, [rax] ; load *(double*)bufferStart into xmm2 as [ab, _] - // vpermps ymm2, ymm1, ymm2 ; permute as a float YMM register to [a, a, a, a, b, b, b, b] - // vfmadd231ps ymm0, ymm2, [r8] ; result256_0 = FMA(pixels, factors) + result256_0 - // - // For tracking the codegen issue with FMA, see: https://github.com/dotnet/runtime/issues/12212. - // Additionally, we're also unrolling two computations per each loop iterations to leverage the - // fact that most CPUs have two ports to schedule multiply operations for FMA instructions. - result256_0 = Fma.MultiplyAdd( - Unsafe.As>(ref rowStartRef), - Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)bufferStart).AsSingle(), mask), - result256_0); - - result256_1 = Fma.MultiplyAdd( - Unsafe.As>(ref Unsafe.Add(ref rowStartRef, 2)), - Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)(bufferStart + 2)).AsSingle(), mask), - result256_1); - - bufferStart += 4; - rowStartRef = ref Unsafe.Add(ref rowStartRef, 4); + Vector256 pixels256_0 = Unsafe.As>(ref rowStartRef); + Vector256 pixels256_1 = Unsafe.As>(ref Unsafe.Add(ref rowStartRef, (nuint)2)); + + result256_0 = Vector256_.MultiplyAddEstimate(Vector256.Load(bufferStart), pixels256_0, result256_0); + result256_1 = Vector256_.MultiplyAddEstimate(Vector256.Load(bufferStart + 8), pixels256_1, result256_1); + + bufferStart += 16; + rowStartRef = ref Unsafe.Add(ref rowStartRef, (nuint)4); } - result256_0 = Avx.Add(result256_0, result256_1); + result256_0 += result256_1; if ((this.Length & 3) >= 2) { - result256_0 = Fma.MultiplyAdd( - Unsafe.As>(ref rowStartRef), - Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)bufferStart).AsSingle(), mask), - result256_0); + Vector256 pixels256_0 = Unsafe.As>(ref rowStartRef); + result256_0 = Vector256_.MultiplyAddEstimate(Vector256.Load(bufferStart), pixels256_0, result256_0); - bufferStart += 2; - rowStartRef = ref Unsafe.Add(ref rowStartRef, 2); + bufferStart += 8; + rowStartRef = ref Unsafe.Add(ref rowStartRef, (nuint)2); } - Vector128 result128 = Sse.Add(result256_0.GetLower(), result256_0.GetUpper()); + Vector128 result128 = result256_0.GetLower() + result256_0.GetUpper(); if ((this.Length & 1) != 0) { - result128 = Fma.MultiplyAdd( - Unsafe.As>(ref rowStartRef), - Vector128.Create(*bufferStart), - result128); + Vector128 pixels128 = Unsafe.As>(ref rowStartRef); + result128 = Vector128_.MultiplyAddEstimate(Vector128.Load(bufferStart), pixels128, result128); } - return *(Vector4*)&result128; + return result128.AsVector4(); } else { @@ -149,7 +145,7 @@ public Vector4 ConvolveCore(ref Vector4 rowStartRef) result += rowStartRef * *bufferStart; bufferStart++; - rowStartRef = ref Unsafe.Add(ref rowStartRef, 1); + rowStartRef = ref Unsafe.Add(ref rowStartRef, (nuint)1); } return result; @@ -160,17 +156,32 @@ public Vector4 ConvolveCore(ref Vector4 rowStartRef) /// Copy the contents of altering /// to the value . /// + /// The new value for . [MethodImpl(InliningOptions.ShortMethod)] internal ResizeKernel AlterLeftValue(int left) => new(left, this.bufferPtr, this.Length); - internal void Fill(Span values) + internal void FillOrCopyAndExpand(Span values) { DebugGuard.IsTrue(values.Length == this.Length, nameof(values), "ResizeKernel.Fill: values.Length != this.Length!"); - for (int i = 0; i < this.Length; i++) + if (IsHardwareAccelerated) + { + Vector4* bufferStart = (Vector4*)this.bufferPtr; + ref float valuesStart = ref MemoryMarshal.GetReference(values); + ref float valuesEnd = ref Unsafe.Add(ref valuesStart, values.Length); + + while (Unsafe.IsAddressLessThan(ref valuesStart, ref valuesEnd)) + { + *bufferStart = new Vector4(valuesStart); + + bufferStart++; + valuesStart = ref Unsafe.Add(ref valuesStart, (nuint)1); + } + } + else { - this.Values[i] = (float)values[i]; + values.CopyTo(this.Values); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index ee1ada43ad..d606738abd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -16,6 +16,8 @@ private sealed class PeriodicKernelMap : ResizeKernelMap private readonly int cornerInterval; + private readonly int sourcePeriod; + public PeriodicKernelMap( MemoryAllocator memoryAllocator, int sourceLength, @@ -24,7 +26,8 @@ public PeriodicKernelMap( double scale, int radius, int period, - int cornerInterval) + int cornerInterval, + int sourcePeriod) : base( memoryAllocator, sourceLength, @@ -36,6 +39,7 @@ public PeriodicKernelMap( { this.cornerInterval = cornerInterval; this.period = period; + this.sourcePeriod = sourcePeriod; } internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}"; @@ -54,10 +58,11 @@ protected internal override void Initialize(in TResampler sampler) int bottomStartDest = this.DestinationLength - this.cornerInterval; for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++) { - double center = ((i + .5) * this.ratio) - .5; - int left = (int)TolerantMath.Ceiling(center - this.radius); ResizeKernel kernel = this.kernels[i - this.period]; - this.kernels[i] = kernel.AlterLeftValue(left); + + // Shift the kernel start index by the source-side period so the same weights align to the + // next repeated sampling window in the source image. + this.kernels[i] = kernel.AlterLeftValue(kernel.StartIndex + this.sourcePeriod); } // Build bottom corner data: diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index c1907bb520..0b8106e0be 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -33,7 +33,7 @@ internal partial class ResizeKernelMap : IDisposable private bool isDisposed; // To avoid both GC allocations, and MemoryAllocator ceremony: - private readonly double[] tempValues; + private readonly float[] tempValues; private ResizeKernelMap( MemoryAllocator memoryAllocator, @@ -50,10 +50,12 @@ private ResizeKernelMap( this.sourceLength = sourceLength; this.DestinationLength = destinationLength; this.MaxDiameter = (radius * 2) + 1; - this.data = memoryAllocator.Allocate2D(this.MaxDiameter, bufferHeight, preferContiguosImageBuffers: true, AllocationOptions.Clean); + + int diameter = ResizeKernel.IsHardwareAccelerated ? this.MaxDiameter * 4 : this.MaxDiameter; + this.data = memoryAllocator.Allocate2D(diameter, bufferHeight, preferContiguosImageBuffers: true); this.pinHandle = this.data.DangerousGetSingleMemory().Pin(); this.kernels = new ResizeKernel[destinationLength]; - this.tempValues = new double[this.MaxDiameter]; + this.tempValues = new float[this.MaxDiameter]; } /// @@ -102,6 +104,12 @@ protected virtual void Dispose(bool disposing) [MethodImpl(InliningOptions.ShortMethod)] internal ref ResizeKernel GetKernel(nuint destIdx) => ref this.kernels[(int)destIdx]; + /// + /// Returns a read-only span of over the underlying kernel data. + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal ReadOnlySpan GetKernelSpan() => this.kernels; + /// /// Computes the weights to apply at each pixel when resizing. /// @@ -129,9 +137,13 @@ public static ResizeKernelMap Calculate( int radius = (int)TolerantMath.Ceiling(scale * sampler.Radius); // 'ratio' is a rational number. - // Multiplying it by destSize/GCD(sourceSize, destSize) will result in a whole number "again". - // This value is determining the length of the periods in repeating kernel map rows. - int period = destinationSize / Numerics.GreatestCommonDivisor(sourceSize, destinationSize); + // Multiplying it by destSize/GCD(sourceSize, destinationSize) yields an integer, so every `period` rows + // the destination-space sampling centers repeat their fractional alignment. `period` is the repeat length + // in destination rows, while `sourcePeriod` is the corresponding integer offset in source pixels that + // must be added to the kernel's left index when we reuse the same weights from a previous period. + int gcd = Numerics.GreatestCommonDivisor(sourceSize, destinationSize); + int period = destinationSize / gcd; + int sourcePeriod = sourceSize / gcd; // the center position at i == 0: double center0 = (ratio - 1) * 0.5; @@ -155,23 +167,24 @@ public static ResizeKernelMap Calculate( bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize; ResizeKernelMap result = hasAtLeast2Periods - ? new PeriodicKernelMap( - memoryAllocator, - sourceSize, - destinationSize, - ratio, - scale, - radius, - period, - cornerInterval) - : new ResizeKernelMap( - memoryAllocator, - sourceSize, - destinationSize, - destinationSize, - ratio, - scale, - radius); + ? new PeriodicKernelMap( + memoryAllocator, + sourceSize, + destinationSize, + ratio, + scale, + radius, + period, + cornerInterval, + sourcePeriod) + : new ResizeKernelMap( + memoryAllocator, + sourceSize, + destinationSize, + destinationSize, + ratio, + scale, + radius); result.Initialize(in sampler); @@ -199,6 +212,7 @@ private ResizeKernel BuildKernel(in TResampler sampler, int destRowI where TResampler : struct, IResampler { double center = ((destRowIndex + .5) * this.ratio) - .5; + double scale = this.scale; // Keep inside bounds. int left = (int)TolerantMath.Ceiling(center - this.radius); @@ -214,30 +228,25 @@ private ResizeKernel BuildKernel(in TResampler sampler, int destRowI } ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right); - - Span kernelValues = this.tempValues.AsSpan(0, kernel.Length); - double sum = 0; + Span kernelValues = this.tempValues.AsSpan(0, kernel.Length); + ref float kernelStart = ref MemoryMarshal.GetReference(kernelValues); + float sum = 0; for (int j = left; j <= right; j++) { - double value = sampler.GetValue((float)((j - center) / this.scale)); + float value = sampler.GetValue((float)((j - center) / scale)); sum += value; - - kernelValues[j - left] = value; + kernelStart = value; + kernelStart = ref Unsafe.Add(ref kernelStart, 1); } // Normalize, best to do it here rather than in the pixel loop later on. if (sum > 0) { - for (int j = 0; j < kernel.Length; j++) - { - // weights[w] = weights[w] / sum: - ref double kRef = ref kernelValues[j]; - kRef /= sum; - } + Numerics.Normalize(kernelValues, sum); } - kernel.Fill(kernelValues); + kernel.FillOrCopyAndExpand(kernelValues); return kernel; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 98c2523fae..870c382413 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -21,6 +22,7 @@ internal class ResizeProcessor : TransformProcessor, IResampling private readonly IResampler resampler; private readonly Rectangle destinationRectangle; private Image? destination; + private readonly Matrix4x4 transformMatrix; public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) @@ -30,6 +32,17 @@ public ResizeProcessor(Configuration configuration, ResizeProcessor definition, this.destinationRectangle = definition.DestinationRectangle; this.options = definition.Options; this.resampler = definition.Options.Sampler; + + // Calculate the transform matrix from the resize operation to allow us + // to update any metadata that represents pixel coordinates in the source image. + Vector2 scale = new( + this.destinationRectangle.Width / (float)this.SourceRectangle.Width, + this.destinationRectangle.Height / (float)this.SourceRectangle.Height); + + this.transformMatrix = new ProjectiveTransformBuilder() + .AppendScale(scale) + .AppendTranslation((PointF)this.destinationRectangle.Location) + .BuildMatrix(sourceRectangle); } /// @@ -50,6 +63,9 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame + protected override Matrix4x4 GetTransformMatrix() => this.transformMatrix; + public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler { @@ -75,13 +91,13 @@ public void ApplyTransform(in TResampler sampler) ImageFrame destinationFrame = destination.Frames[i]; // The cloned will be blank here copy all the pixel data over - sourceFrame.GetPixelMemoryGroup().CopyTo(destinationFrame.GetPixelMemoryGroup()); + sourceFrame.PixelBuffer.CopyTo(destinationFrame.PixelBuffer); } return; } - var interest = Rectangle.Intersect(destinationRectangle, destination.Bounds); + Rectangle interest = Rectangle.Intersect(destinationRectangle, destination.Bounds); if (sampler is NearestNeighborResampler) { @@ -110,13 +126,13 @@ public void ApplyTransform(in TResampler sampler) // Since all image frame dimensions have to be the same we can calculate // the kernel maps and reuse for all frames. MemoryAllocator allocator = configuration.MemoryAllocator; - using var horizontalKernelMap = ResizeKernelMap.Calculate( + using ResizeKernelMap horizontalKernelMap = ResizeKernelMap.Calculate( in sampler, destinationRectangle.Width, sourceRectangle.Width, allocator); - using var verticalKernelMap = ResizeKernelMap.Calculate( + using ResizeKernelMap verticalKernelMap = ResizeKernelMap.Calculate( in sampler, destinationRectangle.Height, sourceRectangle.Height, @@ -158,7 +174,7 @@ private static void ApplyNNResizeFrameTransform( float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width; float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height; - var operation = new NNRowOperation( + NNRowOperation operation = new( sourceRectangle, destinationRectangle, interest, @@ -173,18 +189,6 @@ private static void ApplyNNResizeFrameTransform( in operation); } - private static PixelConversionModifiers GetModifiers(bool compand, bool premultiplyAlpha) - { - if (premultiplyAlpha) - { - return PixelConversionModifiers.Premultiply.ApplyCompanding(compand); - } - else - { - return PixelConversionModifiers.None.ApplyCompanding(compand); - } - } - private static void ApplyResizeFrameTransform( Configuration configuration, ImageFrame source, @@ -197,18 +201,15 @@ private static void ApplyResizeFrameTransform( bool compand, bool premultiplyAlpha) { - PixelAlphaRepresentation? alphaRepresentation = PixelOperations.Instance.GetPixelTypeInfo()?.AlphaRepresentation; - - // Premultiply only if alpha representation is unknown or Unassociated: - bool needsPremultiplication = alphaRepresentation == null || alphaRepresentation.Value == PixelAlphaRepresentation.Unassociated; - premultiplyAlpha &= needsPremultiplication; - PixelConversionModifiers conversionModifiers = GetModifiers(compand, premultiplyAlpha); + // Resampling uses normalized vectors and interpolates them in the alpha representation selected by the processor options. + PixelConversionModifiers alphaModifier = premultiplyAlpha ? PixelConversionModifiers.Premultiply : PixelConversionModifiers.UnPremultiply; + PixelConversionModifiers conversionModifiers = (PixelConversionModifiers.Scale | alphaModifier).ApplyCompanding(compand); Buffer2DRegion sourceRegion = source.PixelBuffer.GetRegion(sourceRectangle); // To reintroduce parallel processing, we would launch multiple workers // for different row intervals of the image. - using var worker = new ResizeWorker( + using ResizeWorker worker = new( configuration, sourceRegion, conversionModifiers, @@ -218,7 +219,7 @@ private static void ApplyResizeFrameTransform( destinationRectangle.Location); worker.Initialize(); - var workingInterval = new RowInterval(interest.Top, interest.Bottom); + RowInterval workingInterval = new(interest.Top, interest.Bottom); worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index cce27a401c..599882fdb8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -59,7 +59,7 @@ public ResizeWorker( { this.configuration = configuration; this.source = source; - this.sourceRectangle = source.Rectangle; + this.sourceRectangle = source.Bounds; this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; @@ -110,38 +110,65 @@ public void FillDestinationPixels(RowInterval rowInterval, Buffer2D dest { Span tempColSpan = this.tempColumnBuffer.GetSpan(); - // When creating transposedFirstPassBuffer, we made sure it's contiguous: + // When creating transposedFirstPassBuffer, we made sure it's contiguous. Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.DangerousGetSingleSpan(); int left = this.targetWorkingRect.Left; - int right = this.targetWorkingRect.Right; int width = this.targetWorkingRect.Width; + nuint widthCount = (uint)width; + + // Normalize destination-space Y to kernel indices using uint arithmetic. + // This relies on the contract that processing addresses are normalized (cropping/padding handled by targetOrigin). + int targetOriginY = this.targetOrigin.Y; + + // Hoist invariant calculations outside the loop. + int currentWindowMax = this.currentWindow.Max; + int currentWindowMin = this.currentWindow.Min; + nuint workerHeight = (uint)this.workerHeight; + nuint workerHeight2 = workerHeight * 2; + + // Ref-walk the kernel table to avoid bounds checks in the tight loop. + ReadOnlySpan vKernels = this.verticalKernelMap.GetKernelSpan(); + ref ResizeKernel vKernelBase = ref MemoryMarshal.GetReference(vKernels); + + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); + for (int y = rowInterval.Min; y < rowInterval.Max; y++) { - // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel((uint)(y - this.targetOrigin.Y)); + // Normalize destination-space Y to an unsigned kernel index. + uint vIdx = (uint)(y - targetOriginY); + ref ResizeKernel kernel = ref Unsafe.Add(ref vKernelBase, (nint)vIdx); - while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) + // Slide the working window when the kernel would read beyond the current cached region. + int kernelEnd = kernel.StartIndex + kernel.Length; + while (kernelEnd > currentWindowMax) { this.Slide(); + currentWindowMax = this.currentWindow.Max; + currentWindowMin = this.currentWindow.Min; } - ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); + int top = kernel.StartIndex - currentWindowMin; + ref Vector4 colRef0 = ref transposedFirstPassBufferSpan[top]; - int top = kernel.StartIndex - this.currentWindow.Min; + // Unroll by 2 and advance column refs via arithmetic to reduce inner-loop overhead. + nuint i = 0; + for (; i + 1 < widthCount; i += 2) + { + ref Vector4 colRef1 = ref Unsafe.Add(ref colRef0, workerHeight); - ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top]; + Unsafe.Add(ref tempRowBase, i) = kernel.ConvolveCore(ref colRef0); + Unsafe.Add(ref tempRowBase, i + 1) = kernel.ConvolveCore(ref colRef1); - for (nuint x = 0; x < (uint)(right - left); x++) - { - ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * (uint)this.workerHeight); + colRef0 = ref Unsafe.Add(ref colRef0, workerHeight2); + } - // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref firstPassColumnBase); + if (i < widthCount) + { + Unsafe.Add(ref tempRowBase, i) = kernel.ConvolveCore(ref colRef0); } Span targetRowSpan = destination.DangerousGetRowSpan(y).Slice(left, width); - PixelOperations.Instance.FromVector4Destructive(this.configuration, tempColSpan, targetRowSpan, this.conversionModifiers); } } @@ -171,28 +198,48 @@ private void CalculateFirstPassValues(RowInterval calculationInterval) nuint left = (uint)this.targetWorkingRect.Left; nuint right = (uint)this.targetWorkingRect.Right; + nuint widthCount = right - left; + + // Normalize destination-space X to kernel indices using uint arithmetic. + // This relies on the contract that processing addresses are normalized (cropping/padding handled by targetOrigin). nuint targetOriginX = (uint)this.targetOrigin.X; + + nuint workerHeight = (uint)this.workerHeight; + int currentWindowMin = this.currentWindow.Min; + + // Ref-walk the kernel table to avoid bounds checks in the tight loop. + ReadOnlySpan hKernels = this.horizontalKernelMap.GetKernelSpan(); + ref ResizeKernel hKernelBase = ref MemoryMarshal.GetReference(hKernels); + for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.DangerousGetRowSpan(y); + PixelOperations.Instance.ToVector4(this.configuration, sourceRow, tempRowSpan, this.conversionModifiers); + + ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - currentWindowMin]; - PixelOperations.Instance.ToVector4( - this.configuration, - sourceRow, - tempRowSpan, - this.conversionModifiers); + // Unroll by 2 to reduce loop and kernel lookup overhead. + nuint x = left; + nuint z = 0; - // optimization for: - // Span firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min); - ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min]; + for (; z + 1 < widthCount; x += 2, z += 2) + { + nuint hIdx0 = (uint)(x - targetOriginX); + nuint hIdx1 = (uint)((x + 1) - targetOriginX); + + ref ResizeKernel kernel0 = ref Unsafe.Add(ref hKernelBase, (nint)hIdx0); + ref ResizeKernel kernel1 = ref Unsafe.Add(ref hKernelBase, (nint)hIdx1); + + Unsafe.Add(ref firstPassBaseRef, z * workerHeight) = kernel0.Convolve(tempRowSpan); + Unsafe.Add(ref firstPassBaseRef, (z + 1) * workerHeight) = kernel1.Convolve(tempRowSpan); + } - for (nuint x = left, z = 0; x < right; x++, z++) + if (z < widthCount) { - ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - targetOriginX); + nuint hIdx = (uint)(x - targetOriginX); + ref ResizeKernel kernel = ref Unsafe.Add(ref hKernelBase, (nint)hIdx); - // optimization for: - // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); - Unsafe.Add(ref firstPassBaseRef, z * (uint)this.workerHeight) = kernel.Convolve(tempRowSpan); + Unsafe.Add(ref firstPassBaseRef, z * workerHeight) = kernel.Convolve(tempRowSpan); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs index 1ea2156c8f..9acf187303 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.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; @@ -12,17 +13,28 @@ internal class SwizzleProcessor : TransformProcessor { private readonly TSwizzler swizzler; private readonly Size destinationSize; + private readonly Matrix4x4 transformMatrix; public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.swizzler = swizzler; this.destinationSize = swizzler.DestinationSize; + + // Calculate the transform matrix from the swizzle operation to allow us + // to update any metadata that represents pixel coordinates in the source image. + this.transformMatrix = new ProjectiveTransformBuilder() + .AppendMatrix(TransformUtilities.GetSwizzlerMatrix(swizzler, sourceRectangle)) + .BuildMatrix(sourceRectangle); } - protected override Size GetDestinationSize() - => this.destinationSize; + /// + protected override Size GetDestinationSize() => this.destinationSize; + + /// + protected override Matrix4x4 GetTransformMatrix() => this.transformMatrix; + /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { Point p = default; diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs index 0b8274aff7..d00e01711e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs @@ -16,10 +16,7 @@ public sealed class SwizzleProcessor : IImageProcessor /// Initializes a new instance of the class. /// /// The swizzler operation. - public SwizzleProcessor(TSwizzler swizzler) - { - this.Swizzler = swizzler; - } + public SwizzleProcessor(TSwizzler swizzler) => this.Swizzler = swizzler; /// /// Gets the swizzler operation. diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs deleted file mode 100644 index 0c2c29391b..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms; - -/// -/// The base class for all transform processors. Any processor that changes the dimensions of the image should inherit from this. -/// -/// The pixel format. -internal abstract class TransformProcessor : CloningImageProcessor - where TPixel : unmanaged, IPixel -{ - /// - /// Initializes a new instance of the class. - /// - /// The configuration which allows altering default behaviour or extending the library. - /// The source for the current processor instance. - /// The source area to process for the current processor instance. - protected TransformProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) - : base(configuration, source, sourceRectangle) - { - } - - /// - protected override void AfterImageApply(Image destination) - { - TransformProcessorHelpers.UpdateDimensionalMetadata(destination); - base.AfterImageApply(destination); - } -} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs deleted file mode 100644 index 0bb4920f0f..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms; - -/// -/// Contains helper methods for working with transforms. -/// -internal static class TransformProcessorHelpers -{ - /// - /// Updates the dimensional metadata of a transformed image - /// - /// The pixel format. - /// The image to update - public static void UpdateDimensionalMetadata(Image image) - where TPixel : unmanaged, IPixel - { - ExifProfile? profile = image.Metadata.ExifProfile; - if (profile is null) - { - return; - } - - // Only set the value if it already exists. - if (profile.TryGetValue(ExifTag.PixelXDimension, out _)) - { - profile.SetValue(ExifTag.PixelXDimension, image.Width); - } - - if (profile.TryGetValue(ExifTag.PixelYDimension, out _)) - { - profile.SetValue(ExifTag.PixelYDimension, image.Height); - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor{TPixel}.cs new file mode 100644 index 0000000000..0d40cd32fd --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor{TPixel}.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms; + +/// +/// The base class for all transform processors. Any processor that changes the dimensions of the image should inherit from this. +/// +/// The pixel format. +internal abstract class TransformProcessor : CloningImageProcessor + where TPixel : unmanaged, IPixel +{ + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected TransformProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) + { + } + + /// + /// Gets the transform matrix that will be applied to the image. + /// + /// + /// The that represents the transformation to be applied to the image. + /// + protected abstract Matrix4x4 GetTransformMatrix(); + + /// + protected override void AfterFrameApply(ImageFrame source, ImageFrame destination) + => destination.Metadata.AfterFrameApply(source, destination, this.GetTransformMatrix()); + + /// + protected override void AfterImageApply(Image destination) + => destination.Metadata.AfterImageApply(destination, this.GetTransformMatrix()); +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs new file mode 100644 index 0000000000..b1d7b61eee --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs @@ -0,0 +1,680 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms; + +/// +/// Contains utility methods for working with transforms. +/// +internal static class TransformUtilities +{ + /// + /// Returns a value that indicates whether the specified matrix is degenerate + /// containing one or more values equivalent to or a + /// zero determinant and therefore cannot be used for linear transforms. + /// + /// The transform matrix. + public static bool IsDegenerate(Matrix3x2 matrix) + => IsNaN(matrix) || matrix.GetDeterminant() == 0F; + + /// + /// Returns a value that indicates whether the specified matrix is degenerate + /// containing one or more values equivalent to or a + /// zero determinant and therefore cannot be used for linear transforms. + /// + /// The transform matrix. + public static bool IsDegenerate(Matrix4x4 matrix) + => IsNaN(matrix) || matrix.GetDeterminant() == 0F; + + /// + /// Returns a value that indicates whether the specified matrix contains any values + /// that are not a number . + /// + /// The transform matrix. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(Matrix3x2 matrix) + => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) + || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) + || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32); + + /// + /// Returns a value that indicates whether the specified matrix contains any values + /// that are not a number . + /// + /// The transform matrix. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(Matrix4x4 matrix) + => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14) + || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24) + || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34) + || float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44); + + /// + /// Applies the projective transform against the given coordinates flattened into the 2D space. + /// + /// The "x" vector coordinate. + /// The "y" vector coordinate. + /// The transform matrix. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix) + { + // Transforms the 2D point (x, y) as the homogeneous coordinate (x, y, 0, 1) and + // performs the perspective divide (X/W, Y/W) to project back into Cartesian 2D space. + // + // For affine matrices (M14=0, M24=0, M34=0, M44=1) W is always 1 and the divide + // is a no-op, producing the same result as Vector2.Transform(v, Matrix4x4).AsVector2() + // (the approach used by .NET 10+). + // + // For projective matrices (taper, quad distortion) W varies per point and the divide + // is essential for correct perspective mapping. W <= 0 means the point has crossed the + // vanishing line of the projection; clamping to epsilon avoids division by zero or + // negative values that would flip/mirror the output. + const float epsilon = 0.0000001F; + Vector4 v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix); + return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, epsilon); + } + + /// + /// Creates a centered rotation transform matrix using the given rotation in degrees and the original source size. + /// + /// The amount of rotation, in degrees. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size) + => CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size); + + /// + /// Creates a centered rotation transform matrix using the given rotation in radians and the original source size. + /// + /// The amount of rotation, in radians. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size) + => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size); + + /// + /// Creates a centered skew transform matrix from the give angles in degrees and the original source size. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size) + => CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size); + + /// + /// Creates a centered skew transform matrix from the give angles in radians and the original source size. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size) + => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size); + + /// + /// Gets the centered transform matrix based upon the source rectangle. + /// + /// The transformation matrix. + /// The source image size. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size) + { + // 1) Unbounded size. + SizeF ts = GetRawTransformedSize(matrix, size); + + // 2) Invert the content transform for screen->world. + Matrix3x2.Invert(matrix, out Matrix3x2 inv); + + // 3) Translate target (canvas) so its center is at the origin, + // translate source so its center is at the origin, then undo the content transform. + Matrix3x2 toTarget = Matrix3x2.CreateTranslation(new Vector2(-ts.Width, -ts.Height) * 0.5f); + Matrix3x2 toSource = Matrix3x2.CreateTranslation(new Vector2(size.Width, size.Height) * 0.5f); + + // 4) World->screen. + Matrix3x2.Invert(toTarget * inv * toSource, out Matrix3x2 centered); + + return centered; + } + + /// + /// Creates a matrix that performs a tapering projective transform. + /// + /// + /// The rectangular size of the image being transformed. + /// An enumeration that indicates the side of the rectangle that tapers. + /// An enumeration that indicates on which corners to taper the rectangle. + /// The amount to taper. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction) + { + Matrix4x4 matrix = Matrix4x4.Identity; + + /* + * SkMatrix is laid out in the following manner: + * + * [ ScaleX SkewY Persp0 ] + * [ SkewX ScaleY Persp1 ] + * [ TransX TransY Persp2 ] + * + * When converting from Matrix4x4 to SkMatrix, the third row and + * column is dropped. When converting from SkMatrix to Matrix4x4 + * the third row and column remain as identity: + * + * [ a b c ] [ a b 0 c ] + * [ d e f ] -> [ d e 0 f ] + * [ g h i ] [ 0 0 1 0 ] + * [ g h 0 i ] + */ + switch (side) + { + case TaperSide.Left: + matrix.M11 = fraction; + matrix.M22 = fraction; + matrix.M14 = (fraction - 1) / size.Width; + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M12 = size.Height * matrix.M14; + matrix.M42 = size.Height * (1 - fraction); + break; + + case TaperCorner.Both: + matrix.M12 = size.Height * .5F * matrix.M14; + matrix.M42 = size.Height * (1 - fraction) / 2; + break; + } + + break; + + case TaperSide.Top: + matrix.M11 = fraction; + matrix.M22 = fraction; + matrix.M24 = (fraction - 1) / size.Height; + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M21 = size.Width * matrix.M24; + matrix.M41 = size.Width * (1 - fraction); + break; + + case TaperCorner.Both: + matrix.M21 = size.Width * .5F * matrix.M24; + matrix.M41 = size.Width * (1 - fraction) * .5F; + break; + } + + break; + + case TaperSide.Right: + matrix.M11 = 1 / fraction; + matrix.M14 = (1 - fraction) / (size.Width * fraction); + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M12 = size.Height * matrix.M14; + break; + + case TaperCorner.Both: + matrix.M12 = size.Height * .5F * matrix.M14; + break; + } + + break; + + case TaperSide.Bottom: + matrix.M22 = 1 / fraction; + matrix.M24 = (1 - fraction) / (size.Height * fraction); + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M21 = size.Width * matrix.M24; + break; + + case TaperCorner.Both: + matrix.M21 = size.Width * .5F * matrix.M24; + break; + } + + break; + } + + return matrix; + } + + /// + /// Computes the projection matrix for a quad distortion transformation. + /// + /// The source rectangle. + /// The top-left point of the distorted quad. + /// The top-right point of the distorted quad. + /// The bottom-right point of the distorted quad. + /// The bottom-left point of the distorted quad. + /// The computed projection matrix for the quad distortion. + /// + /// This method is based on the algorithm described in the following article: + /// + /// + public static Matrix4x4 CreateQuadDistortionMatrix( + Rectangle rectangle, + PointF topLeft, + PointF topRight, + PointF bottomRight, + PointF bottomLeft) + { + PointF p1 = new(rectangle.X, rectangle.Y); + PointF p2 = new(rectangle.X + rectangle.Width, rectangle.Y); + PointF p3 = new(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height); + PointF p4 = new(rectangle.X, rectangle.Y + rectangle.Height); + + PointF q1 = topLeft; + PointF q2 = topRight; + PointF q3 = bottomRight; + PointF q4 = bottomLeft; + + double[][] matrixData = + [ + [p1.X, p1.Y, 1, 0, 0, 0, -p1.X * q1.X, -p1.Y * q1.X], + [0, 0, 0, p1.X, p1.Y, 1, -p1.X * q1.Y, -p1.Y * q1.Y], + [p2.X, p2.Y, 1, 0, 0, 0, -p2.X * q2.X, -p2.Y * q2.X], + [0, 0, 0, p2.X, p2.Y, 1, -p2.X * q2.Y, -p2.Y * q2.Y], + [p3.X, p3.Y, 1, 0, 0, 0, -p3.X * q3.X, -p3.Y * q3.X], + [0, 0, 0, p3.X, p3.Y, 1, -p3.X * q3.Y, -p3.Y * q3.Y], + [p4.X, p4.Y, 1, 0, 0, 0, -p4.X * q4.X, -p4.Y * q4.X], + [0, 0, 0, p4.X, p4.Y, 1, -p4.X * q4.Y, -p4.Y * q4.Y], + ]; + + double[] b = + [ + q1.X, + q1.Y, + q2.X, + q2.Y, + q3.X, + q3.Y, + q4.X, + q4.Y, + ]; + + GaussianEliminationSolver.Solve(matrixData, b); + +#pragma warning disable SA1117 + Matrix4x4 projectionMatrix = new( + (float)b[0], (float)b[3], 0, (float)b[6], + (float)b[1], (float)b[4], 0, (float)b[7], + 0, 0, 1, 0, + (float)b[2], (float)b[5], 0, 1); +#pragma warning restore SA1117 + + return projectionMatrix; + } + + /// + /// Calculates the size of a destination canvas large enough to contain + /// the fully transformed source content, including any translation offsets. + /// + /// The transformation matrix. + /// The original source size. + /// + /// A representing the dimensions of the destination + /// canvas required to fully contain the transformed source, including + /// any positive or negative translation offsets. + /// + /// + /// + /// This method ensures that the transformed content remains fully visible + /// on the destination canvas by expanding its size to include translations + /// in all directions. + /// + /// + /// It behaves identically to calling + /// with + /// preserveCanvas set to . + /// + /// + /// The resulting canvas size represents the total area required to display + /// the transformed image without clipping, not merely the geometric bounds + /// of the transformed source. + /// + /// + public static Size GetTransformedCanvasSize(Matrix3x2 matrix, Size size) + => Size.Ceiling(GetTransformedSize(matrix, size, true)); + + /// + /// Calculates the size of a destination canvas large enough to contain + /// the fully transformed source content, including any translation offsets. + /// + /// The transformation matrix. + /// The original source size. + /// + /// A representing the dimensions of the destination + /// canvas required to fully contain the transformed source, including + /// any positive or negative translation offsets. + /// + /// + /// + /// This method ensures that the transformed content remains fully visible + /// on the destination canvas by expanding its size to include translations + /// in all directions. + /// + /// + /// It behaves identically to calling + /// with + /// preserveCanvas set to . + /// + /// + /// The resulting canvas size represents the total area required to display + /// the transformed image without clipping, not merely the geometric bounds + /// of the transformed source. + /// + /// + public static Size GetTransformedCanvasSize(Matrix4x4 matrix, Size size) + => Size.Ceiling(GetTransformedSize(matrix, size, true)); + + /// + /// Returns the size relative to the source for the given transformation matrix. + /// + /// The transformation matrix. + /// The original source size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF GetRawTransformedSize(Matrix4x4 matrix, Size size) + => GetTransformedSize(matrix, size, false); + + /// + /// Returns the size of the transformed source. When is true, + /// the size is expanded to include translation so the full moved content remains visible. + /// + /// The transformation matrix. + /// The original source size. + /// + /// If , expand the size to account for translation (left/up as well as right/down). + /// If , return only the transformed span without translation expansion. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static SizeF GetTransformedSize(Matrix4x4 matrix, Size size, bool preserveCanvas) + { + Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); + + if (matrix.IsIdentity || matrix.Equals(default)) + { + return size; + } + + if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size), matrix, out RectangleF bounds)) + { + return preserveCanvas ? GetPreserveCanvasSize(bounds) : bounds.Size; + } + + return size; + } + + /// + /// Attempts to derive a 4x4 projective transform matrix that approximates the behavior of an . + /// + /// + /// The swizzler to use for the transformation. + /// + /// + /// The source rectangle that defines the area to be transformed. + /// + /// + /// The type of the swizzler, which must implement . + /// + public static Matrix4x4 GetSwizzlerMatrix(T swizzler, Rectangle sourceRectangle) + where T : struct, ISwizzler + => CreateQuadDistortionMatrix( + sourceRectangle, + swizzler.Transform(new Point(sourceRectangle.Left, sourceRectangle.Top)), + swizzler.Transform(new Point(sourceRectangle.Right, sourceRectangle.Top)), + swizzler.Transform(new Point(sourceRectangle.Right, sourceRectangle.Bottom)), + swizzler.Transform(new Point(sourceRectangle.Left, sourceRectangle.Bottom))); + + /// + /// Returns the size relative to the source for the given transformation matrix. + /// + /// The transformation matrix. + /// The original source size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF GetRawTransformedSize(Matrix3x2 matrix, Size size) + => GetTransformedSize(matrix, size, false); + + /// + /// Returns the size of the transformed source. When is true, + /// the size is expanded to include translation so the full moved content remains visible. + /// + /// The transformation matrix. + /// The original source size. + /// + /// If , expand the size to account for translation (left/up as well as right/down). + /// If , return only the transformed span without translation expansion. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static SizeF GetTransformedSize(Matrix3x2 matrix, Size size, bool preserveCanvas) + { + Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); + + if (matrix.IsIdentity || matrix.Equals(default)) + { + return size; + } + + if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size), matrix, out RectangleF bounds)) + { + return preserveCanvas ? GetPreserveCanvasSize(bounds) : bounds.Size; + } + + return size; + } + + /// + /// Returns the rectangle relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// The resulting bounding rectangle. + /// + /// if the transformation was successful; otherwise, . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix3x2 matrix, out RectangleF bounds) + { + if (matrix.IsIdentity || rectangle.Equals(default)) + { + bounds = default; + return false; + } + + Vector2 tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); + Vector2 tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); + Vector2 bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); + Vector2 br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); + + bounds = GetBoundingRectangle(tl, tr, bl, br); + return true; + } + + /// + /// Returns the rectangle relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// The resulting bounding rectangle. + /// + /// if the transformation was successful; otherwise, . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix4x4 matrix, out RectangleF bounds) + { + if (matrix.IsIdentity || rectangle.Equals(default)) + { + bounds = default; + return false; + } + + Vector2 tl = ProjectiveTransform2D(rectangle.Left, rectangle.Top, matrix); + Vector2 tr = ProjectiveTransform2D(rectangle.Right, rectangle.Top, matrix); + Vector2 bl = ProjectiveTransform2D(rectangle.Left, rectangle.Bottom, matrix); + Vector2 br = ProjectiveTransform2D(rectangle.Right, rectangle.Bottom, matrix); + + bounds = GetBoundingRectangle(tl, tr, bl, br); + return true; + } + + /// + /// Calculates the size of a destination canvas large enough to contain the full + /// transformed content of a source rectangle while preserving any translation offsets. + /// + /// + /// The representing the transformed bounds of the source content + /// in destination (output) space. + /// + /// + /// A that describes the canvas dimensions required to fully + /// contain the transformed content while accounting for any positive or negative translation. + /// + /// + /// + /// This method expands the output canvas to ensure that translated content remains visible. + /// + /// + /// If the transformation produces a positive translation, the method extends the canvas + /// on the positive side (right or bottom). + /// If the transformation produces a negative translation (the content moves left or up), + /// the method extends the canvas on the negative side to include that offset. + /// + /// + /// The result is equivalent to taking the union of: + /// + /// + /// The original, untransformed rectangle at the origin [0..Width] × [0..Height]. + /// + /// + /// The translated rectangle defined by . + /// + /// + /// This ensures the entire translated image fits within the resulting canvas, + /// without trimming any portion caused by translation. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static SizeF GetPreserveCanvasSize(RectangleF rectangle) + { + // Compute the required height. + // If the top is negative, expand upward by that amount (rectangle.Bottom already includes height). + // Otherwise, take the larger of the transformed height or the bottom offset. + float height = rectangle.Top < 0 + ? rectangle.Bottom + : MathF.Max(rectangle.Height, rectangle.Bottom); + + // Compute the required width. + // If the left is negative, expand leftward by that amount (rectangle.Right already includes width). + // Otherwise, take the larger of the transformed width or the right offset. + float width = rectangle.Left < 0 + ? rectangle.Right + : MathF.Max(rectangle.Width, rectangle.Right); + + // Guard: if translation exceeds or cancels dimensions, + // ensure non-zero positive size using the base rectangle dimensions. + if (height <= 0) + { + height = rectangle.Height; + } + + if (width <= 0) + { + width = rectangle.Width; + } + + // Return the final size that preserves the full visible region of the transformed content. + return new SizeF(width, height); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static RectangleF GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) + { + float left = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); + float top = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); + float right = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); + float bottom = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); + + return RectangleF.FromLTRB(left, top, right, bottom); + } + + /// + /// Normalizes an affine 2D matrix so that it operates in pixel space. + /// Applies the row-vector conjugation T(+0.5,+0.5) * M * T(-0.5,-0.5) + /// to align the transform with pixel centers. + /// + /// The affine matrix. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 NormalizeToPixel(Matrix3x2 matrix) + { + const float dx = 0.5f, dy = 0.5f; + + matrix.M31 += (-dx) + ((dx * matrix.M11) + (dy * matrix.M21)); + matrix.M32 += (-dy) + ((dx * matrix.M12) + (dy * matrix.M22)); + return matrix; + } + + /// + /// Normalizes a projective 4×4 matrix so that it operates in pixel space. + /// Applies the row-vector conjugation T(+0.5,+0.5,0) * M * T(-0.5,-0.5,0) + /// to align the transform with pixel centers. + /// + /// The projective matrix. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix4x4 NormalizeToPixel(Matrix4x4 matrix) + { + const float dx = 0.5f, dy = 0.5f; + + // Fast path: affine (no perspective) + if (matrix.M14 == 0f && matrix.M24 == 0f && matrix.M34 == 0f && matrix.M44 == 1f) + { + // t' = t + (-d + d·L) + matrix.M41 += (-dx) + ((dx * matrix.M11) + (dy * matrix.M21)); + matrix.M42 += (-dy) + ((dx * matrix.M12) + (dy * matrix.M22)); + return matrix; + } + + Matrix4x4 tPos = Matrix4x4.Identity; + tPos.M41 = dx; + tPos.M42 = dy; + Matrix4x4 tNeg = Matrix4x4.Identity; + tNeg.M41 = -dx; + tNeg.M42 = -dy; + return tPos * matrix * tNeg; + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs deleted file mode 100644 index 62ea5e830d..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms; - -/// -/// Contains utility methods for working with transforms. -/// -internal static class TransformUtils -{ - /// - /// Returns a value that indicates whether the specified matrix is degenerate - /// containing one or more values equivalent to or a - /// zero determinant and therefore cannot be used for linear transforms. - /// - /// The transform matrix. - public static bool IsDegenerate(Matrix3x2 matrix) - => IsNaN(matrix) || IsZero(matrix.GetDeterminant()); - - /// - /// Returns a value that indicates whether the specified matrix is degenerate - /// containing one or more values equivalent to or a - /// zero determinant and therefore cannot be used for linear transforms. - /// - /// The transform matrix. - public static bool IsDegenerate(Matrix4x4 matrix) - => IsNaN(matrix) || IsZero(matrix.GetDeterminant()); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsZero(float a) - => a > -Constants.EpsilonSquared && a < Constants.EpsilonSquared; - - /// - /// Returns a value that indicates whether the specified matrix contains any values - /// that are not a number . - /// - /// The transform matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNaN(Matrix3x2 matrix) - => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) - || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) - || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32); - - /// - /// Returns a value that indicates whether the specified matrix contains any values - /// that are not a number . - /// - /// The transform matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNaN(Matrix4x4 matrix) - => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14) - || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24) - || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34) - || float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44); - - /// - /// Applies the projective transform against the given coordinates flattened into the 2D space. - /// - /// The "x" vector coordinate. - /// The "y" vector coordinate. - /// The transform matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix) - { - // The w component (v4.W) resulting from the transformation can be less than 0 in certain cases, - // such as when the point is transformed behind the camera in a perspective projection. - // However, in many 2D contexts, negative w values are not meaningful and could cause issues - // like flipped or distorted projections. To avoid this, we take the max of w and epsilon to ensure - // we don't divide by a very small or negative number, effectively treating any negative w as epsilon. - const float epsilon = 0.0000001F; - Vector4 v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix); - return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, epsilon); - } - - /// - /// Creates a centered rotation transform matrix using the given rotation in degrees and the source size. - /// - /// The amount of rotation, in degrees. - /// The source image size. - /// The to use when creating the centered matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size, TransformSpace transformSpace) - => CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size, transformSpace); - - /// - /// Creates a centered rotation transform matrix using the given rotation in radians and the source size. - /// - /// The amount of rotation, in radians. - /// The source image size. - /// The to use when creating the centered matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size, TransformSpace transformSpace) - => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size, transformSpace); - - /// - /// Creates a centered skew transform matrix from the give angles in degrees and the source size. - /// - /// The X angle, in degrees. - /// The Y angle, in degrees. - /// The source image size. - /// The to use when creating the centered matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size, TransformSpace transformSpace) - => CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size, transformSpace); - - /// - /// Creates a centered skew transform matrix from the give angles in radians and the source size. - /// - /// The X angle, in radians. - /// The Y angle, in radians. - /// The source image size. - /// The to use when creating the centered matrix. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size, TransformSpace transformSpace) - => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size, transformSpace); - - /// - /// Gets the centered transform matrix based upon the source rectangle. - /// - /// The transformation matrix. - /// The source image size. - /// - /// The to use when creating the centered matrix. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size, TransformSpace transformSpace) - { - Size transformSize = GetUnboundedTransformedSize(matrix, size, transformSpace); - - // We invert the matrix to handle the transformation from screen to world space. - // This ensures scaling matrices are correct. - Matrix3x2.Invert(matrix, out Matrix3x2 inverted); - - // The source size is provided using the coordinate space of the source image. - // however the transform should always be applied in the pixel space. - // To account for this we offset by the size - 1 to translate to the pixel space. - float offset = transformSpace == TransformSpace.Pixel ? 1F : 0F; - - Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-(transformSize.Width - offset), -(transformSize.Height - offset)) * .5F); - Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(size.Width - offset, size.Height - offset) * .5F); - - // Translate back to world space. - Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); - - return centered; - } - - /// - /// Creates a matrix that performs a tapering projective transform. - /// - /// - /// The rectangular size of the image being transformed. - /// An enumeration that indicates the side of the rectangle that tapers. - /// An enumeration that indicates on which corners to taper the rectangle. - /// The amount to taper. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction) - { - Matrix4x4 matrix = Matrix4x4.Identity; - - /* - * SkMatrix is laid out in the following manner: - * - * [ ScaleX SkewY Persp0 ] - * [ SkewX ScaleY Persp1 ] - * [ TransX TransY Persp2 ] - * - * When converting from Matrix4x4 to SkMatrix, the third row and - * column is dropped. When converting from SkMatrix to Matrix4x4 - * the third row and column remain as identity: - * - * [ a b c ] [ a b 0 c ] - * [ d e f ] -> [ d e 0 f ] - * [ g h i ] [ 0 0 1 0 ] - * [ g h 0 i ] - */ - switch (side) - { - case TaperSide.Left: - matrix.M11 = fraction; - matrix.M22 = fraction; - matrix.M14 = (fraction - 1) / size.Width; - - switch (corner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M12 = size.Height * matrix.M14; - matrix.M42 = size.Height * (1 - fraction); - break; - - case TaperCorner.Both: - matrix.M12 = size.Height * .5F * matrix.M14; - matrix.M42 = size.Height * (1 - fraction) / 2; - break; - } - - break; - - case TaperSide.Top: - matrix.M11 = fraction; - matrix.M22 = fraction; - matrix.M24 = (fraction - 1) / size.Height; - - switch (corner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M21 = size.Width * matrix.M24; - matrix.M41 = size.Width * (1 - fraction); - break; - - case TaperCorner.Both: - matrix.M21 = size.Width * .5F * matrix.M24; - matrix.M41 = size.Width * (1 - fraction) * .5F; - break; - } - - break; - - case TaperSide.Right: - matrix.M11 = 1 / fraction; - matrix.M14 = (1 - fraction) / (size.Width * fraction); - - switch (corner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M12 = size.Height * matrix.M14; - break; - - case TaperCorner.Both: - matrix.M12 = size.Height * .5F * matrix.M14; - break; - } - - break; - - case TaperSide.Bottom: - matrix.M22 = 1 / fraction; - matrix.M24 = (1 - fraction) / (size.Height * fraction); - - switch (corner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M21 = size.Width * matrix.M24; - break; - - case TaperCorner.Both: - matrix.M21 = size.Width * .5F * matrix.M24; - break; - } - - break; - } - - return matrix; - } - - /// - /// Returns the size relative to the source for the given transformation matrix. - /// - /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. - /// The . - public static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace) - => GetTransformedSize(matrix, size, transformSpace, true); - - /// - /// Returns the size relative to the source for the given transformation matrix. - /// - /// The transformation matrix. - /// The source size. - /// - /// The . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size GetTransformedSize(Matrix4x4 matrix, Size size) - { - Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); - - if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity)) - { - return size; - } - - // Check if the matrix involves only affine transformations by inspecting the relevant components. - // We want to use pixel space for calculations only if the transformation is purely 2D and does not include - // any perspective effects, non-standard scaling, or unusual translations that could distort the image. - // The conditions are as follows: - bool usePixelSpace = - - // 1. Ensure there's no perspective distortion: - // M34 corresponds to the perspective component. For a purely 2D affine transformation, this should be 0. - (matrix.M34 == 0) && - - // 2. Ensure standard affine transformation without any unusual depth or perspective scaling: - // M44 should be 1 for a standard affine transformation. If M44 is not 1, it indicates non-standard depth - // scaling or perspective, which suggests a more complex transformation. - (matrix.M44 == 1) && - - // 3. Ensure no unusual translation in the x-direction: - // M14 represents translation in the x-direction that might be part of a more complex transformation. - // For standard affine transformations, M14 should be 0. - (matrix.M14 == 0) && - - // 4. Ensure no unusual translation in the y-direction: - // M24 represents translation in the y-direction that might be part of a more complex transformation. - // For standard affine transformations, M24 should be 0. - (matrix.M24 == 0); - - // Define an offset size to translate between pixel space and coordinate space. - // When using pixel space, apply a scaling sensitive offset to translate to discrete pixel coordinates. - // When not using pixel space, use SizeF.Empty as the offset. - - // Compute scaling factors from the matrix - float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2) - float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2) - - // Apply the offset relative to the scale - SizeF offsetSize = usePixelSpace ? new SizeF(scaleX, scaleY) : SizeF.Empty; - - // Subtract the offset size to translate to the appropriate space (pixel or coordinate). - if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size - offsetSize), matrix, out Rectangle bounds)) - { - // Add the offset size back to translate the transformed bounds to the correct space. - return Size.Ceiling(ConstrainSize(bounds) + offsetSize); - } - - return size; - } - - /// - /// Returns the size relative to the source for the given transformation matrix. - /// - /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. - /// The . - private static Size GetUnboundedTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace) - => GetTransformedSize(matrix, size, transformSpace, false); - - /// - /// Returns the size relative to the source for the given transformation matrix. - /// - /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. - /// Whether to constrain the size to ensure that the dimensions are positive. - /// - /// The . - /// - private static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace, bool constrain) - { - Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); - - if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity)) - { - return size; - } - - // Define an offset size to translate between coordinate space and pixel space. - // Compute scaling factors from the matrix - SizeF offsetSize = SizeF.Empty; - if (transformSpace == TransformSpace.Pixel) - { - float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2) - float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2) - offsetSize = new(scaleX, scaleY); - } - - // Subtract the offset size to translate to the pixel space. - if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size - offsetSize), matrix, out Rectangle bounds)) - { - // Add the offset size back to translate the transformed bounds to the coordinate space. - return Size.Ceiling((constrain ? ConstrainSize(bounds) : bounds.Size) + offsetSize); - } - - return size; - } - - /// - /// Returns the rectangle relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// The resulting bounding rectangle. - /// - /// if the transformation was successful; otherwise, . - /// - private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix3x2 matrix, out Rectangle bounds) - { - if (rectangle.Equals(default) || Matrix3x2.Identity.Equals(matrix)) - { - bounds = default; - return false; - } - - Vector2 tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); - Vector2 tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); - Vector2 bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); - Vector2 br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); - - bounds = GetBoundingRectangle(tl, tr, bl, br); - return true; - } - - /// - /// Returns the rectangle relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// The resulting bounding rectangle. - /// - /// if the transformation was successful; otherwise, . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix4x4 matrix, out Rectangle bounds) - { - if (rectangle.Equals(default) || Matrix4x4.Identity.Equals(matrix)) - { - bounds = default; - return false; - } - - Vector2 tl = ProjectiveTransform2D(rectangle.Left, rectangle.Top, matrix); - Vector2 tr = ProjectiveTransform2D(rectangle.Right, rectangle.Top, matrix); - Vector2 bl = ProjectiveTransform2D(rectangle.Left, rectangle.Bottom, matrix); - Vector2 br = ProjectiveTransform2D(rectangle.Right, rectangle.Bottom, matrix); - - bounds = GetBoundingRectangle(tl, tr, bl, br); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Size ConstrainSize(Rectangle rectangle) - { - // We want to resize the canvas here taking into account any translations. - int height = rectangle.Top < 0 ? rectangle.Bottom : Math.Max(rectangle.Height, rectangle.Bottom); - int width = rectangle.Left < 0 ? rectangle.Right : Math.Max(rectangle.Width, rectangle.Right); - - // If location in either direction is translated to a negative value equal to or exceeding the - // dimensions in either direction we need to reassign the dimension. - if (height <= 0) - { - height = rectangle.Height; - } - - if (width <= 0) - { - width = rectangle.Width; - } - - return new Size(width, height); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) - { - // Find the minimum and maximum "corners" based on the given vectors - float left = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); - float top = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); - float right = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); - float bottom = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); - - // Clamp the values to the nearest whole pixel. - return Rectangle.FromLTRB( - (int)Math.Floor(left), - (int)Math.Floor(top), - (int)Math.Ceiling(right), - (int)Math.Ceiling(bottom)); - } -} diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index 9027ee7266..74da440401 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -11,30 +11,15 @@ namespace SixLabors.ImageSharp.Processing; /// public class ProjectiveTransformBuilder { - private readonly List> transformMatrixFactories = new(); + private readonly List> transformMatrixFactories = []; /// /// Initializes a new instance of the class. /// public ProjectiveTransformBuilder() - : this(TransformSpace.Pixel) { } - /// - /// Initializes a new instance of the class. - /// - /// - /// The to use when applying the projective transform. - /// - public ProjectiveTransformBuilder(TransformSpace transformSpace) - => this.TransformSpace = transformSpace; - - /// - /// Gets the to use when applying the projective transform. - /// - public TransformSpace TransformSpace { get; } - /// /// Prepends a matrix that performs a tapering projective transform. /// @@ -43,7 +28,7 @@ public ProjectiveTransformBuilder(TransformSpace transformSpace) /// The amount to taper. /// The . public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Prepend(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction)); /// /// Appends a matrix that performs a tapering projective transform. @@ -53,7 +38,7 @@ public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corne /// The amount to taper. /// The . public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Append(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction)); /// /// Prepends a centered rotation matrix using the given rotation in degrees. @@ -69,7 +54,7 @@ public ProjectiveTransformBuilder PrependRotationDegrees(float degrees) /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder PrependRotationRadians(float radians) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace))); + => this.Prepend(size => new Matrix4x4(TransformUtilities.CreateRotationTransformMatrixRadians(radians, size))); /// /// Prepends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -87,7 +72,8 @@ internal ProjectiveTransformBuilder PrependRotationDegrees(float degrees, Vector /// The rotation origin point. /// The . internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 origin) - => this.PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); + => this.PrependMatrix( + Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); /// /// Appends a centered rotation matrix using the given rotation in degrees. @@ -103,7 +89,7 @@ public ProjectiveTransformBuilder AppendRotationDegrees(float degrees) /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace))); + => this.Append(size => new Matrix4x4(TransformUtilities.CreateRotationTransformMatrixRadians(radians, size))); /// /// Appends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -187,7 +173,7 @@ internal ProjectiveTransformBuilder PrependSkewDegrees(float degreesX, float deg /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace))); + => this.Prepend(size => new Matrix4x4(TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -225,7 +211,7 @@ internal ProjectiveTransformBuilder AppendSkewDegrees(float degreesX, float degr /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace))); + => this.Append(size => new Matrix4x4(TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -279,6 +265,38 @@ public ProjectiveTransformBuilder AppendTranslation(PointF position) public ProjectiveTransformBuilder AppendTranslation(Vector2 position) => this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0))); + /// + /// Prepends a quad distortion matrix using the specified corner points. + /// + /// The top-left corner point of the distorted quad. + /// The top-right corner point of the distorted quad. + /// The bottom-right corner point of the distorted quad. + /// The bottom-left corner point of the distorted quad. + /// The . + public ProjectiveTransformBuilder PrependQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) + => this.Prepend(size => TransformUtilities.CreateQuadDistortionMatrix( + new Rectangle(Point.Empty, size), + topLeft, + topRight, + bottomRight, + bottomLeft)); + + /// + /// Appends a quad distortion matrix using the specified corner points. + /// + /// The top-left corner point of the distorted quad. + /// The top-right corner point of the distorted quad. + /// The bottom-right corner point of the distorted quad. + /// The bottom-left corner point of the distorted quad. + /// The . + public ProjectiveTransformBuilder AppendQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) + => this.Append(size => TransformUtilities.CreateQuadDistortionMatrix( + new Rectangle(Point.Empty, size), + topLeft, + topRight, + bottomRight, + bottomLeft)); + /// /// Prepends a raw matrix. /// @@ -359,25 +377,39 @@ public Matrix4x4 BuildMatrix(Rectangle sourceRectangle) /// for linear transforms. /// /// The . - public Size GetTransformedSize(Rectangle sourceRectangle) + public SizeF GetTransformedSize(Rectangle sourceRectangle) { - Size size = sourceRectangle.Size; - - // Translate the origin matrix to cater for source rectangle offsets. - Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); + Matrix4x4 matrix = this.BuildMatrix(sourceRectangle); + return GetTransformedSize(sourceRectangle, matrix); + } - foreach (Func factory in this.transformMatrixFactories) - { - matrix *= factory(size); - CheckDegenerate(matrix); - } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// The transformation matrix. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix4x4 matrix) + => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); - return TransformUtils.GetTransformedSize(matrix, size); + /// + /// Clears all accumulated transform matrices, resetting the builder to its initial state. + /// + /// The . + public ProjectiveTransformBuilder Clear() + { + this.transformMatrixFactories.Clear(); + return this; } private static void CheckDegenerate(Matrix4x4 matrix) { - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/TransformSpace.cs b/src/ImageSharp/Processing/TransformSpace.cs deleted file mode 100644 index bca676bd88..0000000000 --- a/src/ImageSharp/Processing/TransformSpace.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Processing; - -/// -/// Represents the different spaces used in transformation operations. -/// -public enum TransformSpace -{ - /// - /// Coordinate space is a continuous, mathematical grid where objects and positions - /// are defined with precise, often fractional values. This space allows for fine-grained - /// transformations like scaling, rotation, and translation with high precision. - /// In coordinate space, an image can span from (0,0) to (4,4) for a 4x4 image, including the boundaries. - /// - Coordinate, - - /// - /// Pixel space is a discrete grid where each position corresponds to a specific pixel on the screen. - /// In this space, positions are defined by whole numbers, with no fractional values. - /// A 4x4 image in pixel space covers exactly 4 pixels wide and 4 pixels tall, ranging from (0,0) to (3,3). - /// Pixel space is used when rendering images to ensure that everything aligns with the actual pixels on the screen. - /// - Pixel -} diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 3a29442872..da57541faa 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -18,20 +18,22 @@ - - - - - - - - - - - - + + + + + + + + + + + - + diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs index bd938c9da9..92d5bcdbfe 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs @@ -49,28 +49,20 @@ public void Naive() 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 -{ -} +public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes; public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes { diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index 7e6cec2018..dcd26c328d 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -14,13 +14,12 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortCore31))] 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,77 +30,63 @@ 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).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()); } +[Config(typeof(Config.Short))] 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); - } - + /// + /// Measures the raw SIMD kernel that converts normalized RGBA vector components to bytes. + /// [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()); + // Four components per pixel make every configured count divisible by the supported 128-, 256-, and 512-bit + // byte-vector widths. Calling the block kernel directly therefore excludes PixelOperations contract handling + // and the general conversion wrapper's remainder dispatch, establishing the lower bound for the SIMD conversion. SimdUtils.HwIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); } - private static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 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 }; + private static ReadOnlySpan PermuteMaskDeinterleave8x32 => [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 + ]; [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; @@ -111,7 +96,7 @@ public void UseAvx2_Grouped() 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++) { @@ -141,25 +126,46 @@ public void UseAvx2_Grouped() } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 ConvertToInt32(Vector256 vf, Vector256 scale) - { - vf = Avx.Multiply(scale, vf); - return Avx.ConvertToVector256Int32(vf); - } + /* + BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + .NET 8.0.28, X64 RyuJIT x86-64-v4 + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | + |---------------------------- |------ |------------:|-----------:|----------:|------:|--------:|----------:| + | PixelOperations_Base | 64 | 60.19 ns | 32.376 ns | 1.775 ns | 1.00 | 0.04 | - | + | PixelOperations_Specialized | 64 | 22.94 ns | 66.082 ns | 3.622 ns | 0.38 | 0.05 | - | + | UseHwIntrinsics | 64 | 10.64 ns | 9.535 ns | 0.523 ns | 0.18 | 0.01 | - | + | UseAvx2_Grouped | 64 | 11.69 ns | 0.997 ns | 0.055 ns | 0.19 | 0.01 | - | + | PixelOperations_Base | 256 | 232.65 ns | 37.948 ns | 2.080 ns | 1.00 | 0.01 | - | + | PixelOperations_Specialized | 256 | 33.75 ns | 3.906 ns | 0.214 ns | 0.15 | 0.00 | - | + | UseHwIntrinsics | 256 | 21.91 ns | 2.732 ns | 0.150 ns | 0.09 | 0.00 | - | + | UseAvx2_Grouped | 256 | 24.84 ns | 5.333 ns | 0.292 ns | 0.11 | 0.00 | - | + | PixelOperations_Base | 2048 | 1,500.98 ns | 951.510 ns | 52.155 ns | 1.00 | 0.04 | - | + | PixelOperations_Specialized | 2048 | 156.21 ns | 65.074 ns | 3.567 ns | 0.10 | 0.00 | - | + | UseHwIntrinsics | 2048 | 144.38 ns | 40.947 ns | 2.244 ns | 0.10 | 0.00 | - | + | UseAvx2_Grouped | 2048 | 189.80 ns | 54.098 ns | 2.965 ns | 0.13 | 0.00 | - | + */ +} - // *** 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 | - | - | - | - | +/// +/// Measures bulk conversion from values to premultiplied pixels. +/// +[Config(typeof(Config.Analysis))] +public class FromVector4Bgra32P : FromVector4 +{ + /* + BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + .NET 8.0.28, X64 RyuJIT x86-64-v4 + + | Method | Count | Mean | Error | StdDev | Ratio | Code Size | Allocated | + |---------------------------- |------ |------------:|---------:|----------:|------:|----------:|----------:| + | PixelOperations_Base | 64 | 53.35 ns | 0.208 ns | 0.311 ns | 1.00 | 1,152 B | - | + | PixelOperations_Specialized | 64 | 46.61 ns | 0.195 ns | 0.280 ns | 0.87 | 2,975 B | - | + | PixelOperations_Base | 256 | 212.11 ns | 0.719 ns | 1.076 ns | 1.00 | 1,152 B | - | + | PixelOperations_Specialized | 256 | 77.12 ns | 0.824 ns | 1.233 ns | 0.36 | 2,992 B | - | + | PixelOperations_Base | 2048 | 1,435.87 ns | 9.402 ns | 13.781 ns | 1.00 | 1,152 B | - | + | PixelOperations_Specialized | 2048 | 250.89 ns | 9.354 ns | 13.416 ns | 0.17 | 2,992 B | - | + */ } diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs index fe0d2a10a7..c6125ef8f0 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs @@ -6,49 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] -public class FromVector4_Rgb24 : FromVector4 -{ -} +[Config(typeof(Config.Short))] +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.Benchmarks/Bulk/Pad3Shuffle4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs index 1b6663e70e..16529306a8 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs @@ -8,7 +8,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Pad3Shuffle4Channel { - private static readonly DefaultPad3Shuffle4 Control = new(SimdUtils.Shuffle.MMShuffle1032); private byte[] source; private byte[] destination; @@ -25,11 +24,11 @@ public void Setup() [Benchmark] public void Pad3Shuffle4() - => SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); + => SimdUtils.Pad3Shuffle4(this.source, this.destination); [Benchmark] public void Pad3Shuffle4FastFallback() - => SimdUtils.Pad3Shuffle4(this.source, this.destination, default(XYZWPad3Shuffle4)); + => SimdUtils.Pad3Shuffle4(this.source, this.destination); } // 2020-10-30 @@ -47,37 +46,37 @@ public void Pad3Shuffle4FastFallback() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | // |------------------------- |------------------- |-------------------------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:|------:|------:|------:|----------:| -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4 | 2. AVX | Empty | 96 | 23.63 ns | 0.175 ns | 0.155 ns | 23.65 ns | 0.15 | 0.01 | - | - | - | - | -// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 96 | 24.84 ns | 0.376 ns | 0.333 ns | 24.74 ns | 1.57 | 0.06 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4 | 2. AVX | Empty | 384 | 41.41 ns | 0.859 ns | 1.204 ns | 41.33 ns | 0.16 | 0.00 | - | - | - | - | -// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 384 | 40.74 ns | 0.624 ns | 0.584 ns | 40.72 ns | 0.55 | 0.01 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4 | 2. AVX | Empty | 768 | 62.86 ns | 0.332 ns | 0.277 ns | 62.80 ns | 0.12 | 0.00 | - | - | - | - | -// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 768 | 64.72 ns | 1.306 ns | 1.090 ns | 64.51 ns | 0.59 | 0.01 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4 | 2. AVX | Empty | 1536 | 110.05 ns | 0.256 ns | 0.214 ns | 110.04 ns | 0.11 | 0.00 | - | - | - | - | -// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - | +// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 1536 | 111.54 ns | 2.173 ns | 2.901 ns | 111.27 ns | 0.51 | 0.01 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - | // 2023-02-21 // ########## @@ -94,34 +93,34 @@ public void Pad3Shuffle4FastFallback() // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | // |------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 57.45 ns | 0.126 ns | 0.118 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.70 ns | 0.105 ns | 0.098 ns | 0.26 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 57.45 ns | 0.126 ns | 0.118 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 96 | 14.70 ns | 0.105 ns | 0.098 ns | 0.26 | - | - | - | - | // | Pad3Shuffle4 | 3. AVX | Empty | 96 | 14.63 ns | 0.070 ns | 0.062 ns | 0.25 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 12.08 ns | 0.028 ns | 0.025 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.04 ns | 0.050 ns | 0.044 ns | 1.16 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 12.08 ns | 0.028 ns | 0.025 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 96 | 14.04 ns | 0.050 ns | 0.044 ns | 1.16 | - | - | - | - | // | Pad3Shuffle4FastFallback | 3. AVX | Empty | 96 | 13.90 ns | 0.086 ns | 0.080 ns | 1.15 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 202.67 ns | 2.010 ns | 1.678 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.54 ns | 0.060 ns | 0.053 ns | 0.13 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 202.67 ns | 2.010 ns | 1.678 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 384 | 25.54 ns | 0.060 ns | 0.053 ns | 0.13 | - | - | - | - | // | Pad3Shuffle4 | 3. AVX | Empty | 384 | 25.72 ns | 0.139 ns | 0.130 ns | 0.13 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 60.35 ns | 0.080 ns | 0.071 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.18 ns | 0.388 ns | 0.324 ns | 0.42 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 60.35 ns | 0.080 ns | 0.071 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 384 | 25.18 ns | 0.388 ns | 0.324 ns | 0.42 | - | - | - | - | // | Pad3Shuffle4FastFallback | 3. AVX | Empty | 384 | 26.21 ns | 0.067 ns | 0.059 ns | 0.43 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 393.88 ns | 1.353 ns | 1.199 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 39.44 ns | 0.230 ns | 0.204 ns | 0.10 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 393.88 ns | 1.353 ns | 1.199 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 768 | 39.44 ns | 0.230 ns | 0.204 ns | 0.10 | - | - | - | - | // | Pad3Shuffle4 | 3. AVX | Empty | 768 | 39.51 ns | 0.108 ns | 0.101 ns | 0.10 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 112.02 ns | 0.140 ns | 0.131 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 768 | 38.60 ns | 0.091 ns | 0.080 ns | 0.34 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 112.02 ns | 0.140 ns | 0.131 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 768 | 38.60 ns | 0.091 ns | 0.080 ns | 0.34 | - | - | - | - | // | Pad3Shuffle4FastFallback | 3. AVX | Empty | 768 | 38.18 ns | 0.100 ns | 0.084 ns | 0.34 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 777.95 ns | 1.719 ns | 1.342 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 73.11 ns | 0.090 ns | 0.075 ns | 0.09 | - | - | - | - | +// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 777.95 ns | 1.719 ns | 1.342 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 73.11 ns | 0.090 ns | 0.075 ns | 0.09 | - | - | - | - | // | Pad3Shuffle4 | 3. AVX | Empty | 1536 | 73.41 ns | 0.125 ns | 0.117 ns | 0.09 | - | - | - | - | // | | | | | | | | | | | | | -// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 218.14 ns | 0.377 ns | 0.334 ns | 1.00 | - | - | - | - | -// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 72.55 ns | 1.418 ns | 1.184 ns | 0.33 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 218.14 ns | 0.377 ns | 0.334 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 72.55 ns | 1.418 ns | 1.184 ns | 0.33 | - | - | - | - | // | Pad3Shuffle4FastFallback | 3. AVX | Empty | 1536 | 73.15 ns | 0.330 ns | 0.292 ns | 0.34 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs index 8b7b89eb36..e09c4ce6ce 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs @@ -8,7 +8,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Shuffle3Channel { - private static readonly DefaultShuffle3 Control = new(SimdUtils.Shuffle.MMShuffle3102); private byte[] source; private byte[] destination; @@ -25,7 +24,7 @@ public void Setup() [Benchmark] public void Shuffle3() - => SimdUtils.Shuffle3(this.source, this.destination, Control); + => SimdUtils.Shuffle3(this.source, this.destination); } // 2020-11-02 @@ -43,21 +42,21 @@ public void Shuffle3() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | // |--------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle3 | 2. AVX | Empty | 96 | 32.42 ns | 0.537 ns | 0.476 ns | 32.34 ns | 0.66 | 0.04 | - | - | - | - | -// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - | +// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle3 | 2. AVX | Empty | 384 | 71.20 ns | 2.654 ns | 7.784 ns | 69.60 ns | 0.41 | 0.02 | - | - | - | - | -// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - | +// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle3 | 2. AVX | Empty | 768 | 109.12 ns | 2.149 ns | 2.010 ns | 108.66 ns | 0.28 | 0.01 | - | - | - | - | -// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - | +// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle3 | 2. AVX | Empty | 1536 | 190.41 ns | 1.090 ns | 0.851 ns | 190.38 ns | 0.25 | 0.00 | - | - | - | - | -// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - | +// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - | // 2023-02-21 // ########## @@ -74,18 +73,18 @@ public void Shuffle3() // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | // |--------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 44.55 ns | 0.564 ns | 0.528 ns | 1.00 | - | - | - | - | -// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 15.46 ns | 0.064 ns | 0.060 ns | 0.35 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 44.55 ns | 0.564 ns | 0.528 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 96 | 15.46 ns | 0.064 ns | 0.060 ns | 0.35 | - | - | - | - | // | Shuffle3 | 3. AVX | Empty | 96 | 15.18 ns | 0.056 ns | 0.053 ns | 0.34 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 155.68 ns | 0.539 ns | 0.504 ns | 1.00 | - | - | - | - | -// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 30.04 ns | 0.100 ns | 0.089 ns | 0.19 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 155.68 ns | 0.539 ns | 0.504 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 384 | 30.04 ns | 0.100 ns | 0.089 ns | 0.19 | - | - | - | - | // | Shuffle3 | 3. AVX | Empty | 384 | 29.70 ns | 0.061 ns | 0.054 ns | 0.19 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 302.76 ns | 1.023 ns | 0.957 ns | 1.00 | - | - | - | - | -// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 50.24 ns | 0.098 ns | 0.092 ns | 0.17 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 302.76 ns | 1.023 ns | 0.957 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 768 | 50.24 ns | 0.098 ns | 0.092 ns | 0.17 | - | - | - | - | // | Shuffle3 | 3. AVX | Empty | 768 | 49.28 ns | 0.156 ns | 0.131 ns | 0.16 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 596.53 ns | 2.675 ns | 2.503 ns | 1.00 | - | - | - | - | -// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 94.09 ns | 0.312 ns | 0.260 ns | 0.16 | - | - | - | - | +// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 596.53 ns | 2.675 ns | 2.503 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 94.09 ns | 0.312 ns | 0.260 ns | 0.16 | - | - | - | - | // | Shuffle3 | 3. AVX | Empty | 1536 | 93.57 ns | 0.196 ns | 0.183 ns | 0.16 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs index 5ade55c73f..4b694768d7 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs @@ -8,7 +8,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Shuffle4Slice3Channel { - private static readonly DefaultShuffle4Slice3 Control = new(SimdUtils.Shuffle.MMShuffle1032); private byte[] source; private byte[] destination; @@ -25,11 +24,11 @@ public void Setup() [Benchmark] public void Shuffle4Slice3() - => SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); + => SimdUtils.Shuffle4Slice3(this.source, this.destination); [Benchmark] public void Shuffle4Slice3FastFallback() - => SimdUtils.Shuffle4Slice3(this.source, this.destination, default(XYZWShuffle4Slice3)); + => SimdUtils.Shuffle4Slice3(this.source, this.destination); } // 2020-10-29 @@ -47,45 +46,45 @@ public void Shuffle4Slice3FastFallback() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | // |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:| -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3 | 2. AVX | Empty | 128 | 27.15 ns | 0.556 ns | 0.762 ns | 27.34 ns | 0.41 | 0.03 | - | - | - | - | -// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 128 | 26.15 ns | 0.113 ns | 0.106 ns | 26.16 ns | 1.01 | 0.02 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3 | 2. AVX | Empty | 256 | 32.61 ns | 0.107 ns | 0.095 ns | 32.62 ns | 0.33 | 0.00 | - | - | - | - | -// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 256 | 32.16 ns | 0.111 ns | 0.104 ns | 32.16 ns | 0.61 | 0.01 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3 | 2. AVX | Empty | 512 | 51.03 ns | 0.535 ns | 0.501 ns | 51.18 ns | 0.24 | 0.01 | - | - | - | - | -// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 512 | 50.33 ns | 0.382 ns | 0.339 ns | 50.41 ns | 0.42 | 0.01 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3 | 2. AVX | Empty | 1024 | 77.13 ns | 1.355 ns | 2.264 ns | 76.19 ns | 0.19 | 0.01 | - | - | - | - | -// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 1024 | 80.25 ns | 1.647 ns | 2.082 ns | 80.98 ns | 0.35 | 0.01 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3 | 2. AVX | Empty | 2048 | 128.41 ns | 0.417 ns | 0.390 ns | 128.24 ns | 0.16 | 0.00 | - | - | - | - | -// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - | +// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 2048 | 126.93 ns | 0.382 ns | 0.339 ns | 126.94 ns | 0.33 | 0.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - | // 2023-02-21 // ########## @@ -102,42 +101,42 @@ public void Shuffle4Slice3FastFallback() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | // |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 45.59 ns | 0.166 ns | 0.147 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 128 | 15.62 ns | 0.056 ns | 0.052 ns | 0.34 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 45.59 ns | 0.166 ns | 0.147 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 128 | 15.62 ns | 0.056 ns | 0.052 ns | 0.34 | - | - | - | - | // | Shuffle4Slice3 | 3. AVX | Empty | 128 | 16.37 ns | 0.047 ns | 0.040 ns | 0.36 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 13.23 ns | 0.028 ns | 0.026 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 128 | 14.41 ns | 0.013 ns | 0.012 ns | 1.09 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 13.23 ns | 0.028 ns | 0.026 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 128 | 14.41 ns | 0.013 ns | 0.012 ns | 1.09 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. AVX | Empty | 128 | 14.70 ns | 0.050 ns | 0.047 ns | 1.11 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 85.48 ns | 0.192 ns | 0.179 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.18 ns | 0.230 ns | 0.204 ns | 0.22 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 85.48 ns | 0.192 ns | 0.179 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 256 | 19.18 ns | 0.230 ns | 0.204 ns | 0.22 | - | - | - | - | // | Shuffle4Slice3 | 3. AVX | Empty | 256 | 18.66 ns | 0.017 ns | 0.015 ns | 0.22 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 24.34 ns | 0.078 ns | 0.073 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 256 | 18.58 ns | 0.061 ns | 0.057 ns | 0.76 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 24.34 ns | 0.078 ns | 0.073 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 256 | 18.58 ns | 0.061 ns | 0.057 ns | 0.76 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. AVX | Empty | 256 | 19.23 ns | 0.018 ns | 0.016 ns | 0.79 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 165.31 ns | 0.742 ns | 0.694 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 512 | 28.10 ns | 0.077 ns | 0.068 ns | 0.17 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 165.31 ns | 0.742 ns | 0.694 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 512 | 28.10 ns | 0.077 ns | 0.068 ns | 0.17 | - | - | - | - | // | Shuffle4Slice3 | 3. AVX | Empty | 512 | 28.99 ns | 0.018 ns | 0.014 ns | 0.18 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 53.45 ns | 0.270 ns | 0.226 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 512 | 27.50 ns | 0.034 ns | 0.028 ns | 0.51 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 53.45 ns | 0.270 ns | 0.226 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 512 | 27.50 ns | 0.034 ns | 0.028 ns | 0.51 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. AVX | Empty | 512 | 28.76 ns | 0.017 ns | 0.015 ns | 0.54 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 323.87 ns | 0.549 ns | 0.487 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.81 ns | 0.056 ns | 0.050 ns | 0.13 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 323.87 ns | 0.549 ns | 0.487 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 40.81 ns | 0.056 ns | 0.050 ns | 0.13 | - | - | - | - | // | Shuffle4Slice3 | 3. AVX | Empty | 1024 | 39.95 ns | 0.075 ns | 0.067 ns | 0.12 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 101.37 ns | 0.080 ns | 0.067 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.72 ns | 0.049 ns | 0.041 ns | 0.40 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 101.37 ns | 0.080 ns | 0.067 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 40.72 ns | 0.049 ns | 0.041 ns | 0.40 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. AVX | Empty | 1024 | 39.78 ns | 0.029 ns | 0.027 ns | 0.39 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 642.95 ns | 2.067 ns | 1.933 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 73.19 ns | 0.082 ns | 0.077 ns | 0.11 | - | - | - | - | +// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 642.95 ns | 2.067 ns | 1.933 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 73.19 ns | 0.082 ns | 0.077 ns | 0.11 | - | - | - | - | // | Shuffle4Slice3 | 3. AVX | Empty | 2048 | 69.83 ns | 0.319 ns | 0.267 ns | 0.11 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 196.85 ns | 0.238 ns | 0.211 ns | 1.00 | - | - | - | - | -// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 72.89 ns | 0.117 ns | 0.098 ns | 0.37 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 196.85 ns | 0.238 ns | 0.211 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 72.89 ns | 0.117 ns | 0.098 ns | 0.37 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. AVX | Empty | 2048 | 69.59 ns | 0.073 ns | 0.061 ns | 0.35 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs index 911c4e0a58..f3ad974c47 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs @@ -24,7 +24,7 @@ public void Setup() [Benchmark] public void Shuffle4Channel() - => SimdUtils.Shuffle4(this.source, this.destination, default); + => SimdUtils.Shuffle4(this.source, this.destination); } // 2020-10-29 @@ -42,25 +42,25 @@ public void Shuffle4Channel() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | // |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:| -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 128 | 21.72 ns | 0.299 ns | 0.279 ns | 1.25 | 0.02 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 256 | 23.90 ns | 0.508 ns | 0.820 ns | 0.69 | 0.02 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 512 | 26.10 ns | 0.418 ns | 0.391 ns | 0.36 | 0.01 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 1024 | 38.67 ns | 0.801 ns | 1.889 ns | 0.24 | 0.02 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 2048 | 57.37 ns | 1.152 ns | 1.078 ns | 0.18 | 0.01 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - | // 2023-02-21 // ########## @@ -77,22 +77,22 @@ public void Shuffle4Channel() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | // |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:| -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 10.76 ns | 0.033 ns | 0.029 ns | 1.00 | 0.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.39 ns | 0.045 ns | 0.040 ns | 1.06 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 10.76 ns | 0.033 ns | 0.029 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 128 | 11.39 ns | 0.045 ns | 0.040 ns | 1.06 | 0.01 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 128 | 14.05 ns | 0.029 ns | 0.024 ns | 1.31 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 32.09 ns | 0.655 ns | 1.000 ns | 1.00 | 0.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 14.03 ns | 0.047 ns | 0.041 ns | 0.44 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 32.09 ns | 0.655 ns | 1.000 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 256 | 14.03 ns | 0.047 ns | 0.041 ns | 0.44 | 0.02 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 256 | 15.18 ns | 0.052 ns | 0.043 ns | 0.48 | 0.03 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 59.26 ns | 0.084 ns | 0.070 ns | 1.00 | 0.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 18.80 ns | 0.036 ns | 0.034 ns | 0.32 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 59.26 ns | 0.084 ns | 0.070 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 512 | 18.80 ns | 0.036 ns | 0.034 ns | 0.32 | 0.00 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 512 | 17.69 ns | 0.038 ns | 0.034 ns | 0.30 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 112.48 ns | 0.285 ns | 0.253 ns | 1.00 | 0.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 31.57 ns | 0.041 ns | 0.036 ns | 0.28 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 112.48 ns | 0.285 ns | 0.253 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 31.57 ns | 0.041 ns | 0.036 ns | 0.28 | 0.00 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 1024 | 28.41 ns | 0.068 ns | 0.064 ns | 0.25 | 0.00 | - | - | - | - | // | | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 218.59 ns | 0.303 ns | 0.283 ns | 1.00 | 0.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 53.04 ns | 0.106 ns | 0.099 ns | 0.24 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 218.59 ns | 0.303 ns | 0.283 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 53.04 ns | 0.106 ns | 0.099 ns | 0.24 | 0.00 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 2048 | 34.74 ns | 0.061 ns | 0.054 ns | 0.16 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs index 5bb3cf9165..7cc894486f 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs @@ -42,25 +42,25 @@ public void Shuffle4Channel() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | // |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:| -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 128 | 9.818 ns | 0.1457 ns | 0.1292 ns | 0.15 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 256 | 15.878 ns | 0.1983 ns | 0.1758 ns | 0.13 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 512 | 29.452 ns | 0.3334 ns | 0.3118 ns | 0.11 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 1024 | 53.757 ns | 0.3212 ns | 0.2847 ns | 0.11 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 2048 | 105.120 ns | 0.6140 ns | 0.5443 ns | 0.11 | - | - | - | - | -// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - | +// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - | // 2023-02-21 // ########## @@ -77,22 +77,22 @@ public void Shuffle4Channel() // // | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | // |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:| -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 57.819 ns | 0.2360 ns | 0.1970 ns | 1.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.564 ns | 0.0234 ns | 0.0195 ns | 0.20 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 57.819 ns | 0.2360 ns | 0.1970 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 128 | 11.564 ns | 0.0234 ns | 0.0195 ns | 0.20 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 128 | 7.770 ns | 0.0696 ns | 0.0617 ns | 0.13 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 105.282 ns | 0.2713 ns | 0.2405 ns | 1.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.867 ns | 0.0393 ns | 0.0348 ns | 0.19 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 105.282 ns | 0.2713 ns | 0.2405 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 256 | 19.867 ns | 0.0393 ns | 0.0348 ns | 0.19 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 256 | 17.586 ns | 0.0582 ns | 0.0544 ns | 0.17 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 200.799 ns | 0.5678 ns | 0.5033 ns | 1.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 41.137 ns | 0.1524 ns | 0.1351 ns | 0.20 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 200.799 ns | 0.5678 ns | 0.5033 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 512 | 41.137 ns | 0.1524 ns | 0.1351 ns | 0.20 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 512 | 24.040 ns | 0.0445 ns | 0.0395 ns | 0.12 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 401.046 ns | 0.5865 ns | 0.5199 ns | 1.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 94.904 ns | 0.4633 ns | 0.4334 ns | 0.24 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 401.046 ns | 0.5865 ns | 0.5199 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 94.904 ns | 0.4633 ns | 0.4334 ns | 0.24 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 1024 | 68.456 ns | 0.1192 ns | 0.0996 ns | 0.17 | - | - | - | - | // | | | | | | | | | | | | | -// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 772.297 ns | 0.6270 ns | 0.5558 ns | 1.00 | - | - | - | - | -// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 184.561 ns | 0.4319 ns | 0.4040 ns | 0.24 | - | - | - | - | +// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 772.297 ns | 0.6270 ns | 0.5558 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 184.561 ns | 0.4319 ns | 0.4040 ns | 0.24 | - | - | - | - | // | Shuffle4Channel | 3. AVX | Empty | 2048 | 133.634 ns | 1.7864 ns | 1.8345 ns | 0.17 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs index 6d3f8f9528..19ab780c01 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs @@ -47,8 +47,7 @@ public void Naive() { 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/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs index 3b4360b161..0df8d9818c 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 void Naive() } [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 2f1064439b..38a09483bf 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class ToVector4_Bgra32 : ToVector4 { [Benchmark(Baseline = true)] @@ -16,28 +16,52 @@ public void PixelOperations_Base() { new PixelOperations().ToVector4( this.Configuration, - this.source.GetSpan(), - this.destination.GetSpan()); + this.Source.GetSpan(), + this.Destination.GetSpan()); } - // RESULTS: - // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | - // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:| - // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B | - // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B | - // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B | - // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B | - // | | | | | | | | | | - // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B | - // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B | + // BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + // AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + // .NET 8.0.28, X64 RyuJIT x86-64-v4 + // + // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Allocated | + // |---------------------------- |------ |------------:|------------:|----------:|------:|--------:|----------:| + // | PixelOperations_Base | 64 | 58.30 ns | 20.13 ns | 1.103 ns | 1.00 | 0.02 | - | + // | PixelOperations_Specialized | 64 | 58.95 ns | 23.68 ns | 1.298 ns | 1.01 | 0.03 | - | + // | PixelOperations_Base | 256 | 226.95 ns | 84.03 ns | 4.606 ns | 1.00 | 0.02 | - | + // | PixelOperations_Specialized | 256 | 229.70 ns | 40.00 ns | 2.192 ns | 1.01 | 0.02 | - | + // | PixelOperations_Base | 2048 | 1,795.42 ns | 1,465.95 ns | 80.354 ns | 1.00 | 0.05 | - | + // | PixelOperations_Specialized | 2048 | 291.89 ns | 109.98 ns | 6.028 ns | 0.16 | 0.01 | - | +} + +/// +/// Measures bulk conversion from premultiplied pixels to values. +/// +[Config(typeof(Config.Analysis))] +public class ToVector4_Bgra32P : ToVector4 +{ + /// + /// Measures the scalar implementation used as the comparison baseline. + /// + [Benchmark(Baseline = true)] + public void PixelOperations_Base() + { + new PixelOperations().ToVector4( + this.Configuration, + this.Source.GetSpan(), + this.Destination.GetSpan()); + } + + // BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) + // AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores + // .NET 8.0.28, X64 RyuJIT x86-64-v4 + // + // | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Allocated | + // |---------------------------- |------ |------------:|---------:|---------:|------:|--------:|----------:|----------:| + // | PixelOperations_Base | 64 | 58.05 ns | 3.204 ns | 4.795 ns | 1.01 | 0.11 | 932 B | - | + // | PixelOperations_Specialized | 64 | 84.78 ns | 0.467 ns | 0.669 ns | 1.47 | 0.12 | 3,731 B | - | + // | PixelOperations_Base | 256 | 208.93 ns | 0.345 ns | 0.484 ns | 1.00 | 0.00 | 958 B | - | + // | PixelOperations_Specialized | 256 | 106.50 ns | 0.280 ns | 0.410 ns | 0.51 | 0.00 | 3,738 B | - | + // | PixelOperations_Base | 2048 | 1,617.39 ns | 3.380 ns | 4.848 ns | 1.00 | 0.00 | 958 B | - | + // | PixelOperations_Specialized | 2048 | 269.90 ns | 0.666 ns | 0.976 ns | 0.17 | 0.00 | 3,735 B | - | } diff --git a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs index 2c700a733f..adedabf8f5 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class ToVector4_Rgb24 : ToVector4 { [Benchmark(Baseline = true)] @@ -16,8 +16,8 @@ public void PixelOperations_Base() { 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 9abf0ed22a..113793a033 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs @@ -11,39 +11,21 @@ namespace SixLabors.ImageSharp.Benchmarks.Bulk; -[Config(typeof(Config.ShortCore31))] +[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()); - - [Benchmark] - public void ExtendedIntrinsics() - { - Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); - Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - - SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); - } + 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); } @@ -51,8 +33,8 @@ public void HwIntrinsics() // [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; @@ -76,14 +58,14 @@ public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() } 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; @@ -93,14 +75,14 @@ public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() // [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++) { @@ -126,8 +108,8 @@ public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_ConvertInSameLoo [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; } @@ -160,4 +142,30 @@ private static Vector ConvertToNormalizedSingle(Vector u, Vector (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs index 5bd806c487..eec926a23c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeBmp { private byte[] bmpBytes; @@ -19,12 +19,7 @@ private string TestImageFullPath [GlobalSetup] public void ReadImages() - { - if (this.bmpBytes == null) - { - this.bmpBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + => this.bmpBytes ??= File.ReadAllBytes(this.TestImageFullPath); [Params(TestImages.Bmp.Car)] public string TestImage { get; set; } @@ -32,16 +27,16 @@ public void ReadImages() [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] public SDSize BmpSystemDrawing() { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.bmpBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "ImageSharp Bmp")] public Size BmpImageSharp() { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.bmpBytes); + using Image image = Image.Load(memoryStream); return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs index ab6cdf28e0..4c0a6c47b3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs @@ -9,10 +9,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeBmp { - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; @@ -40,14 +40,14 @@ public void Cleanup() [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] public void BmpSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Bmp); } [Benchmark(Description = "ImageSharp Bmp")] public void BmpImageSharp() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpCore.SaveAsBmp(memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs index 50afea5a6d..ce54c133b0 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs @@ -7,10 +7,10 @@ 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" }; + protected override IEnumerable InputImageSubfoldersOrFiles => ["Bmp/", "Jpg/baseline"]; [Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] public void EncodeBmpImageSharp() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Exr/DecodeExr.cs b/tests/ImageSharp.Benchmarks/Codecs/Exr/DecodeExr.cs new file mode 100644 index 0000000000..45021cadc0 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Exr/DecodeExr.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; + +using ImageMagick; +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +[MarkdownExporter] +[HtmlExporter] +[Config(typeof(Config.Short))] +public class DecodeExr +{ + private Configuration configuration; + + private byte[] imageBytes; + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params(TestImages.Exr.Benchmark)] + public string TestImage { get; set; } + + [GlobalSetup] + public void ReadImages() + { + this.configuration = Configuration.CreateDefaultInstance(); + new ExrConfigurationModule().Configure(this.configuration); + + this.imageBytes ??= File.ReadAllBytes(this.TestImageFullPath); + } + + [Benchmark(Description = "Magick Exr")] + public uint ExrImageMagick() + { + MagickReadSettings settings = new() { Format = MagickFormat.Exr }; + using MemoryStream memoryStream = new(this.imageBytes); + using MagickImage image = new(memoryStream, settings); + return image.Width; + } + + [Benchmark(Description = "ImageSharp Exr")] + public int ExrImageSharp() + { + using MemoryStream memoryStream = new(this.imageBytes); + using Image image = Image.Load(memoryStream); + return image.Height; + } + + /* Results 27.03.2026 + BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8037/25H2/2025Update/HudsonValley2) + Intel Core i7-14700T 1.30GHz, 1 CPU, 28 logical and 20 physical cores + .NET SDK 10.0.201 + [Host] : .NET 8.0.25 (8.0.25, 8.0.2526.11203), X64 RyuJIT x86-64-v3 + Job-VDWIGO : .NET 8.0.25 (8.0.25, 8.0.2526.11203), X64 RyuJIT x86-64-v3 + + Runtime=.NET 8.0 Arguments=/p:DebugType=portable IterationCount=3 + LaunchCount=1 WarmupCount=3 + + | Method | TestImage | Mean | Error | StdDev | Allocated | + |----------------- |----------------------------- |---------:|---------:|---------:|----------:| + | 'Magick Exr' | Exr/Calliphora_benchmark.exr | 20.37 ms | 0.790 ms | 0.043 ms | 12.98 KB | + | 'ImageSharp Exr' | Exr/Calliphora_benchmark.exr | 45.68 ms | 4.999 ms | 0.274 ms | 34.09 KB | + */ +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeEncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeEncodeGif.cs new file mode 100644 index 0000000000..3238e8dac2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeEncodeGif.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Drawing.Imaging; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +using SixLabors.ImageSharp.Tests; +using SDImage = System.Drawing.Image; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs; + +public abstract class DecodeEncodeGif +{ + private MemoryStream outputStream; + + protected abstract GifEncoder Encoder { get; } + + [Params(TestImages.Gif.Leo, TestImages.Gif.Cheers)] + public string TestImage { get; set; } + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [GlobalSetup] + public void Setup() => this.outputStream = new MemoryStream(); + + [GlobalCleanup] + public void Cleanup() => this.outputStream.Close(); + + [Benchmark(Baseline = true)] + public void SystemDrawing() + { + this.outputStream.Position = 0; + using SDImage image = SDImage.FromFile(this.TestImageFullPath); + image.Save(this.outputStream, ImageFormat.Gif); + } + + [Benchmark] + public void ImageSharp() + { + this.outputStream.Position = 0; + using Image image = Image.Load(this.TestImageFullPath); + image.SaveAsGif(this.outputStream, this.Encoder); + } +} + +public class DecodeEncodeGif_DefaultEncoder : DecodeEncodeGif +{ + protected override GifEncoder Encoder => new(); +} + +public class DecodeEncodeGif_CoarsePaletteEncoder : DecodeEncodeGif +{ + protected override GifEncoder Encoder => new() + { + Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4, ColorMatchingMode = ColorMatchingMode.Coarse }) + }; +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs index 21b193ddaf..117cdd25b3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class DecodeGif { private byte[] gifBytes; @@ -18,30 +18,24 @@ private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [GlobalSetup] - public void ReadImages() - { - if (this.gifBytes == null) - { - this.gifBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + public void ReadImages() => this.gifBytes ??= File.ReadAllBytes(this.TestImageFullPath); - [Params(TestImages.Gif.Rings)] + [Params(TestImages.Gif.Cheers)] public string TestImage { get; set; } [Benchmark(Baseline = true, Description = "System.Drawing Gif")] public SDSize GifSystemDrawing() { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.gifBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "ImageSharp Gif")] public Size GifImageSharp() { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.gifBytes); + using Image image = Image.Load(memoryStream); return new Size(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs index 048c2aadda..b8f8f78517 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs @@ -3,6 +3,7 @@ using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; +using ImageMagick; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -12,21 +13,17 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] -public class EncodeGif +public abstract class EncodeGif { // System.Drawing needs this. - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; + private MagickImageCollection magickImage; - // Try to get as close to System.Drawing's output as possible - private readonly GifEncoder encoder = new GifEncoder - { - Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) - }; + protected abstract GifEncoder Encoder { get; } - [Params(TestImages.Bmp.Car, TestImages.Png.Rgb48Bpp)] + [Params(TestImages.Gif.Leo, TestImages.Gif.Cheers)] public string TestImage { get; set; } [GlobalSetup] @@ -34,10 +31,14 @@ public void ReadImages() { if (this.bmpStream == null) { - this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage)); + string filePath = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + this.bmpStream = File.OpenRead(filePath); this.bmpCore = Image.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = SDImage.FromStream(this.bmpStream); + + this.bmpStream.Position = 0; + this.magickImage = new MagickImageCollection(this.bmpStream); } } @@ -48,19 +49,40 @@ public void Cleanup() this.bmpStream = null; this.bmpCore.Dispose(); this.bmpDrawing.Dispose(); + this.magickImage.Dispose(); } [Benchmark(Baseline = true, Description = "System.Drawing Gif")] public void GifSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Gif); } [Benchmark(Description = "ImageSharp Gif")] public void GifImageSharp() { - using var memoryStream = new MemoryStream(); - this.bmpCore.SaveAsGif(memoryStream, this.encoder); + using MemoryStream memoryStream = new(); + this.bmpCore.SaveAsGif(memoryStream, this.Encoder); } + + [Benchmark(Description = "Magick.NET Gif")] + public void GifMagickNet() + { + using MemoryStream ms = new(); + this.magickImage.Write(ms, MagickFormat.Gif); + } +} + +public class EncodeGif_DefaultEncoder : EncodeGif +{ + protected override GifEncoder Encoder => new(); +} + +public class EncodeGif_CoarsePaletteEncoder : EncodeGif +{ + protected override GifEncoder Encoder => new() + { + Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4, ColorMatchingMode = ColorMatchingMode.Coarse }) + }; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs index c523b5c204..1eca07ec79 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs @@ -9,20 +9,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { [Params(InputImageCategory.AllImages)] public override InputImageCategory InputCategory { get; set; } - protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Gif/" }; + protected override IEnumerable InputImageSubfoldersOrFiles => ["Gif/"]; [Benchmark(Description = "EncodeGifMultiple - ImageSharp")] public void EncodeGifImageSharp() => this.ForEachImageSharpImage((img, ms) => { // Try to get as close to System.Drawing's output as possible - var options = new GifEncoder + GifEncoder options = new() { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) }; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 72b6bb72e8..4ea0a92f19 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -185,15 +185,15 @@ private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 dest ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); - var xLeft = new Vector2(sLeft.X); - var yLeft = new Vector2(sLeft.Y); - var zLeft = new Vector2(sLeft.Z); - var wLeft = new Vector2(sLeft.W); + Vector2 xLeft = new(sLeft.X); + Vector2 yLeft = new(sLeft.Y); + Vector2 zLeft = new(sLeft.Z); + Vector2 wLeft = new(sLeft.W); - var xRight = new Vector2(sRight.X); - var yRight = new Vector2(sRight.Y); - var zRight = new Vector2(sRight.Z); - var wRight = new Vector2(sRight.W); + Vector2 xRight = new(sRight.X); + Vector2 yRight = new(sRight.Y); + Vector2 zRight = new(sRight.Z); + Vector2 wRight = new(sRight.W); dTopLeft = xLeft; Unsafe.Add(ref dTopLeft, 1) = yLeft; @@ -245,15 +245,15 @@ private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 dest ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); - var xLeft = new Vector4(sLeft.X); - var yLeft = new Vector4(sLeft.Y); - var zLeft = new Vector4(sLeft.Z); - var wLeft = new Vector4(sLeft.W); + Vector4 xLeft = new(sLeft.X); + Vector4 yLeft = new(sLeft.Y); + Vector4 zLeft = new(sLeft.Z); + Vector4 wLeft = new(sLeft.W); - var xRight = new Vector4(sRight.X); - var yRight = new Vector4(sRight.Y); - var zRight = new Vector4(sRight.Z); - var wRight = new Vector4(sRight.W); + Vector4 xRight = new(sRight.X); + Vector4 yRight = new(sRight.Y); + Vector4 zRight = new(sRight.Z); + Vector4 wRight = new(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; @@ -303,15 +303,15 @@ private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, (uint)(2 * row * destStride)); ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); - var xLeft = new Vector4(sLeft.X); - var yLeft = new Vector4(sLeft.Y); - var zLeft = new Vector4(sLeft.Z); - var wLeft = new Vector4(sLeft.W); + Vector4 xLeft = new(sLeft.X); + Vector4 yLeft = new(sLeft.Y); + Vector4 zLeft = new(sLeft.Z); + Vector4 wLeft = new(sLeft.W); - var xRight = new Vector4(sRight.X); - var yRight = new Vector4(sRight.Y); - var zRight = new Vector4(sRight.Z); - var wRight = new Vector2(sRight.W); + Vector4 xRight = new(sRight.X); + Vector4 yRight = new(sRight.Y); + Vector4 zRight = new(sRight.Z); + Vector2 wRight = new(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; @@ -362,25 +362,25 @@ private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 d ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, (uint)offset)); ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, (uint)(offset + destStride))); - var xyLeft = new Vector4(sLeft.X) + Vector4 xyLeft = new(sLeft.X) { Z = sLeft.Y, W = sLeft.Y }; - var zwLeft = new Vector4(sLeft.Z) + Vector4 zwLeft = new(sLeft.Z) { Z = sLeft.W, W = sLeft.W }; - var xyRight = new Vector4(sRight.X) + Vector4 xyRight = new(sRight.X) { Z = sRight.Y, W = sRight.Y }; - var zwRight = new Vector4(sRight.Z) + Vector4 zwRight = new(sRight.Z) { Z = sRight.W, W = sRight.W diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs index efc347586c..45c6f63b9c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; public unsafe class Block8x8F_DivideRound { private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks - private static readonly Vector4 MinusOne = new Vector4(-1); - private static readonly Vector4 Half = new Vector4(0.5f); + private static readonly Vector4 MinusOne = new(-1); + private static readonly Vector4 Half = new(0.5f); private Block8x8F inputDividend; private Block8x8F inputDivisor; @@ -140,7 +140,7 @@ private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { - var sign = Vector4.Min(dividend, Vector4.One); + Vector4 sign = Vector4.Min(dividend, Vector4.One); sign = Vector4.Max(sign, MinusOne); return (dividend / divisor) + (sign * Half); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs index 91255c9466..25b5e973e7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs @@ -12,8 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; public class Block8x8F_LoadFromInt16 { private Block8x8 source; - - private Block8x8F dest = default; + private Block8x8F destination; [GlobalSetup] public void Setup() @@ -30,16 +29,10 @@ public void Setup() } [Benchmark(Baseline = true)] - public void Scalar() - { - this.dest.LoadFromInt16Scalar(ref this.source); - } + public void Scalar() => this.destination.LoadFromInt16Scalar(ref this.source); [Benchmark] - public void ExtendedAvx2() - { - this.dest.LoadFromInt16ExtendedAvx2(ref this.source); - } + public void ExtendedAvx2() => this.destination.LoadFromInt16ExtendedVector256(ref this.source); // RESULT: // Method | Mean | Error | StdDev | Scaled | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs index 722b095870..3c03f3e056 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs @@ -20,7 +20,7 @@ public void MultiplyInPlaceBlock() private static Block8x8F Create8x8FloatData() { - var result = new float[64]; + float[] result = new float[64]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs index b1718759ea..88b8877e52 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs @@ -11,7 +11,7 @@ public class Block8x8F_Quantize { private Block8x8F block = CreateFromScalar(1); private Block8x8F quant = CreateFromScalar(1); - private Block8x8 result = default; + private Block8x8 result; [Benchmark] public short Quantize() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 8a520b22d3..1d83851686 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -36,7 +36,7 @@ public void Setup() if (ptr % 16 != 0) { - throw new Exception("ptr is unaligned"); + throw new InvalidOperationException("ptr is unaligned"); } this.alignedPtr = (float*)ptr; @@ -67,21 +67,21 @@ public void SimdUtils_FastRound_Vector8() ref Block8x8F b = ref this.block; ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); + row7 = row7.FastRound(); } [Benchmark] @@ -90,21 +90,21 @@ public void SimdUtils_FastRound_Vector8_ForceAligned() ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); + row7 = row7.FastRound(); } [Benchmark] @@ -117,20 +117,20 @@ public void SimdUtils_FastRound_Vector8_Grouped() ref Vector row2 = ref Unsafe.As>(ref b.V2L); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); row0 = ref Unsafe.As>(ref b.V4L); row1 = ref Unsafe.As>(ref b.V5L); row2 = ref Unsafe.As>(ref b.V6L); row3 = ref Unsafe.As>(ref b.V7L); - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); } [Benchmark] @@ -174,7 +174,7 @@ public void Sse41_V1() } [Benchmark] - public unsafe void Sse41_V2() + public void Sse41_V2() { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); @@ -214,7 +214,7 @@ public unsafe void Sse41_V2() } [Benchmark] - public unsafe void Sse41_V3() + public void Sse41_V3() { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); @@ -228,7 +228,7 @@ public unsafe void Sse41_V3() } [Benchmark] - public unsafe void Sse41_V4() + public void Sse41_V4() { ref Vector128 p = ref Unsafe.As>(ref this.block); nuint offset = (uint)sizeof(Vector128); @@ -271,7 +271,7 @@ public unsafe void Sse41_V4() } [Benchmark] - public unsafe void Sse41_V5_Unaligned() + public void Sse41_V5_Unaligned() { float* p = this.alignedPtr + 1; @@ -356,7 +356,7 @@ public unsafe void Sse41_V5_Unaligned() } [Benchmark] - public unsafe void Sse41_V5_Aligned() + public void Sse41_V5_Aligned() { float* p = this.alignedPtr; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs index 07907f21d7..caca630bc2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs @@ -14,7 +14,7 @@ public class Block8x8F_Transpose [Benchmark] public float TransposeInplace() { - this.source.TransposeInplace(); + this.source.TransposeInPlace(); return this.source[0]; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 51cd02bc7a..ae42298071 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -6,43 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class CmykColorConversion : ColorConversionBenchmark { + private readonly JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Cmyk, 8); + + /// + /// Initializes a new instance of the class. + /// public CmykColorConversion() : base(4) { } - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); - } - + /// + /// Converts one CMYK component row through the adaptive operator traversal. + /// [Benchmark] - public void SimdVectorArm64() + public void ConvertToRgb() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.CmykArm64(8).ConvertToRgbInplace(values); + this.converter.ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs index 8964667b74..106bb5ff6f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/ColorConversionBenchmark.cs @@ -38,11 +38,11 @@ private static Buffer2D[] CreateRandomValues( float minVal = 0f, float maxVal = 255f) { - var rnd = new Random(42); - var buffers = new Buffer2D[componentCount]; + Random rnd = new(42); + Buffer2D[] buffers = new Buffer2D[componentCount]; for (int i = 0; i < componentCount; i++) { - var values = new float[inputBufferLength]; + float[] values = new float[inputBufferLength]; for (int j = 0; j < inputBufferLength; j++) { @@ -51,6 +51,7 @@ private static Buffer2D[] CreateRandomValues( // no need to dispose when buffer is not array owner buffers[i] = Configuration.Default.MemoryAllocator.Allocate2D(values.Length, 1); + values.CopyTo(buffers[i].DangerousGetRowSpan(0)); } return buffers; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 8bf26d721d..9e5bfed12b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -6,35 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] -public class GrayscaleColorConversion : ColorConversionBenchmark +[Config(typeof(Config.Short))] +public class GrayScaleColorConversion : ColorConversionBenchmark { - public GrayscaleColorConversion() - : base(1) - { - } - - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + private readonly JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, 8); - new JpegColorConverterBase.GrayscaleScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() + /// + /// Initializes a new instance of the class. + /// + public GrayScaleColorConversion() + : base(1) { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); } + /// + /// Converts one grayscale component row through the adaptive operator traversal. + /// [Benchmark] - public void SimdVectorArm() + public void ConvertToRgb() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values); + this.converter.ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPacking.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPacking.cs new file mode 100644 index 0000000000..5c36ae59f1 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPacking.cs @@ -0,0 +1,178 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using SixLabors.ImageSharp.Formats.Jpeg.Components; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; + +/// +/// Compares the previous scalar JPEG packing loops with the SIMD register-transpose implementation. +/// +[Config(typeof(Config.Short))] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +[CategoriesColumn] +public class JpegColorPacking +{ + private const float MaximumValue = 255F; + private const float Scale = 1F / MaximumValue; + + private float[] x = null!; + private float[] y = null!; + private float[] z = null!; + private float[] w = null!; + private Vector3[] packed3 = null!; + private float[] destination3 = null!; + private float[] destination4 = null!; + + /// + /// Gets or sets the number of pixels transformed by each benchmark invocation. + /// + [Params(128, 1024, 4096)] + public int Length { get; set; } + + /// + /// Creates deterministic source and destination buffers outside the measured operations. + /// + [GlobalSetup] + public void Setup() + { + this.x = CreateSamples(this.Length, 1); + this.y = CreateSamples(this.Length, 2); + this.z = CreateSamples(this.Length, 3); + this.w = CreateSamples(this.Length, 4); + this.packed3 = new Vector3[this.Length]; + this.destination3 = new float[this.Length * 3]; + this.destination4 = new float[this.Length * 4]; + + for (int i = 0; i < this.packed3.Length; i++) + { + this.packed3[i] = new Vector3(this.x[i], this.y[i], this.z[i]); + } + } + + /// + /// Measures the previous scalar three-plane normalization and interleave loop. + /// + /// The last destination value, keeping the writes observable. + [Benchmark(Baseline = true)] + [BenchmarkCategory("Pack3")] + public float PackedNormalizeInterleave3Scalar() + { + JpegColorPackingScalar.PackedNormalizeInterleave3(this.x, this.y, this.z, this.destination3, Scale); + + return this.destination3[^1]; + } + + /// + /// Measures the SIMD three-plane normalization and interleave implementation. + /// + /// The last destination value, keeping the writes observable. + [Benchmark] + [BenchmarkCategory("Pack3")] + public float PackedNormalizeInterleave3Simd() + { + JpegColorConverterBase.PackedNormalizeInterleave3(this.x, this.y, this.z, this.destination3, Scale); + + return this.destination3[^1]; + } + + /// + /// Measures the previous scalar packed-three-channel deinterleave loop. + /// + /// A checksum containing the last value written to every destination plane. + [Benchmark(Baseline = true)] + [BenchmarkCategory("Unpack3")] + public float UnpackDeinterleave3Scalar() + { + JpegColorPackingScalar.UnpackDeinterleave3(this.packed3, this.x, this.y, this.z); + + return this.x[^1] + this.y[^1] + this.z[^1]; + } + + /// + /// Measures the SIMD packed-three-channel deinterleave implementation. + /// + /// A checksum containing the last value written to every destination plane. + [Benchmark] + [BenchmarkCategory("Unpack3")] + public float UnpackDeinterleave3Simd() + { + JpegColorConverterBase.UnpackDeinterleave3(this.packed3, this.x, this.y, this.z); + + return this.x[^1] + this.y[^1] + this.z[^1]; + } + + /// + /// Measures the previous scalar four-plane normalization and interleave loop. + /// + /// The last destination value, keeping the writes observable. + [Benchmark(Baseline = true)] + [BenchmarkCategory("Pack4")] + public float PackedNormalizeInterleave4Scalar() + { + JpegColorPackingScalar.PackedNormalizeInterleave4(this.x, this.y, this.z, this.w, this.destination4, MaximumValue); + + return this.destination4[^1]; + } + + /// + /// Measures the SIMD four-plane normalization and interleave implementation. + /// + /// The last destination value, keeping the writes observable. + [Benchmark] + [BenchmarkCategory("Pack4")] + public float PackedNormalizeInterleave4Simd() + { + JpegColorConverterBase.PackedNormalizeInterleave4(this.x, this.y, this.z, this.w, this.destination4, MaximumValue); + + return this.destination4[^1]; + } + + /// + /// Measures the previous scalar inverted four-plane normalization and interleave loop. + /// + /// The last destination value, keeping the writes observable. + [Benchmark(Baseline = true)] + [BenchmarkCategory("InvertPack4")] + public float PackedInvertNormalizeInterleave4Scalar() + { + JpegColorPackingScalar.PackedInvertNormalizeInterleave4(this.x, this.y, this.z, this.w, this.destination4, MaximumValue); + + return this.destination4[^1]; + } + + /// + /// Measures the SIMD inverted four-plane normalization and interleave implementation. + /// + /// The last destination value, keeping the writes observable. + [Benchmark] + [BenchmarkCategory("InvertPack4")] + public float PackedInvertNormalizeInterleave4Simd() + { + JpegColorConverterBase.PackedInvertNormalizeInterleave4(this.x, this.y, this.z, this.w, this.destination4, MaximumValue); + + return this.destination4[^1]; + } + + /// + /// Creates deterministic, non-integral samples for one component plane. + /// + /// The number of samples to create. + /// The one-based component number used to distinguish the plane. + /// The generated samples. + private static float[] CreateSamples(int length, int component) + { + float[] samples = new float[length]; + + for (int i = 0; i < samples.Length; i++) + { + samples[i] = (((i * 37) + (component * 53)) % 251) + (component * 0.125F); + } + + return samples; + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPackingScalar.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPackingScalar.cs new file mode 100644 index 0000000000..511abc0db8 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/JpegColorPackingScalar.cs @@ -0,0 +1,117 @@ +// 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.Benchmarks.Codecs.Jpeg; + +/// +/// Preserves the scalar JPEG packing loops that preceded the SIMD investigation. +/// +internal static class JpegColorPackingScalar +{ + /// + /// Normalizes and interleaves three planar component lanes using the previous scalar implementation. + /// + /// The planar X components. + /// The planar Y components. + /// The planar Z components. + /// The destination ordered as consecutive XYZ triples. + /// The normalization factor applied to every component. + public static void PackedNormalizeInterleave3(ReadOnlySpan xLane, ReadOnlySpan yLane, ReadOnlySpan zLane, Span packed, float scale) + { + ref float xLaneRef = ref MemoryMarshal.GetReference(xLane); + ref float yLaneRef = ref MemoryMarshal.GetReference(yLane); + ref float zLaneRef = ref MemoryMarshal.GetReference(zLane); + ref float packedRef = ref MemoryMarshal.GetReference(packed); + + for (nuint i = 0; i < (nuint)xLane.Length; i++) + { + nuint packedOffset = i * 3; + Unsafe.Add(ref packedRef, packedOffset) = Unsafe.Add(ref xLaneRef, i) * scale; + Unsafe.Add(ref packedRef, packedOffset + 1) = Unsafe.Add(ref yLaneRef, i) * scale; + Unsafe.Add(ref packedRef, packedOffset + 2) = Unsafe.Add(ref zLaneRef, i) * scale; + } + } + + /// + /// Deinterleaves packed XYZ values using the previous scalar implementation. + /// + /// The source ordered as consecutive XYZ triples. + /// The destination X components. + /// The destination Y components. + /// The destination Z components. + public static void UnpackDeinterleave3(ReadOnlySpan packed, Span xLane, Span yLane, Span zLane) + { + ref float packedRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(packed)); + ref float xLaneRef = ref MemoryMarshal.GetReference(xLane); + ref float yLaneRef = ref MemoryMarshal.GetReference(yLane); + ref float zLaneRef = ref MemoryMarshal.GetReference(zLane); + + for (nuint i = 0; i < (nuint)packed.Length; i++) + { + nuint packedOffset = i * 3; + Unsafe.Add(ref xLaneRef, i) = Unsafe.Add(ref packedRef, packedOffset); + Unsafe.Add(ref yLaneRef, i) = Unsafe.Add(ref packedRef, packedOffset + 1); + Unsafe.Add(ref zLaneRef, i) = Unsafe.Add(ref packedRef, packedOffset + 2); + } + } + + /// + /// Normalizes and interleaves four planar component lanes using the previous scalar implementation. + /// + /// The planar X components. + /// The planar Y components. + /// The planar Z components. + /// The planar W components. + /// The destination ordered as consecutive XYZW groups. + /// The maximum component value used to normalize each component. + public static void PackedNormalizeInterleave4(ReadOnlySpan xLane, ReadOnlySpan yLane, ReadOnlySpan zLane, ReadOnlySpan wLane, Span packed, float maximumValue) + { + float scale = 1F / maximumValue; + ref float xLaneRef = ref MemoryMarshal.GetReference(xLane); + ref float yLaneRef = ref MemoryMarshal.GetReference(yLane); + ref float zLaneRef = ref MemoryMarshal.GetReference(zLane); + ref float wLaneRef = ref MemoryMarshal.GetReference(wLane); + ref float packedRef = ref MemoryMarshal.GetReference(packed); + + for (nuint i = 0; i < (nuint)xLane.Length; i++) + { + nuint packedOffset = i * 4; + Unsafe.Add(ref packedRef, packedOffset) = Unsafe.Add(ref xLaneRef, i) * scale; + Unsafe.Add(ref packedRef, packedOffset + 1) = Unsafe.Add(ref yLaneRef, i) * scale; + Unsafe.Add(ref packedRef, packedOffset + 2) = Unsafe.Add(ref zLaneRef, i) * scale; + Unsafe.Add(ref packedRef, packedOffset + 3) = Unsafe.Add(ref wLaneRef, i) * scale; + } + } + + /// + /// Inverts, normalizes, and interleaves four planar lanes using the previous scalar implementation. + /// + /// The inverted planar X components. + /// The inverted planar Y components. + /// The inverted planar Z components. + /// The inverted planar W components. + /// The destination ordered as consecutive conventional XYZW groups. + /// The maximum component value used for inversion and normalization. + public static void PackedInvertNormalizeInterleave4(ReadOnlySpan xLane, ReadOnlySpan yLane, ReadOnlySpan zLane, ReadOnlySpan wLane, Span packed, float maximumValue) + { + float scale = 1F / maximumValue; + ref float xLaneRef = ref MemoryMarshal.GetReference(xLane); + ref float yLaneRef = ref MemoryMarshal.GetReference(yLane); + ref float zLaneRef = ref MemoryMarshal.GetReference(zLane); + ref float wLaneRef = ref MemoryMarshal.GetReference(wLane); + ref float packedRef = ref MemoryMarshal.GetReference(packed); + + for (nuint i = 0; i < (nuint)xLane.Length; i++) + { + nuint packedOffset = i * 4; + Unsafe.Add(ref packedRef, packedOffset) = (maximumValue - Unsafe.Add(ref xLaneRef, i)) * scale; + Unsafe.Add(ref packedRef, packedOffset + 1) = (maximumValue - Unsafe.Add(ref yLaneRef, i)) * scale; + Unsafe.Add(ref packedRef, packedOffset + 2) = (maximumValue - Unsafe.Add(ref zLaneRef, i)) * scale; + Unsafe.Add(ref packedRef, packedOffset + 3) = (maximumValue - Unsafe.Add(ref wLaneRef, i)) * scale; + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index ba09644219..b1d72e3ca2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -6,43 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class RgbColorConversion : ColorConversionBenchmark { + private readonly JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, 8); + + /// + /// Initializes a new instance of the class. + /// public RgbColorConversion() : base(3) { } - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); - } - + /// + /// Converts one RGB component row through the adaptive operator traversal. + /// [Benchmark] - public void SimdVectorArm() + public void ConvertToRgb() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.RgbArm(8).ConvertToRgbInplace(values); + this.converter.ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index 87e1bf5aa9..1355aecd19 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -6,43 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class YCbCrColorConversion : ColorConversionBenchmark { + private readonly JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 8); + + /// + /// Initializes a new instance of the class. + /// public YCbCrColorConversion() : base(3) { } + /// + /// Converts one YCbCr component row through the adaptive operator traversal. + /// [Benchmark] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorArm() + public void ConvertToRgb() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.YCbCrArm(8).ConvertToRgbInplace(values); + this.converter.ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index 136182936f..261711d0b7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -6,43 +6,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class YccKColorConverter : ColorConversionBenchmark { + private readonly JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Ycck, 8); + + /// + /// Initializes a new instance of the class. + /// public YccKColorConverter() : base(4) { } - [Benchmark(Baseline = true)] - public void Scalar() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKScalar(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVector8() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKVector(8).ConvertToRgbInplace(values); - } - - [Benchmark] - public void SimdVectorAvx2() - { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); - - new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); - } - + /// + /// Converts one YccK component row through the adaptive operator traversal. + /// [Benchmark] - public void SimdVectorArm64() + public void ConvertToRgb() { - var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + JpegColorConverterBase.ComponentValues values = new(this.Input, 0); - new JpegColorConverterBase.YccKArm64(8).ConvertToRgbInplace(values); + this.converter.ConvertToRgbInPlace(values); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index 0dc6d26bc7..dbd2557225 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; +[Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class DecodeJpeg { private JpegDecoder decoder; @@ -21,7 +22,7 @@ private void GenericSetup(string imageSubpath) this.preloadedImageStream = new MemoryStream(bytes); } - private void GenericBechmark() + private void GenericBenchmark() { this.preloadedImageStream.Position = 0; using Image img = this.decoder.Decode(DecoderOptions.Default, this.preloadedImageStream); @@ -51,16 +52,16 @@ public void Cleanup() } [Benchmark(Description = "Baseline 4:4:4 Interleaved")] - public void JpegBaselineInterleaved444() => this.GenericBechmark(); + public void JpegBaselineInterleaved444() => this.GenericBenchmark(); [Benchmark(Description = "Baseline 4:2:0 Interleaved")] - public void JpegBaselineInterleaved420() => this.GenericBechmark(); + public void JpegBaselineInterleaved420() => this.GenericBenchmark(); [Benchmark(Description = "Baseline 4:0:0 (grayscale)")] - public void JpegBaseline400() => this.GenericBechmark(); + public void JpegBaseline400() => this.GenericBenchmark(); [Benchmark(Description = "Progressive 4:2:0 Non-Interleaved")] - public void JpegProgressiveNonInterleaved420() => this.GenericBechmark(); + public void JpegProgressiveNonInterleaved420() => this.GenericBenchmark(); } /* diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index b5a7245292..e7b66576d1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -2,15 +2,17 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Tests; 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)] @@ -27,20 +29,20 @@ public void Setup() [Benchmark(Baseline = true, Description = "System.Drawing FULL")] public SDSize JpegSystemDrawing() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = System.Drawing.Image.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "JpegDecoderCore.ParseStream")] public void ParseStream() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); - var options = new JpegDecoderOptions() { GeneralOptions = new() { SkipMetadata = true } }; + using MemoryStream memoryStream = new(this.jpegBytes); + using BufferedReadStream bufferedStream = new(Configuration.Default, memoryStream); + JpegDecoderOptions options = new() { GeneralOptions = new DecoderOptions { SkipMetadata = true } }; - using var decoder = new JpegDecoderCore(options); - var spectralConverter = new NoopSpectralConverter(); + using JpegDecoderCore decoder = new(options); + NoopSpectralConverter spectralConverter = new(); decoder.ParseStream(bufferedStream, spectralConverter, cancellationToken: default); } @@ -48,9 +50,9 @@ public void ParseStream() // Nor we need to allocate final pixel buffer // Note: this still introduces virtual method call overhead for baseline interleaved images // There's no way to eliminate it as spectral conversion is built into the scan decoding loop for memory footprint reduction - private class NoopSpectralConverter : SpectralConverter + private sealed class NoopSpectralConverter : SpectralConverter { - public override void ConvertStrideBaseline() + public override void ConvertStrideBaseline(IccProfile iccProfile) { } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index bece1de5bd..9f69f613e8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -13,25 +13,25 @@ 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 - => new[] - { + => + [ TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, - }; + ]; [Params(InputImageCategory.AllImages)] public override InputImageCategory InputCategory { get; set; } [Benchmark] public void ImageSharp() - => this.ForEachStream(ms => Image.Load(ms)); + => this.ForEachStream(Image.Load); [Benchmark(Baseline = true)] public void SystemDrawing() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 035f800a9b..9a4ee39676 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; @@ -35,26 +35,21 @@ public class DecodeJpeg_ImageSpecific [GlobalSetup] public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath); [Benchmark(Baseline = true)] public SDSize SystemDrawing() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark] public Size ImageSharp() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using Image image = Image.Load(new DecoderOptions { SkipMetadata = true }, memoryStream); return new Size(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs index d762e8e95e..c7cecd1a52 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs @@ -19,11 +19,6 @@ public class EncodeJpegComparison { // Big enough, 4:4:4 chroma sampling private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - - // Change/add parameters for extra benchmarks - [Params(75, 90, 100)] - public int Quality; - private MemoryStream destinationStream; // ImageSharp @@ -33,13 +28,17 @@ public class EncodeJpegComparison // SkiaSharp private SKBitmap imageSkiaSharp; + // Change/add parameters for extra benchmarks + [Params(75, 90, 100)] + public int Quality { get; set; } + [GlobalSetup(Target = nameof(BenchmarkImageSharp))] public void SetupImageSharp() { 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 98eb0b54dc..858917995a 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[] - { - JpegEncodingColor.Luminance, - JpegEncodingColor.Rgb, - JpegEncodingColor.YCbCrRatio420, - JpegEncodingColor.YCbCrRatio444, - }; + public static IEnumerable ColorSpaceValues => + [ + JpegColorType.Luminance, + JpegColorType.Rgb, + JpegColorType.YCbCrRatio420, + JpegColorType.YCbCrRatio444, + ]; [Params(75, 90, 100)] - public int Quality; + public int Quality { get; set; } [ParamsSource(nameof(ColorSpaceValues), Priority = -100)] - public JpegEncodingColor TargetColorSpace; + public JpegColorType TargetColorSpace { get; set; } private Image bmpCore; private JpegEncoder encoder; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index d5ad59b00a..9cc61c741d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -8,7 +8,7 @@ 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/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index b75d012f97..0adc52441a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -11,11 +11,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; public abstract class MultiImageBenchmarkBase { - protected Dictionary FileNamesToBytes { get; set; } = new Dictionary(); + protected Dictionary FileNamesToBytes { get; set; } = []; - protected Dictionary> FileNamesToImageSharpImages { get; set; } = new Dictionary>(); + protected Dictionary> FileNamesToImageSharpImages { get; set; } = []; - protected Dictionary FileNamesToSystemDrawingImages { get; set; } = new Dictionary(); + protected Dictionary FileNamesToSystemDrawingImages { get; set; } = []; /// /// The values of this enum separate input files into categories. @@ -43,12 +43,12 @@ public enum InputImageCategory protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; - protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; + protected virtual IEnumerable SearchPatterns => ["*.*"]; /// /// Gets the file names containing these strings are substrings are not processed by the benchmark. /// - protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; + protected virtual IEnumerable ExcludeSubstringsInFileNames => ["badeof", "BadEof", "CriticalEOF"]; /// /// Gets folders containing files OR files to be processed by the benchmark. @@ -70,7 +70,7 @@ protected IEnumerable> EnumeratePairsByBenchmarkSettings InputImageCategory.AllImages => input, InputImageCategory.SmallImagesOnly => input.Where(kv => checkIfSmall(kv.Value)), InputImageCategory.LargeImagesOnly => input.Where(kv => !checkIfSmall(kv.Value)), - _ => throw new ArgumentOutOfRangeException(), + _ => throw new ArgumentOutOfRangeException(nameof(input), "Invalid input category") }; protected IEnumerable> FileNames2Bytes @@ -86,7 +86,7 @@ public virtual void Setup() { if (!Vector.IsHardwareAccelerated) { - throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!"); + throw new InvalidOperationException("Vector.IsHardwareAccelerated == false! Check your build settings!"); } // Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated); @@ -103,13 +103,13 @@ protected virtual void ReadFilesImpl() continue; } - string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray(); + string[] excludeStrings = this.ExcludeSubstringsInFileNames.ToArray(); string[] allFiles = this.SearchPatterns.SelectMany( f => Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) - .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray(); + .Where(fn => !excludeStrings.Any(excludeStr => fn.Contains(excludeStr, StringComparison.OrdinalIgnoreCase)))).ToArray(); foreach (string fn in allFiles) { @@ -126,7 +126,7 @@ protected void ForEachStream(Func operation) { foreach (KeyValuePair kv in this.FileNames2Bytes) { - using var memoryStream = new MemoryStream(kv.Value); + using MemoryStream memoryStream = new(kv.Value); try { object obj = operation(memoryStream); @@ -150,7 +150,7 @@ protected override void ReadFilesImpl() byte[] bytes = kv.Value; string fn = kv.Key; - using (var ms1 = new MemoryStream(bytes)) + using (MemoryStream ms1 = new(bytes)) { this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); } @@ -191,7 +191,7 @@ protected void ForEachImageSharpImage(Func, object> operation) protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) { - using var workStream = new MemoryStream(); + using MemoryStream workStream = new(); this.ForEachImageSharpImage( img => { @@ -222,7 +222,7 @@ protected void ForEachSystemDrawingImage(Func operation) protected void ForEachSystemDrawingImage(Func operation) { - using var workStream = new MemoryStream(); + using MemoryStream workStream = new(); this.ForEachSystemDrawingImage( img => { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs index 986c1431c9..57de8068f0 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs @@ -8,7 +8,7 @@ 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 c23fa25cca..2cf62fccf2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs @@ -9,7 +9,7 @@ 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 26fb0f4a41..69779731be 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs @@ -14,11 +14,11 @@ 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. - private Stream bmpStream; + private FileStream bmpStream; private Image bmpCore; [GlobalSetup] @@ -40,51 +40,51 @@ public void Cleanup() this.bmpCore.Dispose(); } - [Benchmark(Baseline = true, Description = "ImageSharp Octree Png")] - public void PngCoreOctree() + [Benchmark(Baseline = true, Description = "ImageSharp Hexadecatree Png")] + public void PngCoreHexadecatree() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Octree }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.Hexadecatree }; this.bmpCore.SaveAsPng(memoryStream, options); } - [Benchmark(Description = "ImageSharp Octree NoDither Png")] - public void PngCoreOctreeNoDither() + [Benchmark(Description = "ImageSharp Hexadecatree NoDither Png")] + public void PngCoreHexadecatreeNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new HexadecatreeQuantizer(new QuantizerOptions { Dither = null }) }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Palette Png")] public void PngCorePalette() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.WebSafe }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Palette NoDither Png")] public void PngCorePaletteNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Wu Png")] public void PngCoreWu() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Wu }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.Wu }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Wu NoDither Png")] public void PngCoreWuNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs index 5cbe88fee6..30a10af096 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs @@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodePng { // System.Drawing needs this. - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; @@ -46,15 +46,15 @@ public void Cleanup() [Benchmark(Baseline = true, Description = "System.Drawing Png")] public void PngSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Png); } [Benchmark(Description = "ImageSharp Png")] public void PngCore() { - using var memoryStream = new MemoryStream(); - var encoder = new PngEncoder { FilterMethod = PngFilterMethod.None }; + using MemoryStream memoryStream = new(); + PngEncoder encoder = new() { FilterMethod = PngFilterMethod.None }; this.bmpCore.SaveAsPng(memoryStream, encoder); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/PngFilterEncode.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/PngFilterEncode.cs new file mode 100644 index 0000000000..e5a4340675 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/PngFilterEncode.cs @@ -0,0 +1,99 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Png.Filters; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Png; + +/// +/// Measures the shared PNG filter map/reduce traversal. +/// +[Config(typeof(Config.Short))] +public class PngFilterEncode +{ + private const int BytesPerPixel = 4; + + private byte[] scanline; + private byte[] previousScanline; + private byte[] result; + + /// + /// Gets or sets the filter evaluated by each invocation. + /// + [Params(PngFilterMethod.Sub, PngFilterMethod.Up, PngFilterMethod.Average, PngFilterMethod.Paeth)] + public PngFilterMethod Filter { get; set; } + + /// + /// Gets or sets the number of scanline bytes. + /// + [Params(64, 1024, 16384)] + public int Count { get; set; } + + /// + /// Creates deterministic non-uniform inputs and a result buffer. + /// + [GlobalSetup] + public void Setup() + { + this.scanline = new byte[this.Count]; + this.previousScanline = new byte[this.Count]; + this.result = new byte[this.Count + 1]; + + Random random = new(12345678); + random.NextBytes(this.scanline); + random.NextBytes(this.previousScanline); + } + + /// + /// Executes the shared operator-driven map/reduce traversal. + /// + /// The filter variance sum. + [Benchmark] + public int Encode() + => this.Filter switch + { + PngFilterMethod.Sub => this.EncodeSub(), + PngFilterMethod.Up => this.EncodeUp(), + PngFilterMethod.Average => this.EncodeAverage(), + PngFilterMethod.Paeth => this.EncodePaeth(), + _ => throw new InvalidOperationException() + }; + + /// + /// Executes the current Sub encoder. + /// + private int EncodeSub() + { + SubFilter.Encode(this.scanline, this.result, BytesPerPixel, out int sum); + return sum; + } + + /// + /// Executes the current Up encoder. + /// + private int EncodeUp() + { + UpFilter.Encode(this.scanline, this.previousScanline, this.result, out int sum); + return sum; + } + + /// + /// Executes the current Average encoder. + /// + private int EncodeAverage() + { + AverageFilter.Encode(this.scanline, this.previousScanline, this.result, BytesPerPixel, out int sum); + return sum; + } + + /// + /// Executes the current Paeth encoder. + /// + private int EncodePaeth() + { + PaethFilter.Encode(this.scanline, this.previousScanline, this.result, BytesPerPixel, out int sum); + return sum; + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index 363ecf908b..4c81aee6d8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -10,7 +10,7 @@ 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); @@ -27,28 +27,28 @@ public void SetupData() => this.data = File.ReadAllBytes(this.TestImageFullPath); [Benchmark(Baseline = true, Description = "ImageMagick Tga")] - public int TgaImageMagick() + public uint TgaImageMagick() { - var settings = new MagickReadSettings { Format = MagickFormat.Tga }; - using var image = new MagickImage(new MemoryStream(this.data), settings); + MagickReadSettings settings = new() { Format = MagickFormat.Tga }; + using MagickImage image = new(new MemoryStream(this.data), settings); return image.Width; } [Benchmark(Description = "ImageSharp Tga")] public int TgaImageSharp() { - using var image = Image.Load(this.data); + using Image image = Image.Load(this.data); return image.Width; } [Benchmark(Description = "Pfim Tga")] public int TgaPfim() { - using var image = Targa.Create(this.data, this.pfimConfig); + using Targa image = Targa.Create(this.data, this.pfimConfig); return image.Width; } - private class PfimAllocator : IImageAllocator + private sealed class PfimAllocator : IImageAllocator { private int rented; private readonly ArrayPool shared = ArrayPool.Shared; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs index 935706b41b..169a44d91a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeTga { private MagickImage tgaMagick; @@ -41,14 +41,14 @@ public void Cleanup() [Benchmark(Baseline = true, Description = "Magick Tga")] public void MagickTga() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.tgaMagick.Write(memoryStream, MagickFormat.Tga); } [Benchmark(Description = "ImageSharp Tga")] public void ImageSharpTga() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.tga.SaveAsTga(memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index 83f5fdd213..ecb87e16c5 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 ab3b0e95ea..6fa6a15ed4 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -14,10 +14,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeTiff { - private Stream stream; + private FileStream stream; private SDImage drawing; private Image core; @@ -60,12 +60,12 @@ public void Cleanup() public void SystemDrawing() { ImageCodecInfo codec = FindCodecForType("image/tiff"); - using var parameters = new EncoderParameters(1) + using EncoderParameters parameters = new(1) { Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) } }; - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.drawing.Save(memoryStream, codec, parameters); } @@ -77,8 +77,8 @@ public void TiffCore() TiffPhotometricInterpretation.WhiteIsZero : TiffPhotometricInterpretation.Rgb; - var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; - using var memoryStream = new MemoryStream(); + TiffEncoder encoder = new() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; + using MemoryStream memoryStream = new(); this.core.SaveAsTiff(memoryStream, encoder); } @@ -98,33 +98,15 @@ private static ImageCodecInfo FindCodecForType(string mimeType) } private static EncoderValue Cast(TiffCompression compression) - { - switch (compression) + => compression switch { - case TiffCompression.None: - return EncoderValue.CompressionNone; - - case TiffCompression.CcittGroup3Fax: - return EncoderValue.CompressionCCITT3; - - case TiffCompression.Ccitt1D: - return EncoderValue.CompressionRle; - - case TiffCompression.Lzw: - return EncoderValue.CompressionLZW; - - default: - throw new NotSupportedException(compression.ToString()); - } - } + TiffCompression.None => EncoderValue.CompressionNone, + TiffCompression.CcittGroup3Fax => EncoderValue.CompressionCCITT3, + TiffCompression.Ccitt1D => EncoderValue.CompressionRle, + TiffCompression.Lzw => EncoderValue.CompressionLZW, + _ => throw new NotSupportedException(compression.ToString()), + }; public static bool IsOneBitCompression(TiffCompression compression) - { - if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) - { - return true; - } - - return false; - } + => compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs index 34a4ad5931..a10f1527f1 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; @@ -42,36 +42,37 @@ public void ReadImages() } [Benchmark(Description = "Magick Lossy Webp")] - public int WebpLossyMagick() + public uint WebpLossyMagick() { - var settings = new MagickReadSettings { Format = MagickFormat.WebP }; - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = new MagickImage(memoryStream, settings); + MagickReadSettings settings = new() { Format = MagickFormat.WebP }; + using MemoryStream memoryStream = new(this.webpLossyBytes); + using MagickImage image = new(memoryStream, settings); return image.Width; } [Benchmark(Description = "ImageSharp Lossy Webp")] public int WebpLossy() { - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.webpLossyBytes); + using Image image = Image.Load(memoryStream); return image.Height; } [Benchmark(Description = "Magick Lossless Webp")] - public int WebpLosslessMagick() + public uint WebpLosslessMagick() { - var settings = new MagickReadSettings { Format = MagickFormat.WebP }; - using var memoryStream = new MemoryStream(this.webpLossyBytes); - using var image = new MagickImage(memoryStream, settings); + MagickReadSettings settings = new() + { Format = MagickFormat.WebP }; + using MemoryStream memoryStream = new(this.webpLossyBytes); + using MagickImage image = new(memoryStream, settings); return image.Width; } [Benchmark(Description = "ImageSharp Lossless Webp")] public int WebpLossless() { - using var memoryStream = new MemoryStream(this.webpLosslessBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.webpLosslessBytes); + using Image image = Image.Load(memoryStream); return image.Height; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs index c5fa8d03da..3b90584988 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Attributes; using ImageMagick; using ImageMagick.Formats; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -12,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class EncodeWebp { private MagickImage webpMagick; @@ -52,10 +53,7 @@ public void MagickWebpLossy() AlphaCompression = WebPAlphaCompression.None, FilterStrength = 60, SnsStrength = 50, - Pass = 1, - - // 100 means off. - NearLossless = 100 + Pass = 1 }; this.webpMagick.Quality = 75; @@ -85,9 +83,6 @@ public void MagickWebpLossless() { Lossless = true, Method = 4, - - // 100 means off. - NearLossless = 100 }; this.webpMagick.Quality = 75; @@ -106,7 +101,7 @@ public void ImageSharpWebpLossless() Quality = 75, // This is equal to exact = false in libwebp, which is the default. - TransparentColorMode = WebpTransparentColorMode.Clear + TransparentColorMode = TransparentColorMode.Clear }); } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 1ff8013b28..5166c89a93 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 da09a85232..1f8a6b1933 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 c3317c5d94..b11e788193 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 ba213e5b0a..a2c7966d41 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 d7a5deafa7..b63f925046 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 48b4880d94..b847e3ac54 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 { SourceRgbWorkingSpace = 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 14d848bcb7..093397ad5f 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 Vector3 YcbCrToRgba() [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; diff --git a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs index 5a0c574f17..9fd48301ea 100644 --- a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs +++ b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs @@ -33,30 +33,31 @@ public partial class Config // `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things // like `LZCNT`, `BMI1`, or `BMI2` // `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3` - private const string EnableAES = "COMPlus_EnableAES"; - private const string EnableAVX = "COMPlus_EnableAVX"; - private const string EnableAVX2 = "COMPlus_EnableAVX2"; - private const string EnableBMI1 = "COMPlus_EnableBMI1"; - private const string EnableBMI2 = "COMPlus_EnableBMI2"; - private const string EnableFMA = "COMPlus_EnableFMA"; - private const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic"; - private const string EnableLZCNT = "COMPlus_EnableLZCNT"; - private const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ"; - private const string EnablePOPCNT = "COMPlus_EnablePOPCNT"; - private const string EnableSSE = "COMPlus_EnableSSE"; - private const string EnableSSE2 = "COMPlus_EnableSSE2"; - private const string EnableSSE3 = "COMPlus_EnableSSE3"; - private const string EnableSSE3_4 = "COMPlus_EnableSSE3_4"; - private const string EnableSSE41 = "COMPlus_EnableSSE41"; - private const string EnableSSE42 = "COMPlus_EnableSSE42"; - private const string EnableSSSE3 = "COMPlus_EnableSSSE3"; - private const string FeatureSIMD = "COMPlus_FeatureSIMD"; + private const string EnableAES = "DOTNET_EnableAES"; + private const string EnableAVX512F = "DOTNET_EnableAVX512F"; + private const string EnableAVX = "DOTNET_EnableAVX"; + private const string EnableAVX2 = "DOTNET_EnableAVX2"; + private const string EnableBMI1 = "DOTNET_EnableBMI1"; + private const string EnableBMI2 = "DOTNET_EnableBMI2"; + private const string EnableFMA = "DOTNET_EnableFMA"; + private const string EnableHWIntrinsic = "DOTNET_EnableHWIntrinsic"; + private const string EnableLZCNT = "DOTNET_EnableLZCNT"; + private const string EnablePCLMULQDQ = "DOTNET_EnablePCLMULQDQ"; + private const string EnablePOPCNT = "DOTNET_EnablePOPCNT"; + private const string EnableSSE = "DOTNET_EnableSSE"; + private const string EnableSSE2 = "DOTNET_EnableSSE2"; + private const string EnableSSE3 = "DOTNET_EnableSSE3"; + private const string EnableSSE3_4 = "DOTNET_EnableSSE3_4"; + private const string EnableSSE41 = "DOTNET_EnableSSE41"; + private const string EnableSSE42 = "DOTNET_EnableSSE42"; + private const string EnableSSSE3 = "DOTNET_EnableSSSE3"; + private const string FeatureSIMD = "DOTNET_FeatureSIMD"; public class HwIntrinsics_SSE_AVX : 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,16 +65,48 @@ public HwIntrinsics_SSE_AVX() 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")); } } } + + public class HwIntrinsics_SSE_AVX_AVX512F : Config + { + public HwIntrinsics_SSE_AVX_AVX512F() + { + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) + .WithEnvironmentVariables( + new EnvironmentVariable(EnableHWIntrinsic, Off), + new EnvironmentVariable(FeatureSIMD, Off)) + .WithId("1. No HwIntrinsics").AsBaseline()); + + if (Sse.IsSupported) + { + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) + .WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off)) + .WithId("2. SSE")); + } + + if (Avx.IsSupported) + { + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) + .WithEnvironmentVariables(new EnvironmentVariable(EnableAVX512F, Off)) + .WithId("3. AVX")); + } + + if (Avx512F.IsSupported) + { + this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) + .WithId("3. AVX512F")); + } + } + } } diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index d95f4a202c..0c46de4707 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -8,8 +8,10 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters.Json; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Toolchains.InProcess.Emit; namespace SixLabors.ImageSharp.Benchmarks; @@ -29,22 +31,57 @@ public Config() 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 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 Short() => this.AddJob( + Job.Default.WithRuntime(CoreRuntime.Core80) + .WithLaunchCount(1) + .WithWarmupCount(3) + .WithIterationCount(3) + .WithArguments([new MsBuildArgument("/p:DebugType=portable")])); } - public class ShortCore31 : Config + /// + /// Configures a short diagnostic run that exports memory usage, generated code, and full measurement data. + /// Elevated Windows runs also include branch-instruction counters. + /// + public class Analysis : Config { - public ShortCore31() - => this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); + public Analysis() + { + this.AddJob(Job.ShortRun.WithRuntime(CoreRuntime.Core80).WithArguments([new MsBuildArgument("/p:DebugType=portable")])); + + this.AddDiagnoser(new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig( + maxDepth: 3, + printSource: true, + exportGithubMarkdown: true, + exportCombinedDisassemblyReport: true))); + + this.AddExporter(JsonExporter.Full); + +#if OS_WINDOWS + if (this.IsElevated) + { + // ETW hardware counters require elevation, so ordinary benchmark runs omit them instead of requesting more access. + this.AddHardwareCounters(HardwareCounter.BranchMispredictions, HardwareCounter.BranchInstructions); + } +#endif + } + } + + public class StandardInProcess : Config + { + public StandardInProcess() => this.AddJob( + Job.Default + .WithRuntime(CoreRuntime.Core80) + .WithToolchain(InProcessEmitToolchain.Instance) + .WithArguments([new MsBuildArgument("/p:DebugType=portable")])); } #if OS_WINDOWS diff --git a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs index 1aac3cff50..64a8092c63 100644 --- a/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs +++ b/tests/ImageSharp.Benchmarks/General/Adler32Benchmark.cs @@ -7,11 +7,11 @@ namespace SixLabors.ImageSharp.Benchmarks.General; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class Adler32Benchmark { private byte[] data; - private readonly SharpAdler32 adler = new SharpAdler32(); + private readonly SharpAdler32 adler = new(); [Params(1024, 2048, 4096)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs index dd0bc28785..317295144f 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -10,7 +10,7 @@ public class ClampFloat { private readonly float min = -1.5f; private readonly float max = 2.5f; - private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + private static readonly float[] Values = [-10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10]; [Benchmark(Baseline = true)] public float UsingMathF() diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs index d47fcb687e..b61ba27ffb 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampSpan.cs @@ -12,7 +12,7 @@ public class ClampSpan public void Setup() { - var r = new Random(); + Random r = new(); for (int i = 0; i < A.Length; i++) { diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs index 186f88bb71..4969c3ef7a 100644 --- a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampVector4.cs @@ -11,7 +11,7 @@ public class ClampVector4 { private readonly float min = -1.5f; private readonly float max = 2.5f; - private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + private static readonly float[] Values = [-10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10]; [Benchmark(Baseline = true)] public Vector4 UsingVectorClamp() diff --git a/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs b/tests/ImageSharp.Benchmarks/General/CopyBuffers.cs index 3929f7c5ac..031f9ecf27 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 fdc9e26b60..0000000000 --- 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/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs index a6aac20c3b..41e6bdec27 100644 --- a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; @@ -21,7 +22,8 @@ public System.Drawing.Color GetSetSystemDrawing() public Rgba32 GetSetImageSharp() { using Image image = new(400, 400); - image[200, 200] = Color.White; + image[200, 200] = Color.White.ToPixel(); return image[200, 200]; } } +#endif diff --git a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs index 2a926d1cd8..d32e1fdd0d 100644 --- a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Benchmarks.IO; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class BufferedStreams { private readonly byte[] buffer = CreateTestBytes(); @@ -78,7 +78,7 @@ public void DestroyStreams() public int StandardStreamRead() { int r = 0; - Stream stream = this.stream1; + MemoryStream stream = this.stream1; byte[] b = this.chunk1; for (int i = 0; i < stream.Length / 2; i++) @@ -138,7 +138,7 @@ public int BufferedReadStreamWrapRead() public int StandardStreamReadByte() { int r = 0; - Stream stream = this.stream2; + MemoryStream stream = this.stream2; for (int i = 0; i < stream.Length; i++) { @@ -205,8 +205,8 @@ public int ArrayReadByte() private static byte[] CreateTestBytes() { - var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; - var random = new Random(); + byte[] buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; + Random random = new(); random.NextBytes(buffer); return buffer; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs index 8820406af6..82f0da5052 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/PackedPixelConversion.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PackedPixelConversion.cs new file mode 100644 index 0000000000..a7dcb90219 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PackedPixelConversion.cs @@ -0,0 +1,222 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +/// +/// Measures every optimized conversion between the six byte-packed RGB and RGBA pixel layouts. +/// +[Config(typeof(Config.Short))] +public class PackedPixelConversion +{ + private byte[] source3; + private byte[] source4; + private byte[] destination3; + private byte[] destination4; + + /// + /// Gets or sets the number of pixels converted by each invocation. + /// + [Params(7, 256, 4096)] + public int Count { get; set; } + + /// + /// Creates deterministic source buffers and correctly sized destination buffers. + /// + [GlobalSetup] + public void Setup() + { + this.source3 = new byte[this.Count * 3]; + this.source4 = new byte[this.Count * 4]; + this.destination3 = new byte[this.Count * 3]; + this.destination4 = new byte[this.Count * 4]; + + // Non-repeating channel values prevent the JIT or hardware from benefiting from + // zero-filled inputs while keeping both benchmark revisions byte-for-byte identical. + new Random(42).NextBytes(this.source3); + new Random(42).NextBytes(this.source4); + } + + /// + /// Converts RGBA pixels to ARGB pixels. + /// + [Benchmark] + public void Rgba32ToArgb32() => PixelConverter.FromRgba32.ToArgb32(this.source4, this.destination4); + + /// + /// Converts RGBA pixels to ABGR pixels. + /// + [Benchmark] + public void Rgba32ToAbgr32() => PixelConverter.FromRgba32.ToAbgr32(this.source4, this.destination4); + + /// + /// Converts RGBA pixels to BGRA pixels. + /// + [Benchmark] + public void Rgba32ToBgra32() => PixelConverter.FromRgba32.ToBgra32(this.source4, this.destination4); + + /// + /// Converts RGBA pixels to RGB pixels. + /// + [Benchmark] + public void Rgba32ToRgb24() => PixelConverter.FromRgba32.ToRgb24(this.source4, this.destination3); + + /// + /// Converts RGBA pixels to BGR pixels. + /// + [Benchmark] + public void Rgba32ToBgr24() => PixelConverter.FromRgba32.ToBgr24(this.source4, this.destination3); + + /// + /// Converts ARGB pixels to RGBA pixels. + /// + [Benchmark] + public void Argb32ToRgba32() => PixelConverter.FromArgb32.ToRgba32(this.source4, this.destination4); + + /// + /// Converts ARGB pixels to ABGR pixels. + /// + [Benchmark] + public void Argb32ToAbgr32() => PixelConverter.FromArgb32.ToAbgr32(this.source4, this.destination4); + + /// + /// Converts ARGB pixels to BGRA pixels. + /// + [Benchmark] + public void Argb32ToBgra32() => PixelConverter.FromArgb32.ToBgra32(this.source4, this.destination4); + + /// + /// Converts ARGB pixels to RGB pixels. + /// + [Benchmark] + public void Argb32ToRgb24() => PixelConverter.FromArgb32.ToRgb24(this.source4, this.destination3); + + /// + /// Converts ARGB pixels to BGR pixels. + /// + [Benchmark] + public void Argb32ToBgr24() => PixelConverter.FromArgb32.ToBgr24(this.source4, this.destination3); + + /// + /// Converts ABGR pixels to RGBA pixels. + /// + [Benchmark] + public void Abgr32ToRgba32() => PixelConverter.FromAbgr32.ToRgba32(this.source4, this.destination4); + + /// + /// Converts ABGR pixels to ARGB pixels. + /// + [Benchmark] + public void Abgr32ToArgb32() => PixelConverter.FromAbgr32.ToArgb32(this.source4, this.destination4); + + /// + /// Converts ABGR pixels to BGRA pixels. + /// + [Benchmark] + public void Abgr32ToBgra32() => PixelConverter.FromAbgr32.ToBgra32(this.source4, this.destination4); + + /// + /// Converts ABGR pixels to RGB pixels. + /// + [Benchmark] + public void Abgr32ToRgb24() => PixelConverter.FromAbgr32.ToRgb24(this.source4, this.destination3); + + /// + /// Converts ABGR pixels to BGR pixels. + /// + [Benchmark] + public void Abgr32ToBgr24() => PixelConverter.FromAbgr32.ToBgr24(this.source4, this.destination3); + + /// + /// Converts BGRA pixels to RGBA pixels. + /// + [Benchmark] + public void Bgra32ToRgba32() => PixelConverter.FromBgra32.ToRgba32(this.source4, this.destination4); + + /// + /// Converts BGRA pixels to ARGB pixels. + /// + [Benchmark] + public void Bgra32ToArgb32() => PixelConverter.FromBgra32.ToArgb32(this.source4, this.destination4); + + /// + /// Converts BGRA pixels to ABGR pixels. + /// + [Benchmark] + public void Bgra32ToAbgr32() => PixelConverter.FromBgra32.ToAbgr32(this.source4, this.destination4); + + /// + /// Converts BGRA pixels to RGB pixels. + /// + [Benchmark] + public void Bgra32ToRgb24() => PixelConverter.FromBgra32.ToRgb24(this.source4, this.destination3); + + /// + /// Converts BGRA pixels to BGR pixels. + /// + [Benchmark] + public void Bgra32ToBgr24() => PixelConverter.FromBgra32.ToBgr24(this.source4, this.destination3); + + /// + /// Converts RGB pixels to RGBA pixels. + /// + [Benchmark] + public void Rgb24ToRgba32() => PixelConverter.FromRgb24.ToRgba32(this.source3, this.destination4); + + /// + /// Converts RGB pixels to ARGB pixels. + /// + [Benchmark] + public void Rgb24ToArgb32() => PixelConverter.FromRgb24.ToArgb32(this.source3, this.destination4); + + /// + /// Converts RGB pixels to ABGR pixels. + /// + [Benchmark] + public void Rgb24ToAbgr32() => PixelConverter.FromRgb24.ToAbgr32(this.source3, this.destination4); + + /// + /// Converts RGB pixels to BGRA pixels. + /// + [Benchmark] + public void Rgb24ToBgra32() => PixelConverter.FromRgb24.ToBgra32(this.source3, this.destination4); + + /// + /// Converts RGB pixels to BGR pixels. + /// + [Benchmark] + public void Rgb24ToBgr24() => PixelConverter.FromRgb24.ToBgr24(this.source3, this.destination3); + + /// + /// Converts BGR pixels to RGBA pixels. + /// + [Benchmark] + public void Bgr24ToRgba32() => PixelConverter.FromBgr24.ToRgba32(this.source3, this.destination4); + + /// + /// Converts BGR pixels to ARGB pixels. + /// + [Benchmark] + public void Bgr24ToArgb32() => PixelConverter.FromBgr24.ToArgb32(this.source3, this.destination4); + + /// + /// Converts BGR pixels to ABGR pixels. + /// + [Benchmark] + public void Bgr24ToAbgr32() => PixelConverter.FromBgr24.ToAbgr32(this.source3, this.destination4); + + /// + /// Converts BGR pixels to BGRA pixels. + /// + [Benchmark] + public void Bgr24ToBgra32() => PixelConverter.FromBgr24.ToBgra32(this.source3, this.destination4); + + /// + /// Converts BGR pixels to RGB pixels. + /// + [Benchmark] + public void Bgr24ToRgb24() => PixelConverter.FromBgr24.ToRgb24(this.source3, this.destination3); +} diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 8d16849825..c864e26c61 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 void RunByRefConversion() } [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 void RunByValConversion() } [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 void RunFromBytesConversion() internal ConversionRunner PermutedRunnerRgbaToArgb; + internal ConversionRunner RunnerRgbaToRgbaVector; + [Params(256, 2048)] public int Count { get; set; } @@ -82,34 +98,29 @@ public void Setup() { 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++) { @@ -117,39 +128,49 @@ public void Inline() } } - /* 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.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++) { @@ -163,29 +184,70 @@ public void InlineShuffle() } } + // Commenting this out because for some reason MSBuild is showing error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.Span' + // when trying to build via BenchmarkDotnet. (╯‵□′)╯︵┻━┻ + // [Benchmark] + // public void PixelConverter_Rgba32_ToArgb32() + // { + // ReadOnlySpan source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); + // Span destination = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Destination); + // + // PixelConverter.FromRgba32.ToArgb32(source, destination); + // } + + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 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 PixelConverter_Rgba32_ToArgb32() - { - Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); - Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Dest); + public void ByVal() => this.RunnerRgbaToRgbaVector.RunByValConversion(); - PixelConverter.FromRgba32.ToArgb32(source, dest); - } + [Benchmark] + public void StaticByVal() => this.RunnerRgbaToRgbaVector.RunStaticByValConversion(); /* - 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 | 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 dd85d06417..57f79ba1f3 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/PixelConversion_PackFromRgbPlanes.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs index a42c6c253c..226dcc7775 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs @@ -209,7 +209,7 @@ public void Rgba32_Avx2_Float() Vector256 vcontrol = SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32().AsInt32(); - var va = Vector256.Create(1F); + Vector256 va = Vector256.Create(1F); for (nuint i = 0; i < count; i++) { 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 0ee507832a..a8c6a021ae 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 @@ private static void Default_GenericImpl(ReadOnlySpan source, Spa 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 void Default_Group2() 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 void Default_Group2() 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 void Default_Group4() 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 void Default_Group4() 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 void BitOps() 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 void BitOps_GroupAsULong() 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 static uint ToArgb32(uint packedRgba) 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 499f3483dc..c69510bc4d 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -7,7 +7,6 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tuples; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -55,7 +54,7 @@ public void Default() 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 +68,12 @@ private static void Default_GenericImpl(ReadOnlySpan source, Spa 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 +87,8 @@ public void Default_Group2() 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 +109,10 @@ public void Default_Group4() 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 +134,15 @@ private static void Group4GenericImpl(ReadOnlySpan source, Span< 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 +171,15 @@ public void Default_Group8() 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); } } @@ -231,8 +224,8 @@ public void Bitops_SingleTuple() // [Benchmark] public void Bitops_Simd() { - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + ref InlineArray8 sBase = ref Unsafe.As>(ref this.source[0]); + ref InlineArray8 dBase = ref Unsafe.As>(ref this.dest[0]); for (nuint i = 0; i < (uint)this.Count / 8; i++) { @@ -255,11 +248,11 @@ private struct C #pragma warning restore SA1132 // Do not combine fields [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void BitopsSimdImpl(ref Octet s, ref Octet d) + private static void BitopsSimdImpl(ref InlineArray8 s, ref InlineArray8 d) { - Vector sVec = Unsafe.As, Vector>(ref s); - var aMask = new Vector(0xFF00FF00); - var bMask = new Vector(0x00FF00FF); + Vector sVec = Unsafe.As, Vector>(ref s); + Vector aMask = new(0xFF00FF00); + Vector bMask = new(0x00FF00FF); Vector aa = sVec & aMask; Vector bb = sVec & bMask; @@ -280,7 +273,7 @@ private static void BitopsSimdImpl(ref Octet s, ref Octet d) Vector cc = Unsafe.As>(ref c); Vector dd = aa + cc; - d = Unsafe.As, Octet>(ref dd); + d = Unsafe.As, InlineArray8>(ref dd); } // [Benchmark] @@ -352,10 +345,6 @@ public void BitOps_GroupAsULong_V2() public static class FromRgba32 { - /// - /// Converts a packed to . - /// - /// The argb value. [MethodImpl(InliningOptions.ShortMethod)] public static uint ToArgb32(uint packedRgba) { @@ -364,10 +353,6 @@ public static uint ToArgb32(uint packedRgba) 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/TestArgb.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs index 84698a0e19..8698f82231 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 void FromBytes(byte r, byte g, byte b, byte a) } [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 TestArgb(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 76449c9d95..751cd68b48 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 void FromBytes(byte r, byte g, byte b, byte a) 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 TestRgba(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 0000000000..07790ae998 --- /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); +} diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/Vector4AffineTransform.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/Vector4AffineTransform.cs new file mode 100644 index 0000000000..d671117b66 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/Vector4AffineTransform.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +/// +/// Measures the operator-driven affine vector transforms. +/// +[Config(typeof(Config.Short))] +public class Vector4AffineTransform +{ + private static readonly Vector4 Multiplier = new(255F, 2F, 65535F, .5F); + private static readonly Vector4 Offset = new(17F, -1F, 32768F, 3F); + private static readonly Vector4 Divisor = new(255F, 2F, 65535F, .5F); + + private Vector4[] current; + + /// + /// Gets or sets the number of vectors transformed by each invocation. + /// + [Params(1, 3, 4, 17, 256, 4096)] + public int Count { get; set; } + + /// + /// Creates a non-uniform input buffer. + /// + [GlobalSetup] + public void Setup() + { + this.current = new Vector4[this.Count]; + + for (int i = 0; i < this.current.Length; i++) + { + this.current[i] = new Vector4(i + .25F, i + .5F, i + .75F, i + 1F); + } + } + + /// + /// Executes the operator-driven multiply-then-add traversal. + /// + [Benchmark] + public void MultiplyThenAdd() + => Vector4Converters.MultiplyThenAdd(this.current, Multiplier, Offset); + + /// + /// Executes the operator-driven add-then-divide traversal. + /// + [Benchmark] + public void AddThenDivide() + => Vector4Converters.AddThenDivide(this.current, Offset, Divisor); +} diff --git a/tests/ImageSharp.Benchmarks/General/StructCasting.cs b/tests/ImageSharp.Benchmarks/General/StructCasting.cs index f8432112e3..3f3767d429 100644 --- a/tests/ImageSharp.Benchmarks/General/StructCasting.cs +++ b/tests/ImageSharp.Benchmarks/General/StructCasting.cs @@ -11,7 +11,7 @@ public class StructCasting [Benchmark(Baseline = true)] public short ExplicitCast() { - int x = 5 * 2; + const int x = 5 * 2; return (short)x; } @@ -25,6 +25,7 @@ public short UnsafeCast() [Benchmark] public short UnsafeCastRef() { - return Unsafe.As(ref Unsafe.AsRef(5 * 2)); + int x = 5 * 2; + return Unsafe.As(ref Unsafe.AsRef(ref x)); } } diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index 2cd6a5a52a..5919137bf2 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -12,10 +12,10 @@ namespace SixLabors.ImageSharp.Benchmarks.General; /// public class Vector4Constants { - private static readonly Vector4 A = new Vector4(1.2f); - private static readonly Vector4 B = new Vector4(3.4f); - private static readonly Vector4 C = new Vector4(5.6f); - private static readonly Vector4 D = new Vector4(7.8f); + private static readonly Vector4 A = new(1.2f); + private static readonly Vector4 B = new(3.4f); + private static readonly Vector4 C = new(5.6f); + private static readonly Vector4 D = new(7.8f); private Random random; @@ -39,8 +39,8 @@ public Vector4 Static() Vector4 x = (p * A / B) + (p * C / D); Vector4 y = (p / A * B) + (p / C * D); - var z = Vector4.Min(p, A); - var w = Vector4.Max(p, B); + Vector4 z = Vector4.Min(p, A); + Vector4 w = Vector4.Max(p, B); return x + y + z + w; } @@ -51,8 +51,8 @@ public Vector4 Inlined() Vector4 x = (p * new Vector4(1.2f) / new Vector4(2.3f)) + (p * new Vector4(4.5f) / new Vector4(6.7f)); Vector4 y = (p / new Vector4(1.2f) * new Vector4(2.3f)) + (p / new Vector4(4.5f) * new Vector4(6.7f)); - var z = Vector4.Min(p, new Vector4(1.2f)); - var w = Vector4.Max(p, new Vector4(2.3f)); + Vector4 z = Vector4.Min(p, new Vector4(1.2f)); + Vector4 w = Vector4.Max(p, new Vector4(2.3f)); return x + y + z + w; } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index 4d8d9b1ed6..2f9b02b59f 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -43,11 +43,11 @@ public void Standard() [Benchmark] public void Simd() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = Vector.BitwiseOr(a, v); a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index 1b2c56ab97..fd243638b2 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -43,11 +43,11 @@ public void Standard() [Benchmark] public void Simd() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = a / v; a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index d102164e2c..0d2923b619 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -44,11 +44,11 @@ public void Standard() [Benchmark] public void Simd() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = a / v; a.CopyTo(this.result, i); diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs index 9c95c22e0f..61ff9466ee 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -15,10 +15,10 @@ public class DivFloat : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - float v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + float v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -30,10 +30,10 @@ public class Divide : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + uint v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -45,10 +45,10 @@ public class DivInt32 : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + int v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -57,15 +57,15 @@ public class DivInt16 : SIMDBenchmarkBase.Divide { protected override short GetTestValue() => 42; - protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + protected override Vector GetTestVector() => new(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); [Benchmark(Baseline = true)] public void Standard() { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + short v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = (short)(this.input[i] / v); + this.Result[i] = (short)(this.Input[i] / v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index a2eb8d417a..801b0e1613 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -43,11 +43,11 @@ public void Standard() [Benchmark] public void SimdMultiplyByVector() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = a * v; a.CopyTo(this.result, i); } @@ -60,7 +60,7 @@ public void SimdMultiplyByScalar() for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = a * v; a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index a234970a51..db64486ada 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -43,11 +43,11 @@ public void Standard() [Benchmark] public void Simd() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); + Vector a = new(this.input, i); a = a * v; a.CopyTo(this.result, i); } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs index fe48c3301b..d9542bc3f7 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -15,10 +15,10 @@ public class MulUInt32 : SIMDBenchmarkBase.Multiply [Benchmark(Baseline = true)] public void Standard() { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + uint v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] * v; + this.Result[i] = this.Input[i] * v; } } } @@ -28,10 +28,10 @@ public class MulInt32 : SIMDBenchmarkBase.Multiply [Benchmark(Baseline = true)] public void Standard() { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + int v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] * v; + this.Result[i] = this.Input[i] * v; } } } @@ -40,15 +40,15 @@ public class MulInt16 : SIMDBenchmarkBase.Multiply { protected override short GetTestValue() => 42; - protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + protected override Vector GetTestVector() => new(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); [Benchmark(Baseline = true)] public void Standard() { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + short v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = (short)(this.input[i] * v); + this.Result[i] = (short)(this.Input[i] * v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs index 29b90accc5..f99b141b4e 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs @@ -12,14 +12,14 @@ public class Premultiply [Benchmark(Baseline = true)] public Vector4 PremultiplyByVal() { - var input = new Vector4(.5F); + Vector4 input = new(.5F); return Vector4Utils.Premultiply(input); } [Benchmark] public Vector4 PremultiplyByRef() { - var input = new Vector4(.5F); + Vector4 input = new(.5F); Vector4Utils.PremultiplyRef(ref input); return input; } @@ -27,7 +27,7 @@ public Vector4 PremultiplyByRef() [Benchmark] public Vector4 PremultiplyRefWithPropertyAssign() { - var input = new Vector4(.5F); + Vector4 input = new(.5F); Vector4Utils.PremultiplyRefWithPropertyAssign(ref input); return input; } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index 7d626d7856..557d8ff38b 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -53,8 +53,8 @@ public void Simd() { for (int i = 0; i < this.input.Length; i += Vector.Count) { - var a = new Vector(this.input, i); - var b = Vector.AsVectorSingle(a); + Vector a = new(this.input, i); + Vector b = Vector.AsVectorSingle(a); b.CopyTo(this.result, i); } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs index 90d81a0583..0d856df615 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -10,28 +10,28 @@ namespace ImageSharp.Benchmarks.General.Vectorization; public abstract class SIMDBenchmarkBase where T : struct { - protected T[] input; + protected virtual T GetTestValue() => default; - protected T[] result; + protected virtual Vector GetTestVector() => new(this.GetTestValue()); - protected T testValue; + [Params(32)] + public int InputSize { get; set; } - protected Vector testVector; + protected T[] Input { get; set; } - protected virtual T GetTestValue() => default; + protected T[] Result { get; set; } - protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); + protected T TestValue { get; set; } - [Params(32)] - public int InputSize { get; set; } + protected Vector TestVector { get; set; } [GlobalSetup] public virtual void Setup() { - this.input = new T[this.InputSize]; - this.result = new T[this.InputSize]; - this.testValue = this.GetTestValue(); - this.testVector = this.GetTestVector(); + this.Input = new T[this.InputSize]; + this.Result = new T[this.InputSize]; + this.TestValue = this.GetTestValue(); + this.TestVector = this.GetTestVector(); } public abstract class Multiply : SIMDBenchmarkBase @@ -39,13 +39,13 @@ public abstract class Multiply : SIMDBenchmarkBase [Benchmark] public void Simd() { - Vector v = this.testVector; + Vector v = this.TestVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) + for (int i = 0; i < this.Input.Length; i += Vector.Count) { - Vector a = Unsafe.As>(ref this.input[i]); - a = a * v; - Unsafe.As>(ref this.result[i]) = a; + Vector a = Unsafe.As>(ref this.Input[i]); + a *= v; + Unsafe.As>(ref this.Result[i]) = a; } } } @@ -55,13 +55,13 @@ public abstract class Divide : SIMDBenchmarkBase [Benchmark] public void Simd() { - Vector v = this.testVector; + Vector v = this.TestVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) + for (int i = 0; i < this.Input.Length; i += Vector.Count) { - Vector a = Unsafe.As>(ref this.input[i]); - a = a / v; - Unsafe.As>(ref this.result[i]) = a; + Vector a = Unsafe.As>(ref this.Input[i]); + a /= v; + Unsafe.As>(ref this.Result[i]) = a; } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs index fc4dc1fa16..4048bc210e 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class UInt32ToSingle { private float[] data; @@ -27,20 +27,20 @@ public void MagicMethod() nuint n = Count / (uint)Vector.Count; - var bVec = new Vector(256.0f / 255.0f); - var magicFloat = new Vector(32768.0f); - var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f - var mask = new Vector(255); + Vector bVec = new(256.0f / 255.0f); + Vector magicFloat = new(32768.0f); + Vector magicInt = new(1191182336); // reinterpreted value of 32768.0f + Vector mask = new(255); for (nuint i = 0; i < n; i++) { ref Vector df = ref Unsafe.Add(ref b, i); - var vi = Vector.AsVectorUInt32(df); + Vector vi = Vector.AsVectorUInt32(df); vi &= mask; vi |= magicInt; - var vf = Vector.AsVectorSingle(vi); + Vector vf = Vector.AsVectorSingle(vi); vf = (vf - magicFloat) * bVec; df = vf; @@ -55,7 +55,7 @@ public void StandardSimd() ref Vector bf = ref Unsafe.As>(ref this.data[0]); ref Vector bu = ref Unsafe.As, Vector>(ref bf); - var scale = new Vector(1f / 255f); + Vector scale = new(1f / 255f); for (nuint i = 0; i < n; i++) { @@ -74,7 +74,7 @@ public void StandardSimdFromInt() ref Vector bf = ref Unsafe.As>(ref this.data[0]); ref Vector bu = ref Unsafe.As, Vector>(ref bf); - var scale = new Vector(1f / 255f); + Vector scale = new(1f / 255f); for (nuint i = 0; i < n; i++) { @@ -91,7 +91,7 @@ public void StandardSimdFromInt_RefCast() nuint n = Count / (uint)Vector.Count; ref Vector bf = ref Unsafe.As>(ref this.data[0]); - var scale = new Vector(1f / 255f); + Vector scale = new(1f / 255f); for (nuint i = 0; i < n; i++) { diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 5d20f29d18..9c7fd5b6cc 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -47,11 +47,11 @@ public void Baseline() [Benchmark] public void FetchWithVectorConstructor() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); for (int i = 0; i < this.data.Length; i += Vector.Count) { - var a = new Vector(this.data, i); + Vector a = new(this.data, i); a = a * v; a.CopyTo(this.data, i); } @@ -60,7 +60,7 @@ public void FetchWithVectorConstructor() [Benchmark] public void FetchWithUnsafeCast() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); ref Vector start = ref Unsafe.As>(ref this.data[0]); nuint n = (uint)this.InputSize / (uint)Vector.Count; @@ -79,7 +79,7 @@ public void FetchWithUnsafeCast() [Benchmark] public void FetchWithUnsafeCastNoTempVector() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); ref Vector start = ref Unsafe.As>(ref this.data[0]); nuint n = (uint)this.InputSize / (uint)Vector.Count; @@ -94,9 +94,9 @@ public void FetchWithUnsafeCastNoTempVector() [Benchmark] public void FetchWithUnsafeCastFromReference() { - var v = new Vector(this.testValue); + Vector v = new(this.testValue); - var span = new Span(this.data); + Span span = new(this.data); ref Vector start = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs index f391e42012..fd17262dec 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -5,11 +5,9 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Tuples; - namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization; -[Config(typeof(Config.ShortMultiFramework))] +[Config(typeof(Config.Short))] public class WidenBytesToUInt32 { private byte[] source; @@ -30,12 +28,22 @@ public void Standard() { const int N = Count / 8; - ref Octet sBase = ref Unsafe.As>(ref this.source[0]); - ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); + ref InlineArray8 sBase = ref Unsafe.As>(ref this.source[0]); + ref InlineArray8 dBase = ref Unsafe.As>(ref this.dest[0]); for (nuint i = 0; i < N; i++) { - Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); + ref InlineArray8 source = ref Unsafe.Add(ref sBase, i); + ref InlineArray8 destination = ref Unsafe.Add(ref dBase, i); + + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + destination[4] = source[4]; + destination[5] = source[5]; + destination[6] = source[6]; + destination[7] = source[7]; } } diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 0ba2f4b949..0351ecae9f 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -11,24 +11,40 @@ false Debug;Release - + - + + - CA1822 + + + + CA1822;CA1416;CA1001;CS0029;CA1861;CA2201 + + + + + + + + + - net7.0;net6.0 + net8.0;net10.0 - net6.0 + net8.0 @@ -41,8 +57,9 @@ - - + + + @@ -52,6 +69,7 @@ + diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs index 04621695c6..0731c7c004 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressBenchmarks.cs @@ -18,7 +18,7 @@ public class LoadResizeSaveStressBenchmarks [GlobalSetup] public void Setup() { - this.runner = new LoadResizeSaveStressRunner() + this.runner = new LoadResizeSaveStressRunner { ImageCount = Environment.ProcessorCount, Filter = Filter @@ -34,12 +34,12 @@ private void ForEachImage(Action action, int maxDegreeOfParallelism) } public int[] ParallelismValues { get; } = - { + [ // Environment.ProcessorCount, // Environment.ProcessorCount / 2, // Environment.ProcessorCount / 4, 1 - }; + ]; [Benchmark] [ArgumentsSource(nameof(ParallelismValues))] diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index dd9b55e58d..815a145f74 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -6,6 +6,7 @@ using System.Drawing.Imaging; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using ImageMagick; using PhotoSauce.MagicScaler; using SixLabors.ImageSharp.Formats; @@ -19,6 +20,7 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave; +[Flags] public enum JpegKind { Baseline = 1, @@ -26,11 +28,12 @@ public enum JpegKind Any = Baseline | Progressive } +[SupportedOSPlatform("windows")] public class LoadResizeSaveStressRunner { private const int Quality = 75; - // Set the quality for ImagSharp + // Set the quality for ImageSharp private readonly JpegEncoder imageSharpJpegEncoder = new() { Quality = Quality }; private readonly ImageCodecInfo systemDrawingJpegCodec = ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid); @@ -39,7 +42,7 @@ public class LoadResizeSaveStressRunner public double TotalProcessedMegapixels { get; private set; } - public Size LastProcessedImageSize { get; private set; } + public ImageSharpSize LastProcessedImageSize { get; private set; } private string outputDirectory; @@ -52,7 +55,7 @@ public class LoadResizeSaveStressRunner public int ThumbnailSize { get; set; } = 150; private static readonly string[] ProgressiveFiles = - { + [ "ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg", "acanthopus-excellens-f-face-brasil_2014-08-06-132105-zs-pmax_14792513890_o.jpg", "bee-ceratina-monster-f-ukraine-face_2014-08-09-123342-zs-pmax_15068816101_o.jpg", @@ -83,8 +86,8 @@ public class LoadResizeSaveStressRunner "triepeolus-simplex-m-face-md-kent-county_2014-07-22-100937-zs-pmax_14805405233_o.jpg", "washed-megachile-f-face-chile_2014-08-06-103414-zs-pmax_14977843152_o.jpg", "xylocopa-balck-violetwing-f-kyrgystan-angle_2014-08-09-182433-zs-pmax_15123416061_o.jpg", - "xylocopa-india-yellow-m-india-face_2014-08-10-111701-zs-pmax_15166559172_o.jpg", - }; + "xylocopa-india-yellow-m-india-face_2014-08-10-111701-zs-pmax_15166559172_o.jpg" + ]; public void Init() { @@ -126,7 +129,7 @@ public Task ForEachImageParallelAsync(Func action) : Environment.ProcessorCount; int partitionSize = (int)Math.Ceiling((double)this.Images.Length / maxDegreeOfParallelism); - List tasks = new(); + List tasks = []; for (int i = 0; i < this.Images.Length; i += partitionSize) { int end = Math.Min(i + partitionSize, this.Images.Length); @@ -147,7 +150,7 @@ Task RunPartition(int start, int end) => Task.Run(async () => private void LogImageProcessed(int width, int height) { - this.LastProcessedImageSize = new Size(width, height); + this.LastProcessedImageSize = new ImageSharpSize(width, height); double pixels = width * (double)height; this.TotalProcessedMegapixels += pixels / 1_000_000.0; } @@ -157,7 +160,7 @@ private string OutputPath(string inputPath, [CallerMemberName] string postfix = this.outputDirectory, Path.GetFileNameWithoutExtension(inputPath) + "-" + postfix + Path.GetExtension(inputPath)); - private (int Width, int Height) ScaledSize(int inWidth, int inHeight, int outSize) + private static (int Width, int Height) ScaledSize(int inWidth, int inHeight, int outSize) { int width, height; if (inWidth > inHeight) @@ -176,13 +179,13 @@ private string OutputPath(string inputPath, [CallerMemberName] string postfix = public void SystemDrawingResize(string input) { - using var image = SystemDrawingImage.FromFile(input, true); + using SystemDrawingImage image = SystemDrawingImage.FromFile(input, true); this.LogImageProcessed(image.Width, image.Height); - (int Width, int Height) scaled = this.ScaledSize(image.Width, image.Height, this.ThumbnailSize); - var resized = new Bitmap(scaled.Width, scaled.Height); - using var graphics = Graphics.FromImage(resized); - using var attributes = new ImageAttributes(); + (int width, int height) = ScaledSize(image.Width, image.Height, this.ThumbnailSize); + Bitmap resized = new(width, height); + using Graphics graphics = Graphics.FromImage(resized); + using ImageAttributes attributes = new(); attributes.SetWrapMode(WrapMode.TileFlipXY); graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingMode = CompositingMode.SourceCopy; @@ -191,8 +194,8 @@ public void SystemDrawingResize(string input) graphics.DrawImage(image, System.Drawing.Rectangle.FromLTRB(0, 0, resized.Width, resized.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); // Save the results - using var encoderParams = new EncoderParameters(1); - using var qualityParam = new EncoderParameter(Encoder.Quality, (long)Quality); + using EncoderParameters encoderParams = new(1); + using EncoderParameter qualityParam = new(Encoder.Quality, (long)Quality); encoderParams.Param[0] = qualityParam; resized.Save(this.OutputPath(input), this.systemDrawingJpegCodec, encoderParams); } @@ -223,7 +226,7 @@ public void ImageSharpResize(string input) public async Task ImageSharpResizeAsync(string input) { - using FileStream output = File.Open(this.OutputPath(input), FileMode.Create); + await using FileStream output = File.Open(this.OutputPath(input), FileMode.Create); // Resize it to fit a 150x150 square. DecoderOptions options = new() @@ -246,11 +249,11 @@ public async Task ImageSharpResizeAsync(string input) public void MagickResize(string input) { - using var image = new MagickImage(input); - this.LogImageProcessed(image.Width, image.Height); + using MagickImage image = new(input); + this.LogImageProcessed((int)image.Width, (int)image.Height); // Resize it to fit a 150x150 square - image.Resize(this.ThumbnailSize, this.ThumbnailSize); + image.Resize((uint)this.ThumbnailSize, (uint)this.ThumbnailSize); // Reduce the size of the file image.Strip(); @@ -264,30 +267,28 @@ public void MagickResize(string input) public void MagicScalerResize(string input) { - var settings = new ProcessImageSettings() + ProcessImageSettings settings = new() { 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? - using var output = new FileStream(this.OutputPath(input), FileMode.Create); + using FileStream output = new(this.OutputPath(input), FileMode.Create); MagicImageProcessor.ProcessImage(input, output, settings); } public void SkiaCanvasResize(string input) { - using var original = SKBitmap.Decode(input); + using SKBitmap original = SKBitmap.Decode(input); this.LogImageProcessed(original.Width, original.Height); - (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); - using var surface = SKSurface.Create(new SKImageInfo(scaled.Width, scaled.Height, original.ColorType, original.AlphaType)); - using var paint = new SKPaint() { FilterQuality = SKFilterQuality.High }; + (int width, int height) = ScaledSize(original.Width, original.Height, this.ThumbnailSize); + using SKSurface surface = SKSurface.Create(new SKImageInfo(width, height, original.ColorType, original.AlphaType)); + using SKPaint paint = new() { FilterQuality = SKFilterQuality.High }; SKCanvas canvas = surface.Canvas; - canvas.Scale((float)scaled.Width / original.Width); + canvas.Scale((float)width / original.Width); canvas.DrawBitmap(original, 0, 0, paint); canvas.Flush(); @@ -299,16 +300,16 @@ public void SkiaCanvasResize(string input) public void SkiaBitmapResize(string input) { - using var original = SKBitmap.Decode(input); + using SKBitmap original = SKBitmap.Decode(input); this.LogImageProcessed(original.Width, original.Height); - (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize); - using var resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); + (int width, int height) = ScaledSize(original.Width, original.Height, this.ThumbnailSize); + using SKBitmap resized = original.Resize(new SKImageInfo(width, height), SKFilterQuality.High); if (resized == null) { return; } - using var image = SKImage.FromBitmap(resized); + using SKImage image = SKImage.FromBitmap(resized); using FileStream output = File.OpenWrite(this.OutputPath(input)); image.Encode(SKEncodedImageFormat.Jpeg, Quality) .SaveTo(output); @@ -316,21 +317,21 @@ public void SkiaBitmapResize(string input) public void SkiaBitmapDecodeToTargetSize(string input) { - using var codec = SKCodec.Create(input); + using SKCodec codec = SKCodec.Create(input); SKImageInfo info = codec.Info; this.LogImageProcessed(info.Width, info.Height); - (int Width, int Height) scaled = this.ScaledSize(info.Width, info.Height, this.ThumbnailSize); - SKSizeI supportedScale = codec.GetScaledDimensions((float)scaled.Width / info.Width); + (int width, int height) = ScaledSize(info.Width, info.Height, this.ThumbnailSize); + SKSizeI supportedScale = codec.GetScaledDimensions((float)width / info.Width); - using var original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height)); - using SKBitmap resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High); + using SKBitmap original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height)); + using SKBitmap resized = original.Resize(new SKImageInfo(width, height), SKFilterQuality.High); if (resized == null) { return; } - using var image = SKImage.FromBitmap(resized); + using SKImage image = SKImage.FromBitmap(resized); using FileStream output = File.OpenWrite(this.OutputPath(input, nameof(this.SkiaBitmapDecodeToTargetSize))); image.Encode(SKEncodedImageFormat.Jpeg, Quality) @@ -340,9 +341,9 @@ public void SkiaBitmapDecodeToTargetSize(string input) public void NetVipsResize(string input) { // Thumbnail to fit a 150x150 square - using var thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize); + using NetVipsImage 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.Benchmarks/LoadResizeSave/README.md b/tests/ImageSharp.Benchmarks/LoadResizeSave/README.md index 6cb48eb48c..98f472241f 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.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs new file mode 100644 index 0000000000..02f25f2ea3 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/AssociatedAlphaPixelBlenderBenchmark.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.PixelBlenders; + +/// +/// Compares straight-alpha and associated-alpha bulk pixel blending. +/// +[Config(typeof(Config.Short))] +public class AssociatedAlphaPixelBlenderBenchmark +{ + private const int PixelCount = 1024; + + private readonly Rgba32[] unassociatedDestination = new Rgba32[PixelCount]; + private readonly Rgba32[] unassociatedBackground = new Rgba32[PixelCount]; + private readonly Rgba32[] unassociatedSource = new Rgba32[PixelCount]; + private readonly Rgba32P[] associatedDestination = new Rgba32P[PixelCount]; + private readonly Rgba32P[] associatedBackground = new Rgba32P[PixelCount]; + private readonly Rgba32P[] associatedSource = new Rgba32P[PixelCount]; + private readonly float[] amounts = new float[PixelCount]; + private readonly Vector4[] unassociatedWorkingBuffer = new Vector4[PixelCount * 3]; + private readonly Vector4[] associatedWorkingBuffer = new Vector4[PixelCount * 3]; + private PixelBlender unassociatedBlender; + private PixelBlender associatedBlender; + + /// + /// Gets or sets the color-blending mode measured by the benchmark. + /// + [ParamsAllValues] + public PixelColorBlendingMode ColorMode { get; set; } + + /// + /// Gets or sets the alpha-composition mode measured by the benchmark. + /// + [ParamsAllValues] + public PixelAlphaCompositionMode AlphaMode { get; set; } + + /// + /// Initializes equivalent straight-alpha and associated-alpha rows. + /// + [GlobalSetup] + public void Setup() + { + this.unassociatedBlender = PixelOperations.Instance.GetPixelBlender(this.ColorMode, this.AlphaMode); + this.associatedBlender = PixelOperations.Instance.GetPixelBlender(this.ColorMode, this.AlphaMode); + + Random random = new(42); + + for (int i = 0; i < PixelCount; i++) + { + Rgba32 background = new((byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(64, 256)); + Rgba32 source = new((byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(64, 256)); + + this.unassociatedBackground[i] = background; + this.unassociatedSource[i] = source; + this.associatedBackground[i] = Rgba32P.FromRgba32(background); + this.associatedSource[i] = Rgba32P.FromRgba32(source); + this.amounts[i] = random.NextSingle(); + } + } + + /// + /// Blends one row stored with straight alpha. + /// + /// The last destination pixel. + [Benchmark(Description = "Straight alpha", Baseline = true)] + public Rgba32 BlendUnassociated() + { + this.unassociatedBlender.Blend( + Configuration.Default, + this.unassociatedDestination, + this.unassociatedBackground, + this.unassociatedSource, + this.amounts, + this.unassociatedWorkingBuffer); + + return this.unassociatedDestination[^1]; + } + + /// + /// Blends one row stored with associated alpha. + /// + /// The last destination pixel. + [Benchmark(Description = "Associated alpha")] + public Rgba32P BlendAssociated() + { + this.associatedBlender.Blend( + Configuration.Default, + this.associatedDestination, + this.associatedBackground, + this.associatedSource, + this.amounts, + this.associatedWorkingBuffer); + + return this.associatedDestination[^1]; + } +} diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs index ecf8b125f7..fe43ce5e79 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs @@ -18,8 +18,8 @@ public class PorterDuffBulkVsSingleVector [GlobalSetup] public void Setup() { - this.backdrop = new Vector4[8 * 20]; - this.source = new Vector4[8 * 20]; + this.backdrop = new Vector4[8 * 40]; + this.source = new Vector4[8 * 40]; FillRandom(this.backdrop); FillRandom(this.source); @@ -49,7 +49,7 @@ public Vector4 OverlayValueFunction_Scalar() return result; } - [Benchmark(Description = "Avx")] + [Benchmark(Description = "Avx2")] public Vector256 OverlayValueFunction_Avx() { ref Vector256 backdrop = ref Unsafe.As>(ref MemoryMarshal.GetReference(this.backdrop)); @@ -65,4 +65,21 @@ public Vector256 OverlayValueFunction_Avx() return result; } + + [Benchmark(Description = "Avx512")] + public Vector512 OverlayValueFunction_Avx512() + { + ref Vector512 backdrop = ref Unsafe.As>(ref MemoryMarshal.GetReference(this.backdrop)); + ref Vector512 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(this.source)); + + Vector512 result = default; + Vector512 opacity = Vector512.Create(.5F); + int count = this.backdrop.Length / 4; + for (nuint i = 0; i < (uint)count; i++) + { + result = PorterDuffFunctions.NormalSrcOver(Unsafe.Add(ref backdrop, i), Unsafe.Add(ref source, i), opacity); + } + + return result; + } } diff --git a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs index 441500c88c..82fce7dad7 100644 --- a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] 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/ColorMatchingCaches.cs b/tests/ImageSharp.Benchmarks/Processing/ColorMatchingCaches.cs new file mode 100644 index 0000000000..dbaf21a8ef --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Processing/ColorMatchingCaches.cs @@ -0,0 +1,302 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Benchmarks.Processing; + +[Config(typeof(Config.Standard))] +public class ColorMatchingCaches +{ + // IterationSetup forces BenchmarkDotNet to use a single benchmark invocation per iteration. + // Repeated lookups can safely replay a smaller working set because that workload is explicitly + // meant to model steady-state cache hits after warmup. + private const int RepeatedLookupCount = 262_144; + + // DitherLike should avoid replaying the same stream across multiple passes because that warms + // the caches in a way real high-churn input usually does not. Make the single pass larger instead. + private const int DitherLikeLookupCount = 1_048_576; + private const int RepeatedPassCount = 16; + + private Rgba32[] palette; + private Rgba32[] repeatedSeedColors; + private Rgba32[] repeatedLookups; + private Rgba32[] ditherLookups; + + private PixelMap coarse; + private PixelMap legacyCoarse; + private PixelMap exact; + private PixelMap uncached; + + [Params(16, 256)] + public int PaletteSize { get; set; } + + [Params(CacheWorkload.Repeated, CacheWorkload.DitherLike)] + public CacheWorkload Workload { get; set; } + + [GlobalSetup] + public void Setup() + { + this.palette = CreatePalette(this.PaletteSize); + this.repeatedSeedColors = CreateRepeatedSeedColors(this.palette); + this.repeatedLookups = CreateRepeatedLookups(this.repeatedSeedColors); + this.ditherLookups = CreateDitherLikeLookups(); + + this.coarse = CreatePixelMap(this.palette); + this.legacyCoarse = CreatePixelMap(this.palette); + this.exact = CreatePixelMap(this.palette); + this.uncached = CreatePixelMap(this.palette); + } + + [IterationSetup] + public void ResetCaches() + { + // Each benchmark iteration should start from the same cache state so we measure + // the cache policy itself rather than warm state carried over from a previous iteration. + this.coarse.Clear(this.palette); + this.legacyCoarse.Clear(this.palette); + this.exact.Clear(this.palette); + this.uncached.Clear(this.palette); + + if (this.Workload == CacheWorkload.Repeated) + { + // Prime the repeated workload so the benchmark reflects steady-state hit behavior + // instead of mostly measuring the first-wave fill cost. + Prime(this.coarse, this.repeatedSeedColors); + Prime(this.legacyCoarse, this.repeatedSeedColors); + Prime(this.exact, this.repeatedSeedColors); + Prime(this.uncached, this.repeatedSeedColors); + } + } + + [GlobalCleanup] + public void Cleanup() + { + this.coarse.Dispose(); + this.legacyCoarse.Dispose(); + this.exact.Dispose(); + this.uncached.Dispose(); + } + + [Benchmark(Baseline = true, Description = "Coarse")] + public int Coarse() => this.Run(this.coarse); + + [Benchmark(Description = "Legacy Coarse")] + public int LegacyCoarse() => this.Run(this.legacyCoarse); + + [Benchmark(Description = "Exact Cached")] + public int Exact() => this.Run(this.exact); + + [Benchmark(Description = "Exact Uncached")] + public int Uncached() => this.Run(this.uncached); + + public enum CacheWorkload + { + // A small working set that is intentionally reused after priming to measure hit-heavy behavior. + Repeated, + + // A deterministic high-churn stream intended to resemble dithered lookups where exact repeats are rare. + DitherLike + } + + private int Run(PixelMap map) + { + Rgba32[] lookups = this.Workload == CacheWorkload.Repeated ? this.repeatedLookups : this.ditherLookups; + int passCount = this.Workload == CacheWorkload.Repeated ? RepeatedPassCount : 1; + int checksum = 0; + + // Repeated intentionally replays the same lookup stream to measure steady-state hit behavior. + // DitherLike runs as a single larger pass so we do not turn a churn-heavy workload into an + // artificially warmed cache benchmark by replaying the exact same sequence. + for (int pass = 0; pass < passCount; pass++) + { + for (int i = 0; i < lookups.Length; i++) + { + checksum = unchecked((checksum * 31) + map.GetClosestColor(lookups[i], out _)); + } + } + + return checksum; + } + + private static PixelMap CreatePixelMap(Rgba32[] palette) + where TCache : struct, IColorIndexCache + => new EuclideanPixelMap(Configuration.Default, palette); + + private static void Prime(PixelMap map, Rgba32[] colors) + { + for (int i = 0; i < colors.Length; i++) + { + map.GetClosestColor(colors[i], out _); + } + } + + private static Rgba32[] CreatePalette(int count) + { + Rgba32[] result = new Rgba32[count]; + + for (int i = 0; i < result.Length; i++) + { + // Use the Knuth/golden-ratio multiplicative hash constant to spread colors across + // RGBA space without clustering into a gradient. That keeps the benchmark from + // accidentally favoring any cache because the palette itself is too regular. + uint value = unchecked((uint)(i + 1) * 2654435761U); + result[i] = new( + (byte)value, + (byte)(value >> 8), + (byte)(value >> 16), + (byte)((value >> 24) | 0x80)); + } + + return result; + } + + private static Rgba32[] CreateRepeatedSeedColors(Rgba32[] palette) + { + // Reuse colors derived from the palette but perturb them slightly so the workload still + // exercises nearest-color matching rather than only exact palette-entry hits. + int count = Math.Min(64, palette.Length * 2); + Rgba32[] result = new Rgba32[count]; + + for (int i = 0; i < result.Length; i++) + { + Rgba32 source = palette[(i * 17) % palette.Length]; + result[i] = new( + (byte)(source.R + ((i * 3) & 0x07)), + (byte)(source.G + ((i * 5) & 0x07)), + (byte)(source.B + ((i * 7) & 0x07)), + source.A); + } + + return result; + } + + private static Rgba32[] CreateRepeatedLookups(Rgba32[] seedColors) + { + Rgba32[] result = new Rgba32[RepeatedLookupCount]; + + // Cycle a small seed set to produce a stable, hit-heavy stream after priming. + for (int i = 0; i < result.Length; i++) + { + result[i] = seedColors[i % seedColors.Length]; + } + + return result; + } + + private static Rgba32[] CreateDitherLikeLookups() + { + Rgba32[] result = new Rgba32[DitherLikeLookupCount]; + + // Generate a deterministic pseudo-image signal with independent channel slopes so nearby + // samples are correlated but exact repeats are uncommon, which is closer to dithered input. + for (int i = 0; i < result.Length; i++) + { + int x = i & 511; + int y = i >> 9; + + result[i] = new( + (byte)((x * 17) + (y * 13)), + (byte)((x * 29) + (y * 7)), + (byte)((x * 11) + (y * 23)), + (byte)(255 - ((x * 3) + (y * 5)))); + } + + return result; + } + + /// + /// Preserves the original direct-mapped coarse cache implementation for side-by-side benchmarks. + /// + private unsafe struct LegacyCoarseCache : IColorIndexCache + { + private const int IndexRBits = 5; + private const int IndexGBits = 5; + private const int IndexBBits = 5; + private const int IndexABits = 6; + private const int IndexRCount = 1 << IndexRBits; + private const int IndexGCount = 1 << IndexGBits; + private const int IndexBCount = 1 << IndexBBits; + private const int IndexACount = 1 << IndexABits; + private const int TotalBins = IndexRCount * IndexGCount * IndexBCount * IndexACount; + + private readonly IMemoryOwner binsOwner; + private readonly short* binsPointer; + private MemoryHandle binsHandle; + + private LegacyCoarseCache(MemoryAllocator allocator) + { + this.binsOwner = allocator.Allocate(TotalBins); + this.binsOwner.GetSpan().Fill(-1); + this.binsHandle = this.binsOwner.Memory.Pin(); + this.binsPointer = (short*)this.binsHandle.Pointer; + } + + public static LegacyCoarseCache Create(MemoryAllocator allocator) => new(allocator); + + [MethodImpl(InliningOptions.ShortMethod)] + public readonly bool TryAdd(Rgba32 color, short value) + { + this.binsPointer[GetCoarseIndex(color)] = value; + return true; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public readonly bool TryGetValue(Rgba32 color, out short value) + { + value = this.binsPointer[GetCoarseIndex(color)]; + return value > -1; + } + + public readonly void Clear() => this.binsOwner.GetSpan().Fill(-1); + + public void Dispose() + { + this.binsHandle.Dispose(); + this.binsOwner.Dispose(); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int GetCoarseIndex(Rgba32 color) + { + int rIndex = color.R >> (8 - IndexRBits); + int gIndex = color.G >> (8 - IndexGBits); + int bIndex = color.B >> (8 - IndexBBits); + int aIndex = color.A >> (8 - IndexABits); + + return (aIndex * IndexRCount * IndexGCount * IndexBCount) + + (rIndex * IndexGCount * IndexBCount) + + (gIndex * IndexBCount) + + bIndex; + } + } + + /// + /// Preserves the uncached path for exact-cache comparison benchmarks. + /// + private readonly struct UncachedCache : IColorIndexCache + { + public static UncachedCache Create(MemoryAllocator allocator) => default; + + public bool TryAdd(Rgba32 color, short value) => true; + + public bool TryGetValue(Rgba32 color, out short value) + { + value = -1; + return false; + } + + public void Clear() + { + } + + public void Dispose() + { + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Processing/Crop.cs b/tests/ImageSharp.Benchmarks/Processing/Crop.cs index d802d3293a..e14366bfde 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Crop.cs @@ -11,15 +11,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class Crop { [Benchmark(Baseline = true, Description = "System.Drawing Crop")] public SDSize CropSystemDrawing() { - using var source = new Bitmap(800, 800); - using var destination = new Bitmap(100, 100); - using var graphics = Graphics.FromImage(destination); + using Bitmap source = new(800, 800); + using Bitmap destination = new(100, 100); + using Graphics graphics = Graphics.FromImage(destination); graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; @@ -32,7 +32,7 @@ public SDSize CropSystemDrawing() [Benchmark(Description = "ImageSharp Crop")] public Size CropImageSharp() { - using var image = new Image(800, 800); + using Image image = new(800, 800); image.Mutate(x => x.Crop(100, 100)); return new Size(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs index 9371906965..c48f3e4f49 100644 --- a/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Processing/DetectEdges.cs @@ -8,7 +8,7 @@ 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 c74c4a858a..fae80fe841 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Diffuse.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] 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 Size DoDiffuse() [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 0123f4d3b8..462d83488d 100644 --- a/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/GaussianBlur.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Samplers; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] 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/HistogramEqualization.cs b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs index 6292b793ec..93ac9fc0e3 100644 --- a/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs +++ b/tests/ImageSharp.Benchmarks/Processing/HistogramEqualization.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class HistogramEqualization { private Image image; @@ -33,7 +33,7 @@ public void Cleanup() [Benchmark(Description = "Global Histogram Equalization")] public void GlobalHistogramEqualization() => this.image.Mutate(img => img.HistogramEqualization( - new HistogramEqualizationOptions() + new HistogramEqualizationOptions { LuminanceLevels = 256, Method = HistogramEqualizationMethod.Global @@ -42,7 +42,7 @@ public void GlobalHistogramEqualization() [Benchmark(Description = "AdaptiveHistogramEqualization (Tile interpolation)")] public void AdaptiveHistogramEqualization() => this.image.Mutate(img => img.HistogramEqualization( - new HistogramEqualizationOptions() + new HistogramEqualizationOptions { LuminanceLevels = 256, Method = HistogramEqualizationMethod.AdaptiveTileInterpolation diff --git a/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs b/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs index 239d5a93bc..7077484998 100644 --- a/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs +++ b/tests/ImageSharp.Benchmarks/Processing/OilPaint.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] public class OilPaint { [Benchmark] public void DoOilPaint() { - using Image image = new Image(1920, 1200, new(127, 191, 255)); + using Image image = new(1920, 1200, new RgbaVector(127, 191, 255)); image.Mutate(ctx => ctx.OilPaint()); } } diff --git a/tests/ImageSharp.Benchmarks/Processing/ParallelProcessing.cs b/tests/ImageSharp.Benchmarks/Processing/ParallelProcessing.cs new file mode 100644 index 0000000000..4f2089713d --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Processing/ParallelProcessing.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests; + +namespace SixLabors.ImageSharp.Benchmarks; + +public class ParallelProcessing +{ + private Image image; + private Image foreground; + private Configuration configuration; + + public static IEnumerable MaxDegreeOfParallelismValues() + { + int processorCount = Environment.ProcessorCount; + for (int p = 1; p <= processorCount; p *= 2) + { + yield return p; + } + + if ((processorCount & (processorCount - 1)) != 0) + { + yield return processorCount; + } + } + + [ParamsSource(nameof(MaxDegreeOfParallelismValues))] + public int MaxDegreeOfParallelism { get; set; } + + [GlobalSetup] + public void Setup() + { + this.image = new Image(2048, 2048); + this.foreground = new Image(2048, 2048); + this.configuration = Configuration.Default.Clone(); + this.configuration.MaxDegreeOfParallelism = this.MaxDegreeOfParallelism; + } + + [Benchmark] + public void DetectEdges() => this.image.Mutate(this.configuration, x => x.DetectEdges()); + + [Benchmark] + public void DrawImage() => this.image.Mutate(this.configuration, x => x.DrawImage(this.foreground, 0.5f)); + + [Benchmark] + public void Crop() + { + Rectangle bounds = this.image.Bounds; + bounds = new Rectangle(1, 1, bounds.Width - 2, bounds.Height - 2); + this.image + .Clone(this.configuration, x => x.Crop(bounds)) + .Dispose(); + } + + [GlobalCleanup] + public void Cleanup() + { + this.image.Dispose(); + this.foreground.Dispose(); + } +} diff --git a/tests/ImageSharp.Benchmarks/Processing/Resize.cs b/tests/ImageSharp.Benchmarks/Processing/Resize.cs index ae993cd25b..3cc10afb72 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Resize.cs @@ -12,17 +12,17 @@ namespace SixLabors.ImageSharp.Benchmarks; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.StandardInProcess))] public abstract class Resize where TPixel : unmanaged, IPixel { - private byte[] bytes = null; + private byte[] bytes; private Image sourceImage; private SDImage sourceBitmap; - protected Configuration Configuration { get; } = new Configuration(new JpegConfigurationModule()); + protected Configuration Configuration { get; } = new(new JpegConfigurationModule()); protected int DestSize { get; private set; } @@ -35,7 +35,7 @@ public virtual void Setup() this.sourceImage = Image.Load(this.bytes); - var ms1 = new MemoryStream(this.bytes); + MemoryStream ms1 = new(this.bytes); this.sourceBitmap = SDImage.FromStream(ms1); this.DestSize = this.sourceBitmap.Width / 2; } @@ -52,21 +52,19 @@ public void Cleanup() [Benchmark(Baseline = true)] public int SystemDrawing() { - using (var destination = new Bitmap(this.DestSize, this.DestSize)) + using Bitmap destination = new(this.DestSize, this.DestSize); + using (Graphics g = Graphics.FromImage(destination)) { - using (var g = Graphics.FromImage(destination)) - { - g.CompositingMode = CompositingMode.SourceCopy; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - g.CompositingQuality = CompositingQuality.HighQuality; - g.SmoothingMode = SmoothingMode.HighQuality; - - g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); - } - - return destination.Width; + g.CompositingMode = CompositingMode.SourceCopy; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.SmoothingMode = SmoothingMode.HighQuality; + + g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); } + + return destination.Width; } [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] @@ -87,10 +85,8 @@ protected int RunImageSharpResize(int maxDegreeOfParallelism) { this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; - using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) - { - return clone.Width; - } + using Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation); + return clone.Width; } protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); diff --git a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs index b9b0f6b3df..b041a9d57e 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] 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 26f2e7d2d6..9f0103fa6b 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Skew.cs @@ -7,13 +7,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing; -[Config(typeof(Config.MultiFramework))] +[Config(typeof(Config.Standard))] 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.PublicApi.Tests/ImageSharp.PublicApi.Tests.csproj b/tests/ImageSharp.PublicApi.Tests/ImageSharp.PublicApi.Tests.csproj new file mode 100644 index 0000000000..33bbc1b940 --- /dev/null +++ b/tests/ImageSharp.PublicApi.Tests/ImageSharp.PublicApi.Tests.csproj @@ -0,0 +1,23 @@ + + + + + SixLabors.ImageSharp.PublicApi.Tests + SixLabors.ImageSharp.PublicApi.Tests + Debug;Release + + + + + + net8.0;net10.0 + + + + + net8.0 + + + + + diff --git a/tests/ImageSharp.PublicApi.Tests/MemoryAllocatorExtensibilityTests.cs b/tests/ImageSharp.PublicApi.Tests/MemoryAllocatorExtensibilityTests.cs new file mode 100644 index 0000000000..766e073415 --- /dev/null +++ b/tests/ImageSharp.PublicApi.Tests/MemoryAllocatorExtensibilityTests.cs @@ -0,0 +1,210 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.PublicApi.Tests; + +/// +/// Verifies that a fully functional can be implemented outside the ImageSharp assembly. +/// +public class MemoryAllocatorExtensibilityTests +{ + private const int OneMegabyte = 1 << 20; + + /// + /// Verifies that an external allocator can apply from its constructor + /// and that the applied limits are readable through the public properties. + /// + [Fact] + public void ExternalAllocatorCanApplyOptionsFromConstructor() + { + ExternalArrayMemoryAllocator allocator = new(new MemoryAllocatorOptions + { + AllocationLimitMegabytes = 8, + SingleBufferAllocationLimitMegabytes = 2, + AccumulativeAllocationLimitMegabytes = 8 + }); + + Assert.Equal(8L * OneMegabyte, allocator.MemoryGroupAllocationLimitBytes); + Assert.Equal(2 * OneMegabyte, allocator.SingleBufferAllocationLimitBytes); + Assert.Equal(8L * OneMegabyte, allocator.AccumulativeAllocationLimitBytes); + } + + /// + /// Verifies that the applied single buffer limit is capped to the group allocation limit. + /// + [Fact] + public void ExternalAllocatorAppliedSingleBufferLimitIsCappedToGroupLimit() + { + ExternalArrayMemoryAllocator allocator = new(new MemoryAllocatorOptions + { + AllocationLimitMegabytes = 4, + SingleBufferAllocationLimitMegabytes = 8 + }); + + Assert.Equal(4 * OneMegabyte, allocator.SingleBufferAllocationLimitBytes); + } + + /// + /// Verifies that an external allocator can set the limit properties directly + /// and that the base class validates allocations against the configured values. + /// + [Fact] + public void ExternalAllocatorCanSetSingleBufferLimit() + { + ExternalArrayMemoryAllocator allocator = new(); + allocator.SetLimits( + memoryGroupAllocationLimitBytes: 4096, + singleBufferAllocationLimitBytes: 1024, + accumulativeAllocationLimitBytes: 4096); + + allocator.Allocate(1024).Dispose(); + Assert.Throws(() => allocator.Allocate(1025)); + } + + /// + /// Verifies that owners produced by an external allocator participate in accumulative allocation tracking. + /// + [Fact] + public void ExternalAllocatorTracksAccumulativeAllocations() + { + ExternalArrayMemoryAllocator allocator = new(); + allocator.SetLimits( + memoryGroupAllocationLimitBytes: 4096, + singleBufferAllocationLimitBytes: 4096, + accumulativeAllocationLimitBytes: 4096); + + IMemoryOwner owner = allocator.Allocate(4096); + + // The full accumulative budget is reserved while the owner is live. + Assert.Throws(() => allocator.Allocate(1)); + + // Disposing the owner releases the reservation. + owner.Dispose(); + allocator.Allocate(4096).Dispose(); + } + + /// + /// Verifies that an external allocator can back image creation through , + /// for both discontiguous and contiguous buffer preferences, and that disposal reaches the external owners. + /// + /// The contiguous buffer preference to apply. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ExternalAllocatorBacksImageCreation(bool preferContiguousImageBuffers) + { + ExternalArrayMemoryAllocator allocator = new(); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = allocator; + configuration.PreferContiguousImageBuffers = preferContiguousImageBuffers; + + using (Image image = new(configuration, 16, 16, Color.Red.ToPixel())) + { + Assert.True(allocator.CreatedOwners > 0); + Assert.Equal(Color.Red.ToPixel(), image[8, 8]); + } + + Assert.Equal(0, allocator.LiveOwners); + } + + /// + /// A implemented with only the public API surface, backed by managed arrays. + /// + private sealed class ExternalArrayMemoryAllocator : MemoryAllocator + { + /// + /// Initializes a new instance of the class with default limits. + /// + public ExternalArrayMemoryAllocator() + { + } + + /// + /// Initializes a new instance of the class with custom limits. + /// + /// The to apply. + public ExternalArrayMemoryAllocator(MemoryAllocatorOptions options) => this.ApplyOptions(options); + + /// + /// Gets the total number of owners created by this allocator. + /// + public int CreatedOwners { get; private set; } + + /// + /// Gets the number of owners created by this allocator that are not yet disposed. + /// + public int LiveOwners { get; private set; } + + /// + /// Sets the protected limit properties directly, as a derived allocator can. + /// + /// The group allocation limit, in bytes. + /// The single buffer allocation limit, in bytes. + /// The accumulative allocation limit, in bytes. + public void SetLimits( + long memoryGroupAllocationLimitBytes, + int singleBufferAllocationLimitBytes, + long accumulativeAllocationLimitBytes) + { + this.MemoryGroupAllocationLimitBytes = memoryGroupAllocationLimitBytes; + this.SingleBufferAllocationLimitBytes = singleBufferAllocationLimitBytes; + this.AccumulativeAllocationLimitBytes = accumulativeAllocationLimitBytes; + } + + /// + protected override int GetBufferCapacityInBytes() => int.MaxValue; + + /// + protected override AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) + { + this.CreatedOwners++; + this.LiveOwners++; + return new ExternalArrayMemoryManager(new T[length], this); + } + + /// + /// Records the disposal of an owner created by this allocator. + /// + internal void OnOwnerDisposed() => this.LiveOwners--; + } + + /// + /// An implemented with only the public API surface. + /// + /// The element type. + private sealed class ExternalArrayMemoryManager : AllocationTrackedMemoryManager + where T : struct + { + private readonly T[] array; + private readonly ExternalArrayMemoryAllocator allocator; + + /// + /// Initializes a new instance of the class. + /// + /// The array that backs this owner. + /// The allocator that created this owner. + public ExternalArrayMemoryManager(T[] array, ExternalArrayMemoryAllocator allocator) + { + this.array = array; + this.allocator = allocator; + } + + /// + public override Span GetSpan() => this.array; + + /// + public override MemoryHandle Pin(int elementIndex = 0) => throw new NotSupportedException("Pinning is not required by these tests."); + + /// + public override void Unpin() + { + } + + /// + protected override void DisposeCore(bool disposing) => this.allocator.OnOwnerDisposed(); + } +} diff --git a/tests/ImageSharp.PublicApi.Tests/PixelOperationsExtensibilityTests.cs b/tests/ImageSharp.PublicApi.Tests/PixelOperationsExtensibilityTests.cs new file mode 100644 index 0000000000..a1916831ff --- /dev/null +++ b/tests/ImageSharp.PublicApi.Tests/PixelOperationsExtensibilityTests.cs @@ -0,0 +1,163 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.PublicApi.Tests; + +/// +/// Verifies that representation-aware pixel operations can be implemented outside the ImageSharp assembly. +/// +public class PixelOperationsExtensibilityTests +{ + /// + /// Verifies that an external subclass can override and invoke every representation-aware hook. + /// + [Fact] + public void PixelOperationsExposesAllRepresentationHooksToExternalSubclasses() + { + ExternalRgba32PixelOperations operations = new(); + + operations.InvokeRepresentationHooks(); + + Assert.Equal(RepresentationHooks.All, operations.InvokedHooks); + } + + /// + /// Verifies that an external subclass can override and invoke every representation-aware hook. + /// + [Fact] + public void AssociatedAlphaPixelOperationsExposesAllRepresentationHooksToExternalSubclasses() + { + ExternalRgba32PPixelOperations operations = new(); + + operations.InvokeRepresentationHooks(); + + Assert.Equal(RepresentationHooks.All, operations.InvokedHooks); + } + + [Flags] + private enum RepresentationHooks + { + None = 0, + ToUnassociatedVector4 = 1 << 0, + ToAssociatedVector4 = 1 << 1, + ToUnassociatedScaledVector4 = 1 << 2, + ToAssociatedScaledVector4 = 1 << 3, + FromUnassociatedVector4Destructive = 1 << 4, + FromAssociatedVector4Destructive = 1 << 5, + FromUnassociatedScaledVector4Destructive = 1 << 6, + FromAssociatedScaledVector4Destructive = 1 << 7, + All = ToUnassociatedVector4 + | ToAssociatedVector4 + | ToUnassociatedScaledVector4 + | ToAssociatedScaledVector4 + | FromUnassociatedVector4Destructive + | FromAssociatedVector4Destructive + | FromUnassociatedScaledVector4Destructive + | FromAssociatedScaledVector4Destructive + } + + /// + /// Exercises the representation-aware hooks inherited directly from . + /// + private sealed class ExternalRgba32PixelOperations : PixelOperations + { + /// + /// Gets the hooks invoked through this external subclass. + /// + public RepresentationHooks InvokedHooks { get; private set; } + + /// + /// Invokes every representation-aware operation exposed to derived classes. + /// + public void InvokeRepresentationHooks() + { + // Empty spans isolate accessibility and virtual dispatch; conversion behavior is covered by the pixel-operation test suite. + this.ToUnassociatedVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToAssociatedVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToUnassociatedScaledVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToAssociatedScaledVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.FromUnassociatedVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromAssociatedVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromUnassociatedScaledVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromAssociatedScaledVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + } + + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToUnassociatedVector4; + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToAssociatedVector4; + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToUnassociatedScaledVector4; + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToAssociatedScaledVector4; + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromUnassociatedVector4Destructive; + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromAssociatedVector4Destructive; + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromUnassociatedScaledVector4Destructive; + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromAssociatedScaledVector4Destructive; + } + + /// + /// Exercises the representation-aware hooks inherited through . + /// + private sealed class ExternalRgba32PPixelOperations : AssociatedAlphaPixelOperations + { + /// + /// Gets the hooks invoked through this external subclass. + /// + public RepresentationHooks InvokedHooks { get; private set; } + + /// + /// Invokes every representation-aware operation exposed to derived classes. + /// + public void InvokeRepresentationHooks() + { + // Empty spans isolate accessibility and virtual dispatch; conversion behavior is covered by the pixel-operation test suite. + this.ToUnassociatedVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToAssociatedVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToUnassociatedScaledVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.ToAssociatedScaledVector4(Configuration.Default, ReadOnlySpan.Empty, Span.Empty); + this.FromUnassociatedVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromAssociatedVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromUnassociatedScaledVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + this.FromAssociatedScaledVector4Destructive(Configuration.Default, Span.Empty, Span.Empty); + } + + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToUnassociatedVector4; + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToAssociatedVector4; + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToUnassociatedScaledVector4; + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) => this.InvokedHooks |= RepresentationHooks.ToAssociatedScaledVector4; + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromUnassociatedVector4Destructive; + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromAssociatedVector4Destructive; + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromUnassociatedScaledVector4Destructive; + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) => this.InvokedHooks |= RepresentationHooks.FromAssociatedScaledVector4Destructive; + } +} diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 492ce36b81..f3aa910b96 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -7,8 +7,7 @@ Exe false SixLabors.ImageSharp.Tests.ProfilingSandbox - win7-x64 - SixLabors.ImageSharp.Tests.ProfilingSandbox.Program + win-x64 false false @@ -19,12 +18,12 @@ - net7.0;net6.0 + net8.0;net10.0 - net6.0 + net8.0 @@ -43,7 +42,7 @@ - + diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs index 248912b14f..5e21b7cc1a 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Globalization; +using System.Runtime.Versioning; using System.Text; using CommandLine; using CommandLine.Text; @@ -13,11 +14,12 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox; // See ImageSharp.Benchmarks/LoadResizeSave/README.md -internal class LoadResizeSaveParallelMemoryStress +[SupportedOSPlatform("windows")] +internal sealed class LoadResizeSaveParallelMemoryStress { private LoadResizeSaveParallelMemoryStress() { - this.Benchmarks = new LoadResizeSaveStressRunner() + this.Benchmarks = new LoadResizeSaveStressRunner { Filter = JpegKind.Baseline, }; @@ -38,7 +40,7 @@ public static void Run(string[] args) Console.WriteLine($"64 bit: {Environment.Is64BitProcess}"); CommandLineOptions options = args.Length > 0 ? CommandLineOptions.Parse(args) : null; - var lrs = new LoadResizeSaveParallelMemoryStress(); + LoadResizeSaveParallelMemoryStress lrs = new(); if (options != null) { lrs.Benchmarks.MaxDegreeOfParallelism = options.MaxDegreeOfParallelism; @@ -108,7 +110,7 @@ public static void Run(string[] args) } } - var stats = new Stats(timer, lrs.Benchmarks.TotalProcessedMegapixels); + Stats stats = new(timer, lrs.Benchmarks.TotalProcessedMegapixels); Console.WriteLine($"Total Megapixels: {stats.TotalMegapixels}, TotalOomRetries: {UnmanagedMemoryHandle.TotalOomRetries}, TotalOutstandingHandles: {UnmanagedMemoryHandle.TotalOutstandingHandles}, Total Gen2 GC count: {GC.CollectionCount(2)}"); Console.WriteLine(stats.GetMarkdown()); if (options?.FileOutput != null) @@ -203,17 +205,18 @@ public Stats(Stopwatch sw, double totalMegapixels) public string GetMarkdown() { - var bld = new StringBuilder(); + StringBuilder bld = new(); bld.AppendLine($"| {nameof(this.TotalSeconds)} | {nameof(this.MegapixelsPerSec)} | {nameof(this.MegapixelsPerSecPerCpu)} |"); bld.AppendLine( + CultureInfo.InvariantCulture, $"| {L(nameof(this.TotalSeconds))} | {L(nameof(this.MegapixelsPerSec))} | {L(nameof(this.MegapixelsPerSecPerCpu))} |"); bld.Append("| "); - bld.AppendFormat(F(nameof(this.TotalSeconds)), this.TotalSeconds); + bld.AppendFormat(CultureInfo.InvariantCulture, F(nameof(this.TotalSeconds)), this.TotalSeconds); bld.Append(" | "); - bld.AppendFormat(F(nameof(this.MegapixelsPerSec)), this.MegapixelsPerSec); + bld.AppendFormat(CultureInfo.InvariantCulture, F(nameof(this.MegapixelsPerSec)), this.MegapixelsPerSec); bld.Append(" | "); - bld.AppendFormat(F(nameof(this.MegapixelsPerSecPerCpu)), this.MegapixelsPerSecPerCpu); + bld.AppendFormat(CultureInfo.InvariantCulture, F(nameof(this.MegapixelsPerSecPerCpu)), this.MegapixelsPerSecPerCpu); bld.AppendLine(" |"); return bld.ToString(); @@ -223,7 +226,7 @@ public string GetMarkdown() } } - private class CommandLineOptions + private sealed class CommandLineOptions { [Option('a', "async-imagesharp", Required = false, Default = false, HelpText = "Async ImageSharp without benchmark switching")] public bool AsyncImageSharp { get; set; } diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ProcessorThroughputBenchmark.cs b/tests/ImageSharp.Tests.ProfilingSandbox/ProcessorThroughputBenchmark.cs new file mode 100644 index 0000000000..e85a3ee972 --- /dev/null +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ProcessorThroughputBenchmark.cs @@ -0,0 +1,262 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using CommandLine; +using CommandLine.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Tests.ProfilingSandbox; + +public sealed class ProcessorThroughputBenchmark +{ + private readonly CommandLineOptions options; + private readonly Configuration configuration; + private ulong totalProcessedPixels; + + private ProcessorThroughputBenchmark(CommandLineOptions options) + { + this.options = options; + this.configuration = Configuration.Default.Clone(); + this.configuration.MaxDegreeOfParallelism = options.ProcessorParallelism > 0 + ? options.ProcessorParallelism + : Environment.ProcessorCount; + } + + public static Task RunAsync(string[] args) + { + CommandLineOptions options = null; + if (args.Length > 0) + { + options = CommandLineOptions.Parse(args); + if (options == null) + { + return Task.CompletedTask; + } + } + + options ??= new CommandLineOptions(); + return new ProcessorThroughputBenchmark(options.Normalize()) + .RunAsync(); + } + + private async Task RunAsync() + { + SemaphoreSlim semaphore = new(this.options.ConcurrentRequests); + Console.WriteLine(this.options.Method); + Func action = this.options.Method switch + { + Method.Crop => this.Crop, + Method.Edges => this.DetectEdges, + Method.EdgesCompass => this.EdgesCompass, + Method.DrawImage => this.DrawImage, + Method.BinaryThreshold => this.BinaryThreshold, + Method.Histogram => this.Histogram, + Method.OilPaint => this.OilPaint, + Method.GaussianBlur => this.GaussianBlur, + _ => throw new NotImplementedException(), + }; + + Console.WriteLine(this.options); + Console.WriteLine($"Running {this.options.Method} for {this.options.Seconds} seconds ..."); + TimeSpan runFor = TimeSpan.FromSeconds(this.options.Seconds); + + // inFlight starts at 1 to represent the dispatch loop itself + int inFlight = 1; + TaskCompletionSource drainTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); + + Stopwatch stopwatch = Stopwatch.StartNew(); + while (stopwatch.Elapsed < runFor && !drainTcs.Task.IsCompleted) + { + await semaphore.WaitAsync(); + + if (stopwatch.Elapsed >= runFor) + { + semaphore.Release(); + break; + } + + Interlocked.Increment(ref inFlight); + + _ = ProcessImage(); + + async Task ProcessImage() + { + try + { + if (stopwatch.Elapsed >= runFor || drainTcs.Task.IsCompleted) + { + return; + } + + await Task.Yield(); // "emulate IO", i.e., make sure the processing code is async + ulong pixels = (ulong)action(); + Interlocked.Add(ref this.totalProcessedPixels, pixels); + } + catch (Exception ex) + { + Console.WriteLine(ex); + drainTcs.TrySetException(ex); + } + finally + { + semaphore.Release(); + if (Interlocked.Decrement(ref inFlight) == 0) + { + drainTcs.TrySetResult(); + } + } + } + } + + // Release the dispatch loop's own count; if no work is in flight, this completes immediately + if (Interlocked.Decrement(ref inFlight) == 0) + { + drainTcs.TrySetResult(); + } + + await drainTcs.Task; + stopwatch.Stop(); + + double totalMegaPixels = this.totalProcessedPixels / 1_000_000.0; + double totalSeconds = stopwatch.ElapsedMilliseconds / 1000.0; + double megapixelsPerSec = totalMegaPixels / totalSeconds; + Console.WriteLine($"TotalSeconds: {totalSeconds:F2}"); + Console.WriteLine($"MegaPixelsPerSec: {megapixelsPerSec:F2}"); + } + + private int OilPaint() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, x => x.OilPaint()); + return image.Width * image.Height; + } + + private int DetectEdges() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, x => x.DetectEdges()); + return image.Width * image.Height; + } + + private int EdgesCompass() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, x => x.DetectEdges(EdgeDetectorCompassKernel.Kirsch)); + return image.Width * image.Height; + } + + private int Crop() + { + using Image image = new(this.options.Width, this.options.Height); + Rectangle bounds = image.Bounds; + bounds = new Rectangle(1, 1, bounds.Width - 2, bounds.Height - 2); + image.Clone(this.configuration, x => x.Crop(bounds)).Dispose(); + return image.Width * image.Height; + } + + private int DrawImage() + { + using Image image = new(this.options.Width, this.options.Height); + using Image foreground = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, c => c.DrawImage(foreground, 0.5f)); + return image.Width * image.Height; + } + + private int BinaryThreshold() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, c => c.BinaryThreshold(0.5f)); + return image.Width * image.Height; + } + + private int Histogram() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, c => c.HistogramEqualization()); + return image.Width * image.Height; + } + + private int GaussianBlur() + { + using Image image = new(this.options.Width, this.options.Height); + image.Mutate(this.configuration, c => c.GaussianBlur()); + return image.Width * image.Height; + } + + private enum Method + { + Edges, + EdgesCompass, + Crop, + DrawImage, + BinaryThreshold, + Histogram, + OilPaint, + GaussianBlur, + } + + private sealed class CommandLineOptions + { + [Option('m', "method", Required = false, Default = Method.Edges, HelpText = "The stress test method to run (Edges, Crop)")] + public Method Method { get; set; } = Method.Edges; + + [Option('p', "processor-parallelism", Required = false, Default = -1, HelpText = "Level of parallelism for the image processor")] + public int ProcessorParallelism { get; set; } = -1; + + [Option('c', "concurrent-requests", Required = false, Default = -1, HelpText = "Number of concurrent in-flight requests")] + public int ConcurrentRequests { get; set; } = -1; + + [Option('w', "width", Required = false, Default = 4000, HelpText = "Width of the test image")] + public int Width { get; set; } = 4000; + + [Option('h', "height", Required = false, Default = 4000, HelpText = "Height of the test image")] + public int Height { get; set; } = 4000; + + [Option('s', "seconds", Required = false, Default = 5, HelpText = "Duration of the stress test in seconds")] + public int Seconds { get; set; } = 5; + + public override string ToString() => string.Join( + "|", + $"method: {this.Method}", + $"processor-parallelism: {this.ProcessorParallelism}", + $"concurrent-requests: {this.ConcurrentRequests}", + $"width: {this.Width}", + $"height: {this.Height}", + $"seconds: {this.Seconds}"); + + public CommandLineOptions Normalize() + { + if (this.ProcessorParallelism < 0) + { + this.ProcessorParallelism = Environment.ProcessorCount; + } + + if (this.ConcurrentRequests < 0) + { + this.ConcurrentRequests = Environment.ProcessorCount; + } + + return this; + } + + public static CommandLineOptions Parse(string[] args) + { + CommandLineOptions result = null; + using Parser parser = new(settings => settings.CaseInsensitiveEnumValues = true); + ParserResult parserResult = parser.ParseArguments(args).WithParsed(o => + { + result = o; + }); + + if (result == null) + { + Console.WriteLine(HelpText.RenderUsageText(parserResult)); + } + + return result; + } + } +} diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs index b5c8b70cde..db8892cd75 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs @@ -1,76 +1,38 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Reflection; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; +using SixLabors.ImageSharp.Tests.ProfilingSandbox; using Xunit.Abstractions; // in this file, comments are used for disabling stuff for local execution #pragma warning disable SA1515 #pragma warning disable SA1512 -namespace SixLabors.ImageSharp.Tests.ProfilingSandbox; +// LoadResizeSaveParallelMemoryStress.Run(args); +// ParallelProcessingStress.RunExperiment(args); +// ParallelProcessingStress.Run(args); +await ProcessorThroughputBenchmark.RunAsync(args); -public class Program -{ - private class ConsoleOutput : ITestOutputHelper - { - public void WriteLine(string message) => Console.WriteLine(message); - - public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args); - } - - /// - /// The main entry point. Useful for executing benchmarks and performance unit tests manually, - /// when the IDE test runners lack some of the functionality. Eg.: it's not possible to run JetBrains memory profiler for unit tests. - /// - /// - /// The arguments to pass to the program. - /// - public static void Main(string[] args) - { - try - { - LoadResizeSaveParallelMemoryStress.Run(args); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - - // RunJpegEncoderProfilingTests(); - // RunJpegColorProfilingTests(); - // RunDecodeJpegProfilingTests(); - // RunToVector4ProfilingTest(); - // RunResizeProfilingTest(); - - // Console.ReadLine(); - } +// RunToVector4ProfilingTest(); +// RunResizeProfilingTest(); - private static Version GetNetCoreVersion() - { - Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; - Console.WriteLine(assembly.Location); - string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); - int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); - if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) - { - return Version.Parse(assemblyPath[netCoreAppIndex + 1]); - } +static void RunResizeProfilingTest() +{ + ResizeProfilingBenchmarks test = new(new ConsoleOutput()); + test.ResizeBicubic(4000, 4000); +} - return null; - } +static void RunToVector4ProfilingTest() +{ + PixelOperationsTests.Rgba32_OperationsTests tests = new(new ConsoleOutput()); + tests.Benchmark_ToVector4(); +} - private static void RunResizeProfilingTest() - { - var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); - test.ResizeBicubic(4000, 4000); - } +sealed class ConsoleOutput : ITestOutputHelper +{ + public void WriteLine(string message) => Console.WriteLine(message); - private static void RunToVector4ProfilingTest() - { - var tests = new PixelOperationsTests.Rgba32_OperationsTests(new ConsoleOutput()); - tests.Benchmark_ToVector4(); - } + public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args); } diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs index 4091e0cd40..8897464539 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs @@ -12,10 +12,10 @@ public class CastFrom [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,23 +25,37 @@ public void Rgba64() [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(); Assert.Equal(source, data); } + [Fact] + public void Rgba32P() + { + Rgba32P source = new(64, 32, 16, 128); + + // Act: + Color color = Color.FromPixel(source); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation); + Assert.Equal(source.ToScaledVector4(), color.ToScaledVector4()); + Assert.Equal(source, color.ToPixel()); + } + [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 +65,10 @@ public void Argb32() [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 +78,10 @@ public void Bgra32() [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 +91,10 @@ public void Abgr32() [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 +104,10 @@ public void Rgb24() [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 dacec71449..66537007fb 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -13,91 +13,91 @@ public class CastTo [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; + Rgba64 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Rgba32 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Argb32 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Bgra32 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Abgr32 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Rgb24 data = color.ToPixel(); Assert.Equal(source, data); } [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; + Bgr24 data = color.ToPixel(); Assert.Equal(source, data); } @@ -105,7 +105,7 @@ public void Bgr24() public void Vector4Constructor() { // Act: - Color color = new(Vector4.One); + Color color = Color.FromScaledVector(Vector4.One); // Assert: Assert.Equal(new RgbaVector(1, 1, 1, 1), color.ToPixel()); @@ -114,10 +114,69 @@ public void Vector4Constructor() Assert.Equal(new L8(255), color.ToPixel()); } + [Fact] + public void AssociatedVectorConstructor() + { + Rgba32P expected = new(64, 32, 16, 128); + + // Act: + Color color = Color.FromScaledVector(expected.ToScaledVector4(), PixelAlphaRepresentation.Associated); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation); + Assert.Equal(expected.ToScaledVector4(), color.ToScaledVector4()); + Assert.Equal(expected, color.ToPixel()); + Assert.Equal(new Rgba32(128, 64, 32, 128), color.ToPixel()); + } + + [Fact] + public void UnassociatedPixelToAssociatedPixel() + { + Color color = Color.FromPixel(new Rgba32(128, 64, 32, 128)); + + // Act: + Rgba32P actual = color.ToPixel(); + + // Assert: + Assert.Equal(new Rgba32P(64, 32, 16, 128), actual); + } + + [Fact] + public void AssociatedVectorSpanConstructor() + { + Rgba32P expected = new(64, 32, 16, 128); + Vector4[] source = [expected.ToScaledVector4()]; + Color[] destination = new Color[source.Length]; + + // Act: + Color.FromScaledVector(source, destination, PixelAlphaRepresentation.Associated); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, destination[0].AlphaRepresentation); + Assert.Equal(expected.ToScaledVector4(), destination[0].ToScaledVector4()); + Assert.Equal(expected, destination[0].ToPixel()); + } + + [Fact] + public void AssociatedPixelSpanRoundTrip() + { + Rgba32P[] source = [new(64, 32, 16, 128), new(24, 12, 6, 96)]; + Color[] colors = new Color[source.Length]; + Rgba32P[] destination = new Rgba32P[source.Length]; + + // Act: + Color.FromPixel(source, colors); + Color.ToPixel(colors, destination); + + // Assert: + Assert.Equal(source, destination); + Assert.All(colors, color => Assert.Equal(PixelAlphaRepresentation.Associated, color.AlphaRepresentation)); + } + [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)); @@ -129,7 +188,7 @@ private static void AssertGenericPixelRoundTrip(TPixel source) where TPixel : unmanaged, IPixel { // Act: - var color = Color.FromPixel(source); + Color color = Color.FromPixel(source); // Assert: TPixel actual = color.ToPixel(); @@ -150,7 +209,7 @@ private static void AssertGenericPixelDifferentPrecision(TPixel where TPixel2 : unmanaged, IPixel { // Act: - var color = Color.FromPixel(source); + Color color = Color.FromPixel(source); // Assert: TPixel2 actual = color.ToPixel(); diff --git a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs index 3ea8623d94..9126543e55 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs @@ -12,10 +12,10 @@ public class ConstructFrom [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 void Rgba64() [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 void Rgba32() [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 void Argb32() [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 void Bgra32() [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 void Abgr32() [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 void Rgb24() [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 f7e2092176..6c205888af 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; @@ -10,12 +11,64 @@ 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()); + } + + [Fact] + public void WithAlphaPreservesAssociatedRepresentation() + { + Color color = Color.FromPixel(new Rgba32P(64, 32, 16, 128)); + + // Act: + Color actual = color.WithAlpha(64F / 255F); + + // Assert: + Assert.Equal(PixelAlphaRepresentation.Associated, actual.AlphaRepresentation); + Assert.Equal(new Rgba32P(32, 16, 8, 64), actual.ToPixel()); + Assert.Equal(new Rgba32(128, 64, 32, 64), actual.ToPixel()); + } + + [Fact] + public void ToHexConvertsAssociatedRepresentation() + { + Color color = Color.FromPixel(new Rgba32P(64, 32, 16, 128)); + + Assert.Equal("80402080", color.ToHex()); + } + + [Fact] + public void ToScaledVector4ConvertsUnassociatedToAssociated() + { + Color color = Color.FromScaledVector(new Vector4(1F, 0.5F, 0.25F, 0.5F)); + + Vector4 actual = color.ToScaledVector4(PixelAlphaRepresentation.Associated); + + Assert.Equal(new Vector4(0.5F, 0.25F, 0.125F, 0.5F), actual); + } + + [Fact] + public void ToScaledVector4ConvertsAssociatedToUnassociated() + { + Color color = Color.FromScaledVector(new Vector4(0.5F, 0.25F, 0.125F, 0.5F), PixelAlphaRepresentation.Associated); + + Vector4 actual = color.ToScaledVector4(PixelAlphaRepresentation.Unassociated); + + Assert.Equal(new Vector4(1F, 0.5F, 0.25F, 0.5F), actual); + } + + [Fact] + public void EqualityDoesNotDependOnInternalAssociationStorage() + { + Color fromPixel = Color.FromPixel(new Rgba32P(64, 32, 16, 128)); + Color fromVector = Color.FromScaledVector(fromPixel.ToScaledVector4(), PixelAlphaRepresentation.Associated); + + Assert.Equal(fromPixel, fromVector); + Assert.Equal(fromPixel.GetHashCode(), fromVector.GetHashCode()); } [Theory] @@ -23,13 +76,13 @@ public void WithAlpha() [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 +96,15 @@ public void Equality_WhenTrue(bool highPrecision) [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)); @@ -67,107 +120,189 @@ public void Equality_WhenFalse(bool highPrecision) [Theory] [InlineData(false)] [InlineData(true)] - public void ToHex(bool highPrecision) + public void ToHexRgba(bool highPrecision) { - string expected = "ABCD1234"; + const string expected = "AABBCCDD"; Color color = Color.ParseHex(expected); if (highPrecision) { - color = Color.FromPixel(color.ToPixel()); + color = Color.FromPixel(color.ToPixel()); } string actual = color.ToHex(); Assert.Equal(expected, actual); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ToHexArgb(bool highPrecision) + { + const string expected = "AABBCCDD"; + Color color = Color.ParseHex(expected, ColorHexFormat.Argb); + + if (highPrecision) + { + color = Color.FromPixel(color.ToPixel()); + } + + string actual = color.ToHex(ColorHexFormat.Argb); + Assert.Equal(expected, actual); + } + [Fact] public void WebSafePalette_IsCorrect() { - Rgba32[] actualPalette = Color.WebSafePalette.ToArray().Select(c => (Rgba32)c).ToArray(); + Rgba32[] actualPalette = [.. Color.WebSafePalette.ToArray().Select(c => c.ToPixel())]; 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())]; 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]); } } - public class FromHex + public class FromHexRgba { [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() + public void LongHex() { - 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 Rgba32(255, 255, 255, 0), Color.ParseHex("#FFFFFF00").ToPixel()); + Assert.Equal(new Rgba32(255, 255, 255, 128), Color.ParseHex("#FFFFFF80").ToPixel()); } [Fact] - public void ThrowsOnEmpty() + public void TryLongHex() { - Assert.Throws(() => Color.ParseHex(string.Empty)); + Assert.True(Color.TryParseHex("#FFFFFF00", out Color actual)); + Assert.Equal(new Rgba32(255, 255, 255, 0), actual.ToPixel()); + + Assert.True(Color.TryParseHex("#FFFFFF80", out actual)); + Assert.Equal(new Rgba32(255, 255, 255, 128), actual.ToPixel()); } [Fact] - public void ThrowsOnInvalid() + public void LeadingPoundIsOptional() { - Assert.Throws(() => Color.ParseHex("!")); + 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 ThrowsOnNull() + public void ThrowsOnEmpty() => Assert.Throws(() => Color.ParseHex(string.Empty)); + + [Fact] + public void ThrowsOnInvalid() => Assert.Throws(() => Color.ParseHex("!")); + + [Fact] + public void ThrowsOnNull() => Assert.Throws(() => Color.ParseHex(null)); + + [Fact] + public void FalseOnEmpty() => Assert.False(Color.TryParseHex(string.Empty, out Color _)); + + [Fact] + public void FalseOnInvalid() => Assert.False(Color.TryParseHex("!", out Color _)); + + [Fact] + public void FalseOnNull() => Assert.False(Color.TryParseHex(null, out Color _)); + } + + public class FromHexArgb + { + [Fact] + public void ShortHex() { - Assert.Throws(() => Color.ParseHex(null)); + Assert.Equal(new Rgb24(255, 255, 255), Color.ParseHex("#fff", ColorHexFormat.Argb).ToPixel()); + Assert.Equal(new Rgb24(255, 255, 255), Color.ParseHex("fff", ColorHexFormat.Argb).ToPixel()); + Assert.Equal(new Argb32(0, 0, 255, 0), Color.ParseHex("000f", ColorHexFormat.Argb).ToPixel()); } [Fact] - public void FalseOnEmpty() + public void TryShortHex() { - Assert.False(Color.TryParseHex(string.Empty, out Color _)); + Assert.True(Color.TryParseHex("#fff", out Color actual, ColorHexFormat.Argb)); + Assert.Equal(new Rgb24(255, 255, 255), actual.ToPixel()); + + Assert.True(Color.TryParseHex("fff", out actual, ColorHexFormat.Argb)); + Assert.Equal(new Rgb24(255, 255, 255), actual.ToPixel()); + + Assert.True(Color.TryParseHex("000f", out actual, ColorHexFormat.Argb)); + Assert.Equal(new Argb32(0, 0, 255, 0), actual.ToPixel()); } [Fact] - public void FalseOnInvalid() + public void LongHex() { - Assert.False(Color.TryParseHex("!", out Color _)); + Assert.Equal(new Argb32(255, 255, 255, 0), Color.ParseHex("#00FFFFFF", ColorHexFormat.Argb).ToPixel()); + Assert.Equal(new Argb32(255, 255, 255, 128), Color.ParseHex("#80FFFFFF", ColorHexFormat.Argb).ToPixel()); } [Fact] - public void FalseOnNull() + public void TryLongHex() { - Assert.False(Color.TryParseHex(null, out Color _)); + Assert.True(Color.TryParseHex("#00FFFFFF", out Color actual, ColorHexFormat.Argb)); + Assert.Equal(new Argb32(255, 255, 255, 0), actual.ToPixel()); + + Assert.True(Color.TryParseHex("#80FFFFFF", out actual, ColorHexFormat.Argb)); + Assert.Equal(new Argb32(255, 255, 255, 128), actual.ToPixel()); } + + [Fact] + public void LeadingPoundIsOptional() + { + Assert.Equal(new Rgb24(0, 128, 128), Color.ParseHex("#008080", ColorHexFormat.Argb).ToPixel()); + Assert.Equal(new Rgb24(0, 128, 128), Color.ParseHex("008080", ColorHexFormat.Argb).ToPixel()); + } + + [Fact] + public void ThrowsOnEmpty() => Assert.Throws(() => Color.ParseHex(string.Empty, ColorHexFormat.Argb)); + + [Fact] + public void ThrowsOnInvalid() => Assert.Throws(() => Color.ParseHex("!", ColorHexFormat.Argb)); + + [Fact] + public void ThrowsOnNull() => Assert.Throws(() => Color.ParseHex(null, ColorHexFormat.Argb)); + + [Fact] + public void FalseOnEmpty() => Assert.False(Color.TryParseHex(string.Empty, out Color _, ColorHexFormat.Argb)); + + [Fact] + public void FalseOnInvalid() => Assert.False(Color.TryParseHex("!", out Color _, ColorHexFormat.Argb)); + + [Fact] + public void FalseOnNull() => Assert.False(Color.TryParseHex(null, out Color _, ColorHexFormat.Argb)); } public class FromString @@ -177,10 +312,10 @@ public void ColorNames() { 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 +324,35 @@ public void TryColorNames() { 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/ReferencePalette.cs b/tests/ImageSharp.Tests/Color/ReferencePalette.cs index 8e26961092..88787afd4f 100644 --- a/tests/ImageSharp.Tests/Color/ReferencePalette.cs +++ b/tests/ImageSharp.Tests/Color/ReferencePalette.cs @@ -9,7 +9,7 @@ internal static class ReferencePalette /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. /// public static readonly Color[] WebSafeColors = - { + [ Color.AliceBlue, Color.AntiqueWhite, Color.Aqua, @@ -152,14 +152,14 @@ internal static class ReferencePalette Color.WhiteSmoke, Color.Yellow, Color.YellowGreen - }; + ]; /// /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. /// The hex codes were collected and defined by Nicholas Rougeux /// public static readonly Color[] WernerColors = - { + [ Color.ParseHex("#f1e9cd"), Color.ParseHex("#f2e7cf"), Color.ParseHex("#ece6d0"), @@ -270,10 +270,10 @@ internal static class ReferencePalette Color.ParseHex("#9b856b"), Color.ParseHex("#766051"), Color.ParseHex("#453b32") - }; + ]; public static readonly Dictionary ColorNames = - new Dictionary(StringComparer.OrdinalIgnoreCase) + new(StringComparer.OrdinalIgnoreCase) { { nameof(Color.AliceBlue), Color.AliceBlue }, { nameof(Color.AntiqueWhite), Color.AntiqueWhite }, diff --git a/tests/ImageSharp.Tests/Color/RgbaDouble.cs b/tests/ImageSharp.Tests/Color/RgbaDouble.cs new file mode 100644 index 0000000000..2487d5fdef --- /dev/null +++ b/tests/ImageSharp.Tests/Color/RgbaDouble.cs @@ -0,0 +1,230 @@ +// 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. +/// +[StructLayout(LayoutKind.Sequential)] +public struct RgbaDouble : IPixel +{ + /// + /// Gets or sets the red component. + /// + public double R; + + /// + /// Gets or sets the green component. + /// + public double G; + + /// + /// Gets or sets the blue component. + /// + public double B; + + /// + /// Gets or sets the alpha component. + /// + 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. + /// + /// 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); + + /// + [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( + PixelComponentInfo.Create(4, 64, 64, 64, 64), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Unassociated); + + /// + public static PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() + { + Vector4 vector = this.ToScaledVector4(); + Numerics.Premultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromAssociatedScaledVector4(Vector4 source) + { + Numerics.UnPremultiply(ref source); + return FromScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromUnassociatedVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromScaledVector4(Vector4 source) => FromVector4(source); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + return new RgbaDouble(source.X, source.Y, source.Z, source.W); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromAbgr32(Abgr32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromArgb32(Argb32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgra5551(Bgra5551 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgr24(Bgr24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromBgra32(Bgra32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromL8(L8 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromL16(L16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromLa16(La16 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromLa32(La32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgb24(Rgb24 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgba32(Rgba32 source) => FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaDouble FromRgb48(Rgb48 source) => 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); + + /// + [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/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs new file mode 100644 index 0000000000..f4078887c7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -0,0 +1,102 @@ +// 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, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + 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 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 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 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 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 bool Equals(Y x, Y y) => this.Equals(x.L, y.L); + + public bool Equals(YccK x, YccK y) => this.Equals(x.Y, y.Y) && this.Equals(x.Cb, y.Cb) && this.Equals(x.Cr, y.Cr) && this.Equals(x.K, y.K); + + 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(); + + public int GetHashCode([DisallowNull] Rgb obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] YCbCr obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieLchuv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieLuv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieXyy obj) => obj.GetHashCode(); + + 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(); + + public int GetHashCode([DisallowNull] Y obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] YccK 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/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs new file mode 100644 index 0000000000..d6e3738952 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.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; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +[Trait("Color", "Conversion")] +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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs new file mode 100644 index 0000000000..73fa7128fa --- /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; + +/// +/// 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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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 0000000000..0846bdda3f --- /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; + +/// +/// 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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..a464eeca11 --- /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; + +/// +/// 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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs new file mode 100644 index 0000000000..746671c4a7 --- /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; + +/// +/// 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/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs new file mode 100644 index 0000000000..96779f1896 --- /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 0000000000..3389da9815 --- /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 0000000000..1054d537ac --- /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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs new file mode 100644 index 0000000000..77243268fd --- /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/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs new file mode 100644 index 0000000000..0a0453bc62 --- /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; + +/// +/// 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/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..15677c46f0 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.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 CieLabAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(1, .5F, .5F, 100, 0, 0)] + [InlineData(0, .5F, .5F, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, 53.38897F, 0, 0)] + 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(100, 0, 0, 1, .5F, .5F)] + [InlineData(0, 0, 0, 0, .5F, .5F)] + [InlineData(53.38897F, 0, 0, .5F, .5F, .5F)] + 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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs new file mode 100644 index 0000000000..69fabc7508 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs @@ -0,0 +1,45 @@ +// 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. +/// +[Trait("Color", "Conversion")] +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/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs new file mode 100644 index 0000000000..12313281fa --- /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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..94f5515bff --- /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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs new file mode 100644 index 0000000000..484db3e8cf --- /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/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs new file mode 100644 index 0000000000..857bdb3da1 --- /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; + +/// +/// 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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs new file mode 100644 index 0000000000..424cb8cc77 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -0,0 +1,90 @@ +// 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: +/// +/// +[Trait("Color", "Conversion")] +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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs new file mode 100644 index 0000000000..3c8a93ee12 --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs new file mode 100644 index 0000000000..3fe550a5ba --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.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. +/// +[Trait("Color", "Conversion")] +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/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..08e73a1a71 --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs new file mode 100644 index 0000000000..bfcd236c75 --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs new file mode 100644 index 0000000000..8a25f95b7b --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..1c667f6794 --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs new file mode 100644 index 0000000000..812b2b61e5 --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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 0000000000..1af802326e --- /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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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 0000000000..62b276a1f1 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.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. +/// +public class CieLuvAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(100, 0, 0, 1, .5F, .5F)] + [InlineData(0, 0, 0, 0, .5F, .5F)] + [InlineData(53.38897F, 0, 0, .5F, .5F, .5F)] + 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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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(1, .5F, .5F, 100, 0, 0)] + [InlineData(0, .5F, .5F, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, 53.38897F, 0, 0)] + 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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs new file mode 100644 index 0000000000..173491081d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.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. +/// +[Trait("Color", "Conversion")] +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 0000000000..8bc71f1e18 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.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; + +/// +/// Tests the struct. +/// +[Trait("Color", "Conversion")] +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/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs new file mode 100644 index 0000000000..3e93206ce4 --- /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 0000000000..c2547ca847 --- /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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..4f66538f0f --- /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 0000000000..1a89cb26dc --- /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; + +/// +/// 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 0000000000..18df2ce145 --- /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 0000000000..f9d571e036 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.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 CieXyyAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(.34566915F, .358496159F, .99999994F, 1, .5F, .5F)] + [InlineData(0, 0, 0, 0, .5F, .5F)] + [InlineData(.34566915F, .358496159F, .214041144F, .5F, .5F, .5F)] + 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(1, .5F, .5F, .34566915F, .358496159F, .99999994F)] + [InlineData(0, .5F, .5F, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, .34566915F, .358496159F, .214041144F)] + 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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs new file mode 100644 index 0000000000..80904c5df1 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.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. +/// +[Trait("Color", "Conversion")] +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/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs new file mode 100644 index 0000000000..76fceec413 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -0,0 +1,87 @@ +// 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: +/// +/// +[Trait("Color", "Conversion")] +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.08712, 0.01126, -0.00192)] + 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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs new file mode 100644 index 0000000000..bde3b8e17b --- /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/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs new file mode 100644 index 0000000000..fa604250f2 --- /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 0000000000..b269818ae8 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -0,0 +1,87 @@ +// 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.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 + CieXyz input = new(x, y, z); + CieLuv expected = new(l, u, v); + + ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieXyz[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(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() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs new file mode 100644 index 0000000000..48bb6c1e16 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.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; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +[Trait("Color", "Conversion")] +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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs new file mode 100644 index 0000000000..cffdb008b8 --- /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 0000000000..d4a0022a47 --- /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 0000000000..aef26fe9a3 --- /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 new file mode 100644 index 0000000000..c7898904da --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs @@ -0,0 +1,82 @@ +// 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. +/// +[Trait("Color", "Conversion")] +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/ColorProfiles/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..90175a0585 --- /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, .5F, .5F)] + [InlineData(.206382737F, .214041144F, .176628917F, .5F, .5F, .5F)] + 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, .5F, .5F, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, .206382737F, .214041144F, .176628917F)] + 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/CieXyzTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs new file mode 100644 index 0000000000..683b3b6611 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.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. +/// +[Trait("Color", "Conversion")] +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/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs new file mode 100644 index 0000000000..a5230eb312 --- /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 0000000000..cfbd080541 --- /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 new file mode 100644 index 0000000000..3a5fe7c15c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.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 CmykAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 1, 0, .5F, .5F)] + [InlineData(0, 0, 0, 0, 1, .5F, .5F)] + [InlineData(0, .8570679F, .49999997F, 0, .439901F, .5339159F, .899500132F)] + 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(0, .5F, .5F, 0, 0, 0, 1)] + [InlineData(1, .5F, .5F, 0, 0, 0, 0)] + [InlineData(.5F, .5F, 1F, 0, .8570679F, .49999997F, 0)] + 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/CmykTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs new file mode 100644 index 0000000000..22b7a7f70c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs @@ -0,0 +1,45 @@ +// 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. +/// +[Trait("Color", "Conversion")] +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/ColorProfileConverterChomaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs new file mode 100644 index 0000000000..525220d8e0 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs @@ -0,0 +1,203 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +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() + { + SourceRgbWorkingSpace = 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() + { + SourceRgbWorkingSpace = 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() + { + SourceWhitePoint = 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() + { + SourceWhitePoint = 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() + { + SourceWhitePoint = 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() + { + SourceWhitePoint = 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() + { + SourceWhitePoint = 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() + { + SourceWhitePoint = 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/CompandingTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs new file mode 100644 index 0000000000..1bdefa1095 --- /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/HslTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs new file mode 100644 index 0000000000..6697cbfde2 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HslTests.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. +/// +[Trait("Color", "Conversion")] +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 0000000000..dd71fcd3ff --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HsvTests.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. +/// +[Trait("Color", "Conversion")] +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 0000000000..af06b3c91f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.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. +/// +[Trait("Color", "Conversion")] +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/Icc/Calculators/ClutCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/ClutCalculatorTests.cs new file mode 100644 index 0000000000..249e7f4ed1 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/ClutCalculatorTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class ClutCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataClut.ClutConversionTestData), MemberType = typeof(IccConversionDataClut))] + internal void ClutCalculator_WithClut_ReturnsResult(IccClut lut, Vector4 input, Vector4 expected) + { + ClutCalculator calculator = new(lut); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/CurveCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/CurveCalculatorTests.cs new file mode 100644 index 0000000000..8f48277d6c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/CurveCalculatorTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles.Conversion.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class CurveCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataTrc.CurveConversionTestData), MemberType = typeof(IccConversionDataTrc))] + internal void CurveCalculator_WithCurveEntry_ReturnsResult(IccCurveTagDataEntry curve, bool inverted, float input, float expected) + { + CurveCalculator calculator = new(curve, inverted); + + float result = calculator.Calculate(input); + + Assert.Equal(expected, result, 4f); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutABCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutABCalculatorTests.cs new file mode 100644 index 0000000000..de2b4f5fae --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutABCalculatorTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Conversion.Icc; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class LutABCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataLutAB.LutAToBConversionTestData), MemberType = typeof(IccConversionDataLutAB))] + internal void LutABCalculator_WithLutAToB_ReturnsResult(IccLutAToBTagDataEntry lut, Vector4 input, Vector4 expected) + { + LutABCalculator calculator = new(lut); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } + + [Theory] + [MemberData(nameof(IccConversionDataLutAB.LutBToAConversionTestData), MemberType = typeof(IccConversionDataLutAB))] + internal void LutABCalculator_WithLutBToA_ReturnsResult(IccLutBToATagDataEntry lut, Vector4 input, Vector4 expected) + { + LutABCalculator calculator = new(lut); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutCalculatorTests.cs new file mode 100644 index 0000000000..6cc77247a9 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutCalculatorTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class LutCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataLut.LutConversionTestData), MemberType = typeof(IccConversionDataLut))] + internal void LutCalculator_WithLut_ReturnsResult(float[] lut, bool inverted, float input, float expected) + { + LutCalculator calculator = new(lut, inverted); + + float result = calculator.Calculate(input); + + Assert.Equal(expected, result, 4f); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutEntryCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutEntryCalculatorTests.cs new file mode 100644 index 0000000000..14f1386eb8 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutEntryCalculatorTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class LutEntryCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataLutEntry.Lut8ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))] + internal void LutEntryCalculator_WithLut8_ReturnsResult(IccLut8TagDataEntry lut, Vector4 input, Vector4 expected) + { + LutEntryCalculator calculator = new(lut); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } + + [Theory] + [MemberData(nameof(IccConversionDataLutEntry.Lut16ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))] + internal void LutEntryCalculator_WithLut16_ReturnsResult(IccLut16TagDataEntry lut, Vector4 input, Vector4 expected) + { + LutEntryCalculator calculator = new(lut); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/MatrixCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/MatrixCalculatorTests.cs new file mode 100644 index 0000000000..f56bf5873e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/MatrixCalculatorTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class MatrixCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataMatrix.MatrixConversionTestData), MemberType = typeof(IccConversionDataMatrix))] + internal void MatrixCalculator_WithMatrix_ReturnsResult(Matrix4x4 matrix2D, Vector3 matrix1D, Vector4 input, Vector4 expected) + { + MatrixCalculator calculator = new(matrix2D, matrix1D); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/ParametricCurveCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/ParametricCurveCalculatorTests.cs new file mode 100644 index 0000000000..aac3f42c9b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/ParametricCurveCalculatorTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class ParametricCurveCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataTrc.ParametricCurveConversionTestData), MemberType = typeof(IccConversionDataTrc))] + internal void ParametricCurveCalculator_WithCurveEntry_ReturnsResult(IccParametricCurveTagDataEntry curve, bool inverted, float input, float expected) + { + ParametricCurveCalculator calculator = new(curve, inverted); + + float result = calculator.Calculate(input); + + Assert.Equal(expected, result, 4f); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/TrcCalculatorTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/TrcCalculatorTests.cs new file mode 100644 index 0000000000..65f02c3fb7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/TrcCalculatorTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Icc.Calculators; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators; + +/// +/// Tests ICC +/// +[Trait("Color", "Conversion")] +public class TrcCalculatorTests +{ + [Theory] + [MemberData(nameof(IccConversionDataTrc.TrcArrayConversionTestData), MemberType = typeof(IccConversionDataTrc))] + internal void TrcCalculator_WithCurvesArray_ReturnsResult(IccTagDataEntry[] entries, bool inverted, Vector4 input, Vector4 expected) + { + TrcCalculator calculator = new(entries, inverted); + + Vector4 result = calculator.Calculate(input); + + VectorAssert.Equal(expected, result, 4); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs new file mode 100644 index 0000000000..cb349af96a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs @@ -0,0 +1,263 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using Wacton.Unicolour; +using Wacton.Unicolour.Icc; +using Xunit.Abstractions; +using Rgb = SixLabors.ImageSharp.ColorProfiles.Rgb; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc; + +public class ColorProfileConverterTests(ITestOutputHelper testOutputHelper) +{ + // for 3-channel spaces, 4th item is ignored + private static readonly List Inputs = + [ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [1, 1, 1, 1], + [0.5f, 0.5f, 0.5f, 0.5f], + [0.199678659f, 0.67982769f, 0.805381715f, 0.982666492f], // requires clipping before source is PCS adjusted for Fogra39 -> sRGBv2 + [0.776568174f, 0.961630166f, 0.31032759f, 0.895294666f], // requires clipping after target is PCS adjusted for Fogra39 -> sRGBv2 + [GetNormalizedRandomValue(), GetNormalizedRandomValue(), GetNormalizedRandomValue(), GetNormalizedRandomValue()] + ]; + + [Theory] + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.JapanColor2003)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 8-bit vs 16-bit) + [InlineData(TestIccProfiles.JapanColor2011, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Cgats21)] // CMYK -> LAB -> RGB (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV4)] // RGB -> LAB -> CMYK (different LUT versions, v4 vs v2) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.Fogra39)] // RGB -> LAB -> XYZ -> RGB (different LUT elements, B-Matrix-M-CLUT-A vs B-Matrix-M) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.RommRgb)] // RGB -> XYZ -> LAB -> RGB (different LUT elements, B-Matrix-M vs B-Matrix-M-CLUT-A) + [InlineData(TestIccProfiles.RommRgb, TestIccProfiles.StandardRgbV4)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 16-bit vs 8-bit) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV2, 0.0005)] // CMYK -> LAB -> XYZ -> RGB (different LUT tags, A2B vs TRC) --- tolerance slightly higher due to difference in inverse curve implementation + [InlineData(TestIccProfiles.StandardRgbV2, TestIccProfiles.Fogra39)] // RGB -> XYZ -> LAB -> CMYK (different LUT tags, TRC vs A2B) + public void CanConvertIccProfiles(string sourceProfile, string targetProfile, double tolerance = 0.000005) + { + List actual = Inputs.ConvertAll(input => GetActualTargetValues(input, sourceProfile, targetProfile)); + AssertConversion(sourceProfile, targetProfile, actual, tolerance, testOutputHelper); + } + + [Theory] + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.JapanColor2003)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 8-bit vs 16-bit) + [InlineData(TestIccProfiles.JapanColor2011, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Cgats21)] // CMYK -> LAB -> RGB (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV4)] // RGB -> LAB -> CMYK (different LUT versions, v4 vs v2) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.Fogra39)] // RGB -> LAB -> XYZ -> RGB (different LUT elements, B-Matrix-M-CLUT-A vs B-Matrix-M) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.RommRgb)] // RGB -> XYZ -> LAB -> RGB (different LUT elements, B-Matrix-M vs B-Matrix-M-CLUT-A) + [InlineData(TestIccProfiles.RommRgb, TestIccProfiles.StandardRgbV4)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 16-bit vs 8-bit) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV2, 0.0005)] // CMYK -> LAB -> XYZ -> RGB (different LUT tags, A2B vs TRC) --- tolerance slightly higher due to difference in inverse curve implementation + [InlineData(TestIccProfiles.StandardRgbV2, TestIccProfiles.Fogra39)] // RGB -> XYZ -> LAB -> CMYK (different LUT tags, TRC vs A2B) + [InlineData(TestIccProfiles.Issue129, TestIccProfiles.StandardRgbV4)] // CMYK -> LAB -> -> XYZ -> RGB + public void CanBulkConvertIccProfiles(string sourceProfile, string targetProfile, double tolerance = 0.000005) + { + List actual = GetBulkActualTargetValues(Inputs, sourceProfile, targetProfile); + AssertConversion(sourceProfile, targetProfile, actual, tolerance, testOutputHelper); + } + + private static void AssertConversion(string sourceProfile, string targetProfile, List actual, double tolerance, ITestOutputHelper testOutputHelper) + { + List expected = Inputs.ConvertAll(input => GetExpectedTargetValues(sourceProfile, targetProfile, input, testOutputHelper)); + Assert.Equal(expected.Count, actual.Count); + + for (int i = 0; i < expected.Count; i++) + { + Log(testOutputHelper, Inputs[i], expected[i], actual[i]); + for (int j = 0; j < expected[i].Length; j++) + { + Assert.Equal(expected[i][j], actual[i][j], tolerance); + } + } + } + + private static double[] GetExpectedTargetValues(string sourceProfile, string targetProfile, float[] input, ITestOutputHelper testOutputHelper) + { + Wacton.Unicolour.Configuration sourceConfig = TestIccProfiles.GetUnicolourConfiguration(sourceProfile); + Wacton.Unicolour.Configuration targetConfig = TestIccProfiles.GetUnicolourConfiguration(targetProfile); + + if (sourceConfig.Icc.Error != null || targetConfig.Icc.Error != null) + { + Assert.Fail("Unicolour does not support the ICC profile - test values will need to be calculated manually"); + } + + /* This is a hack to trick Unicolour to work in the same way as ImageSharp. + * ImageSharp bypasses PCS adjustment for v2 perceptual intent if source and target both need it + * as they both share the same understanding of what the PCS is (see ColorProfileConverterExtensionsIcc.GetTargetPcsWithPerceptualAdjustment) + * Unicolour does not support a direct profile-to-profile conversion so will always perform PCS adjustment for v2 perceptual intent. + * However, PCS adjustment clips negative XYZ values, causing those particular values in Unicolour and ImageSharp to diverge. + * It's unclear to me if there's a fundamental correct answer here. + * + * There are two obvious ways to keep Unicolour and ImageSharp values aligned: + * 1. Make ImageSharp always perform PCS adjustment, clipping negative XYZ values during the process - but creates a lot more calculations + * 2. Make Unicolour stop performing PCS adjustment, allowing negative XYZ values during conversion + * + * Option 2 is implemented by modifying the profiles so they claim to be v4 profiles + * since v4 perceptual profiles do not apply PCS adjustment. + */ + bool isSourcePerceptualV2 = sourceConfig.Icc.Intent == Intent.Perceptual && sourceConfig.Icc.Profile!.Header.ProfileVersion.Major == 2; + bool isTargetPerceptualV2 = targetConfig.Icc.Intent == Intent.Perceptual && targetConfig.Icc.Profile!.Header.ProfileVersion.Major == 2; + if (isSourcePerceptualV2 && isTargetPerceptualV2) + { + sourceConfig = GetUnicolourConfigAsV4Header(sourceConfig); + targetConfig = GetUnicolourConfigAsV4Header(targetConfig); + } + + Channels channels = new([.. input.Select(value => (double)value)]); + Unicolour source = new(sourceConfig, channels); + Unicolour target = source.ConvertToConfiguration(targetConfig); + if (target.Icc.Error != null) + { + testOutputHelper.WriteLine($"Error during Unicolour ICC conversion of supported profile: {target.Icc.Error}"); + } + + return target.Icc.Values; + } + + private static Wacton.Unicolour.Configuration GetUnicolourConfigAsV4Header(Wacton.Unicolour.Configuration config) + { + string profilePath = config.Icc.Profile!.FileInfo.FullName; + string modifiedFilename = $"{Path.GetFileNameWithoutExtension(profilePath)}_modified.icc"; + string modifiedProfile = Path.Combine(Path.GetDirectoryName(profilePath)!, modifiedFilename); + + Wacton.Unicolour.Configuration modifiedConfig; + if (!TestIccProfiles.HasUnicolourConfiguration(modifiedProfile)) + { + byte[] bytes = File.ReadAllBytes(profilePath); + bytes[8] = 4; // byte 8 of profile is major version + File.WriteAllBytes(modifiedProfile, bytes); + modifiedConfig = TestIccProfiles.GetUnicolourConfiguration(modifiedProfile); + File.Delete(modifiedProfile); + } + else + { + modifiedConfig = TestIccProfiles.GetUnicolourConfiguration(modifiedProfile); + } + + return modifiedConfig; + } + + private static Vector4 GetActualTargetValues(float[] input, string sourceProfile, string targetProfile) + { + ColorProfileConverter converter = new(new ColorConversionOptions + { + SourceIccProfile = TestIccProfiles.GetProfile(sourceProfile), + TargetIccProfile = TestIccProfiles.GetProfile(targetProfile) + }); + + IccColorSpaceType sourceDataSpace = converter.Options.SourceIccProfile!.Header.DataColorSpace; + IccColorSpaceType targetDataSpace = converter.Options.TargetIccProfile!.Header.DataColorSpace; + return sourceDataSpace switch + { + IccColorSpaceType.Cmyk when targetDataSpace == IccColorSpaceType.Cmyk + => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), + IccColorSpaceType.Cmyk when targetDataSpace == IccColorSpaceType.Rgb + => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), + IccColorSpaceType.Rgb when targetDataSpace == IccColorSpaceType.Cmyk + => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), + IccColorSpaceType.Rgb when targetDataSpace == IccColorSpaceType.Rgb + => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), + _ => throw new NotSupportedException($"Unsupported ICC profile data color space conversion: {sourceDataSpace} -> {targetDataSpace}") + }; + } + + private static List GetBulkActualTargetValues(List inputs, string sourceProfile, string targetProfile) + { + ColorProfileConverter converter = new(new ColorConversionOptions + { + SourceIccProfile = TestIccProfiles.GetProfile(sourceProfile), + TargetIccProfile = TestIccProfiles.GetProfile(targetProfile) + }); + + IccColorSpaceType sourceDataSpace = converter.Options.SourceIccProfile!.Header.DataColorSpace; + IccColorSpaceType targetDataSpace = converter.Options.TargetIccProfile!.Header.DataColorSpace; + + switch (sourceDataSpace) + { + case IccColorSpaceType.Cmyk: + { + Span inputSpan = inputs.Select(x => new Cmyk(new Vector4(x))).ToArray(); + + switch (targetDataSpace) + { + case IccColorSpaceType.Cmyk: + { + Span outputSpan = stackalloc Cmyk[inputs.Count]; + converter.Convert(inputSpan, outputSpan); + return [.. outputSpan.ToArray().Select(x => x.ToScaledVector4())]; + } + + case IccColorSpaceType.Rgb: + { + Span outputSpan = stackalloc Rgb[inputs.Count]; + converter.Convert(inputSpan, outputSpan); + return [.. outputSpan.ToArray().Select(x => x.ToScaledVector4())]; + } + + default: + throw new NotSupportedException($"Unsupported ICC profile data color space conversion: {sourceDataSpace} -> {targetDataSpace}"); + } + } + + case IccColorSpaceType.Rgb: + { + Span inputSpan = inputs.Select(x => new Rgb(new Vector3(x))).ToArray(); + + switch (targetDataSpace) + { + case IccColorSpaceType.Cmyk: + { + Span outputSpan = stackalloc Cmyk[inputs.Count]; + converter.Convert(inputSpan, outputSpan); + return [.. outputSpan.ToArray().Select(x => x.ToScaledVector4())]; + } + + case IccColorSpaceType.Rgb: + { + Span outputSpan = stackalloc Rgb[inputs.Count]; + converter.Convert(inputSpan, outputSpan); + return [.. outputSpan.ToArray().Select(x => x.ToScaledVector4())]; + } + + default: + throw new NotSupportedException($"Unsupported ICC profile data color space conversion: {sourceDataSpace} -> {targetDataSpace}"); + } + } + + default: + throw new NotSupportedException($"Unsupported ICC profile data color space conversion: {sourceDataSpace} -> {targetDataSpace}"); + } + } + + private static float GetNormalizedRandomValue() + { + // Generate a random value between 0 (inclusive) and 1 (exclusive). + double value = Random.Shared.NextDouble(); + + // If the random value is exactly 0, return 0F to ensure inclusivity at the lower bound. + // For non-zero values, add a small increment (0.0000001F) to ensure the range + // is inclusive at the upper bound while retaining precision. + // Clamp the result between 0 and 1 to ensure it does not exceed the bounds. + return value == 0 ? 0F : Math.Clamp((float)value + 0.0000001F, 0, 1); + } + + private static void Log(ITestOutputHelper testOutputHelper, float[] input, double[] expected, Vector4 actual) + { + string inputText = string.Join(", ", input); + string expectedText = string.Join(", ", expected.Select(x => $"{x:f8}")); + testOutputHelper.WriteLine($"Input {inputText} · Expected output {expectedText} · Actual output {actual}"); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/TestIccProfiles.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/TestIccProfiles.cs new file mode 100644 index 0000000000..eec27fcd76 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/TestIccProfiles.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Collections.Concurrent; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using Wacton.Unicolour; +using Wacton.Unicolour.Icc; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc; + +internal static class TestIccProfiles +{ + private static readonly ConcurrentDictionary ProfileCache = new(); + private static readonly ConcurrentDictionary UnicolourConfigurationCache = new(); + + /// + /// v2 CMYK -> LAB, output, lut16 + /// + public const string Fogra39 = "Coated_Fogra39L_VIGC_300.icc"; + + /// + /// v2 CMYK -> LAB, output, lut16 + /// + public const string Swop2006 = "SWOP2006_Coated5v2.icc"; + + /// + /// v2 CMYK -> LAB, output, lut8 (A2B tags) + /// + public const string JapanColor2011 = "JapanColor2011Coated.icc"; + + /// + /// v2 CMYK -> LAB, output, lut8 (B2A tags) + /// + public const string JapanColor2003 = "JapanColor2003WebCoated.icc"; + + /// + /// v4 CMYK -> LAB, output, lutAToB: B-CLUT-A + /// + public const string Cgats21 = "CGATS21_CRPC7.icc"; + + /// + /// v4 RGB -> XYZ, colorspace, lutAToB: B-Matrix-M [only intent 0] + /// + public const string RommRgb = "ISO22028-2_ROMM-RGB.icc"; + + /// + /// v4 RGB -> LAB, colorspace, lutAToB: B-Matrix-M-CLUT-A [only intent 0 & 1] + /// + public const string StandardRgbV4 = "sRGB_v4_ICC_preference.icc"; + + /// + /// v2 CMYK -> LAB, output + /// + public const string Issue129 = "issue-129.icc"; + + /// + /// v2 RGB -> XYZ, display, TRCs + /// + public const string StandardRgbV2 = "sRGB2014.icc"; + + public static IccProfile GetProfile(string file) + => ProfileCache.GetOrAdd(file, f => new IccProfile(File.ReadAllBytes(GetFullPath(f)))); + + public static Wacton.Unicolour.Configuration GetUnicolourConfiguration(string file) + => UnicolourConfigurationCache.GetOrAdd( + file, + f => new Wacton.Unicolour.Configuration(iccConfig: new IccConfiguration(GetFullPath(f), Intent.Unspecified, f))); + + public static bool HasUnicolourConfiguration(string file) + => UnicolourConfigurationCache.ContainsKey(file); + + private static string GetFullPath(string file) + => Path.GetFullPath(Path.Combine(".", "TestDataIcc", "Profiles", file)); +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs new file mode 100644 index 0000000000..395b547fdb --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.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. +/// +[Trait("Color", "Conversion")] +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/RbgAndYConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RbgAndYConversionTests.cs new file mode 100644 index 0000000000..017ba78d0b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RbgAndYConversionTests.cs @@ -0,0 +1,84 @@ +// 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 mathematically +/// +public class RbgAndYConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.001F); + + [Theory] + [InlineData(0F, 0F, 0F, 0F)] + [InlineData(0.5F, 0.5F, 0.5F, 0.5F)] + [InlineData(1F, 1F, 1F, 1F)] + public void Convert_Rgb_To_Y_BT601(float r, float g, float b, float y) + { + ColorConversionOptions options = new() + { + YCbCrTransform = KnownYCbCrMatrices.BT601 + }; + + Convert_Rgb_To_Y_Core(r, g, b, y, options); + } + + [Theory] + [InlineData(0F, 0F, 0F, 0F)] + [InlineData(0.5F, 0.5F, 0.5F, 0.5F)] + [InlineData(1F, 1F, 1F, 1F)] + public void Convert_Rgb_To_Y_BT709(float r, float g, float b, float y) + { + ColorConversionOptions options = new() + { + YCbCrTransform = KnownYCbCrMatrices.BT709 + }; + + Convert_Rgb_To_Y_Core(r, g, b, y, options); + } + + [Theory] + [InlineData(0F, 0F, 0F, 0F)] + [InlineData(0.5F, 0.5F, 0.5F, 0.49999997F)] + [InlineData(1F, 1F, 1F, 0.99999994F)] + public void Convert_Rgb_To_Y_BT2020(float r, float g, float b, float y) + { + ColorConversionOptions options = new() + { + YCbCrTransform = KnownYCbCrMatrices.BT2020 + }; + + Convert_Rgb_To_Y_Core(r, g, b, y, options); + } + + private static void Convert_Rgb_To_Y_Core(float r, float g, float b, float y, ColorConversionOptions options) + { + // Arrange + Rgb input = new(r, g, b); + Y expected = new(y); + ColorProfileConverter converter = new(options); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Y[5]; + + // Act + Y 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/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs new file mode 100644 index 0000000000..7b48089c7c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -0,0 +1,150 @@ +// 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 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() { SourceWhitePoint = KnownIlluminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.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() { SourceWhitePoint = KnownIlluminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.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 = KnownIlluminants.D50, SourceRgbWorkingSpace = KnownRgbWorkingSpaces.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 = KnownIlluminants.D65, SourceRgbWorkingSpace = KnownRgbWorkingSpaces.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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs new file mode 100644 index 0000000000..bd35fb7751 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs @@ -0,0 +1,78 @@ +// 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: +/// +/// +/// +[Trait("Color", "Conversion")] +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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs new file mode 100644 index 0000000000..e5874c3d13 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs @@ -0,0 +1,84 @@ +// 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: +/// +/// +/// +[Trait("Color", "Conversion")] +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 0000000000..4a685abe5f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs @@ -0,0 +1,82 @@ +// 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: +/// +/// +[Trait("Color", "Conversion")] +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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs new file mode 100644 index 0000000000..ede8226e40 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs @@ -0,0 +1,75 @@ +// 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 mathematically +/// +public class RgbAndYCbCrConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.001F); + + [Theory] + [InlineData(1, .5F, .5F, 1, 1, 1)] + [InlineData(0, .5F, .5F, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, .5F, .5F, .5F)] + 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(1, 1, 1, 1, .5F, .5F)] + [InlineData(0, 0, 0, 0, .5F, .5F)] + [InlineData(.5F, .5F, .5F, .5F, .5F, .5F)] + 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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYccKConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYccKConversionTests.cs new file mode 100644 index 0000000000..78f424cc28 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYccKConversionTests.cs @@ -0,0 +1,80 @@ +// 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 mathematically +/// +public class RgbAndYccKConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.001F); + + [Theory] + [InlineData(1, .5F, .5F, 0, 1, 1, 1)] + [InlineData(0, .5F, .5F, 1, 0, 0, 0)] + [InlineData(.5F, .5F, .5F, 0, .5F, .5F, .5F)] + public void Convert_YccK_To_Rgb(float y, float cb, float cr, float k, float r, float g, float b) + { + // Arrange + YccK input = new(y, cb, cr, k); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new YccK[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, 1, .5F, .5F, 0)] + [InlineData(0, 0, 0, 0, .5F, .5F, 1)] + [InlineData(.5F, .5F, .5F, 1, .5F, .5F, .5F)] + public void Convert_Rgb_To_YccK(float r, float g, float b, float y, float cb, float cr, float k) + { + // Multiple YccK representations can decode to the same RGB value. + // For example, (Y=1.0, Cb=0.5, Cr=0.5, K=0.5) and (Y=0.5, Cb=0.5, Cr=0.5, K=0.0) both yield RGB (0.5, 0.5, 0.5). + // This is expected because YccK is not a unique encoding — K modulates RGB after YCbCr decoding. + // Round-tripping RGB -> YccK -> RGB is stable, but YccK -> RGB -> YccK is not injective. + + // Arrange + Rgb input = new(r, g, b); + YccK expected = new(y, cb, cr, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new YccK[5]; + + // Act + YccK 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/RgbTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs new file mode 100644 index 0000000000..707b3e2a7d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs @@ -0,0 +1,82 @@ +// 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. +/// +[Trait("Color", "Conversion")] +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/RgbWorkingSpaceTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs new file mode 100644 index 0000000000..8a38ea11fc --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbWorkingSpaceTests.cs @@ -0,0 +1,122 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the class. +/// +[Trait("Color", "Conversion")] +public class RgbWorkingSpaceTests +{ + private static readonly ApproximateFloatComparer TolerantComparer = new(1e-5F); + + public static readonly TheoryData WorkingSpaces = new() + { + KnownRgbWorkingSpaces.SRgb, + KnownRgbWorkingSpaces.SRgbSimplified, + KnownRgbWorkingSpaces.Rec709, + KnownRgbWorkingSpaces.Rec2020, + KnownRgbWorkingSpaces.ECIRgbv2, + KnownRgbWorkingSpaces.AdobeRgb1998, + KnownRgbWorkingSpaces.ApplesRgb, + KnownRgbWorkingSpaces.BestRgb, + KnownRgbWorkingSpaces.BetaRgb, + KnownRgbWorkingSpaces.BruceRgb, + KnownRgbWorkingSpaces.CIERgb, + KnownRgbWorkingSpaces.ColorMatchRgb, + KnownRgbWorkingSpaces.DonRgb4, + KnownRgbWorkingSpaces.EktaSpacePS5, + KnownRgbWorkingSpaces.NTSCRgb, + KnownRgbWorkingSpaces.PALSECAMRgb, + KnownRgbWorkingSpaces.ProPhotoRgb, + KnownRgbWorkingSpaces.SMPTECRgb, + KnownRgbWorkingSpaces.WideGamutRgb + }; + + [Fact] + public void RgbWorkingSpaceEqualityRequiresSameConcreteType() + { + RgbWorkingSpace sRgb = KnownRgbWorkingSpaces.SRgb; + RgbWorkingSpace sRgbSimplified = KnownRgbWorkingSpaces.SRgbSimplified; + RgbWorkingSpace rec709 = KnownRgbWorkingSpaces.Rec709; + + Assert.False(sRgb.Equals(sRgbSimplified)); + Assert.False(sRgbSimplified.Equals(sRgb)); + Assert.False(sRgb.Equals(rec709)); + Assert.False(rec709.Equals(sRgb)); + + Assert.NotEqual(sRgb.GetHashCode(), sRgbSimplified.GetHashCode()); + Assert.NotEqual(sRgb.GetHashCode(), rec709.GetHashCode()); + } + + [Fact] + public void RgbWorkingSpaceEqualityMatchesSameConcreteTypeValues() + { + RgbWorkingSpace x = new SRgbWorkingSpace( + KnownRgbWorkingSpaces.SRgb.WhitePoint, + KnownRgbWorkingSpaces.SRgb.ChromaticityCoordinates); + + RgbWorkingSpace y = new SRgbWorkingSpace( + KnownRgbWorkingSpaces.SRgb.WhitePoint, + KnownRgbWorkingSpaces.SRgb.ChromaticityCoordinates); + + Assert.Equal(x, y); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Fact] + public void GammaWorkingSpaceEqualityIncludesGamma() + { + GammaWorkingSpace x = new( + 2.2F, + KnownRgbWorkingSpaces.SRgbSimplified.WhitePoint, + KnownRgbWorkingSpaces.SRgbSimplified.ChromaticityCoordinates); + + GammaWorkingSpace y = new( + 1.8F, + KnownRgbWorkingSpaces.SRgbSimplified.WhitePoint, + KnownRgbWorkingSpaces.SRgbSimplified.ChromaticityCoordinates); + + Assert.NotEqual(x, y); + Assert.NotEqual(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [MemberData(nameof(WorkingSpaces))] + public void CompressAndExpand_RoundTripsWithTolerance(RgbWorkingSpace workingSpace) + { + Vector4[] linear = + [ + new(0F, .001F, .18F, 1F), // Endpoint, below the sRGB breakpoint, common middle gray, and opaque alpha. + new(.0031308F, .25F, .5F, .75F), // sRGB linear/gamma breakpoint with interior values and partial alpha. + new(.75F, .5F, .25F, .5F), // Reversed interior values ensure channel order does not hide errors. + new(1F, .8F, .2F, .25F) // Upper endpoint, high/low interior values, and low alpha. + ]; + + Vector4[] expectedCompressed = new Vector4[linear.Length]; + + for (int i = 0; i < linear.Length; i++) + { + Vector4 compressed = workingSpace.Compress(linear[i]); + Vector4 expanded = workingSpace.Expand(compressed); + + expectedCompressed[i] = compressed; + + Assert.Equal(linear[i], expanded, TolerantComparer); + } + + Vector4[] actualCompressed = linear.ToArray(); + workingSpace.Compress(actualCompressed); + + Assert.Equal(expectedCompressed, actualCompressed, TolerantComparer); + + workingSpace.Expand(actualCompressed); + + Assert.Equal(linear, actualCompressed, TolerantComparer); + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs similarity index 76% rename from tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs rename to tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs index 2ae0824f28..f61124d8f5 100644 --- a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs @@ -2,18 +2,19 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.Colorspaces; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; +[Trait("Color", "Conversion")] 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); + 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); + private static readonly Vector4 Random4 = new(42.4F, 94.5F, 83.4F, 1); - public static readonly TheoryData TestData = new TheoryData + public static readonly TheoryData TestData = new() { { new CieLab(Zero), "CieLab(0, 0, 0)" }, { new CieLch(Zero), "CieLch(0, 0, 0)" }, @@ -23,7 +24,6 @@ public class StringRepresentationTests { 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)" }, @@ -36,7 +36,6 @@ public class StringRepresentationTests { 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)" }, @@ -50,11 +49,14 @@ public class StringRepresentationTests { 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 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)" }, + { new Y(Random.X), "Y(1)" }, + { new YCbCr(Random), "YCbCr(1, 1, 1)" }, + { new YccK(Random4), "YccK(1, 1, 1, 1)" }, + { new Cmyk(Random4), "Cmyk(1, 1, 1, 1)" }, }; [Theory] diff --git a/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs new file mode 100644 index 0000000000..1622af1bfb --- /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() + { + SourceWhitePoint = 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); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs new file mode 100644 index 0000000000..64558a3f8a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.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. +/// +[Trait("Color", "Conversion")] +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())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/YTests.cs b/tests/ImageSharp.Tests/ColorProfiles/YTests.cs new file mode 100644 index 0000000000..7e5e48b69e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/YTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +[Trait("Color", "Conversion")] +public class YTests +{ + [Fact] + public void YConstructorAssignsFields() + { + const float y = .75F; + Y yValue = new(y); + + Assert.Equal(y, yValue.L); + } + + [Fact] + public void YEquality() + { + Y x = default; + Y y = new(1F); + Assert.True(default == default(Y)); + Assert.False(default != default(Y)); + Assert.Equal(default, default(Y)); + Assert.Equal(new Y(1), new Y(1)); + + Assert.Equal(new Y(.5F), new Y(.5F)); + 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/YccKTests.cs b/tests/ImageSharp.Tests/ColorProfiles/YccKTests.cs new file mode 100644 index 0000000000..bfe0bdb175 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/YccKTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +[Trait("Color", "Conversion")] +public class YccKTests +{ + [Fact] + public void YccKConstructorAssignsFields() + { + const float y = .75F; + const float cb = .5F; + const float cr = .25F; + const float k = .125F; + + YccK ycckValue = new(y, cb, cr, k); + Assert.Equal(y, ycckValue.Y); + Assert.Equal(cb, ycckValue.Cb); + Assert.Equal(cr, ycckValue.Cr); + Assert.Equal(k, ycckValue.K); + } + + [Fact] + public void YccKEquality() + { + YccK x = default; + YccK y = new(1F, 1F, 1F, 1F); + Assert.True(default == default(YccK)); + Assert.False(default != default(YccK)); + Assert.Equal(default, default(YccK)); + Assert.Equal(new YccK(1, 1, 1, 1), new YccK(1, 1, 1, 1)); + Assert.Equal(new YccK(.5F, .5F, .5F, .5F), new YccK(.5F, .5F, .5F, .5F)); + + 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/CieLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs deleted file mode 100644 index 52cb63d217..0000000000 --- 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 83e4d8a59a..0000000000 --- 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 f6c68aae91..0000000000 --- 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 0ebf1bdeaa..0000000000 --- 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 061d6c432f..0000000000 --- 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 f1eb126401..0000000000 --- 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 6de961cf1b..0000000000 --- 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 b4e55ed24c..0000000000 --- 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 3fb3598909..0000000000 --- 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 d66a73b5a1..0000000000 --- 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 d71f0fa5e0..0000000000 --- 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 d8378217e4..0000000000 --- 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 220ef4f220..0000000000 --- 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 49ea4502fc..0000000000 --- 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 7f9470e202..0000000000 --- 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 1af6a9315f..0000000000 --- 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 c7c3ad19b5..0000000000 --- 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 81fff6e144..0000000000 --- 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 68547d8a22..0000000000 --- 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 2918b6f62b..0000000000 --- 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 3acf695d12..0000000000 --- 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 f13e989a85..0000000000 --- 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 b50d47aeba..0000000000 --- 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 93f0828861..0000000000 --- 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 0bc76562b1..0000000000 --- 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 bb28c03946..0000000000 --- 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 9892ac8f57..0000000000 --- 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 3ecfadf113..0000000000 --- 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 7a19391211..0000000000 --- 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 013e79534d..0000000000 --- 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 bcde97102f..0000000000 --- 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 1098de91e0..0000000000 --- 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 ba3bc9e799..0000000000 --- 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 176a0e2175..0000000000 --- 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 e32cc6ebfc..0000000000 --- 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 95f07465ab..0000000000 --- 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 ddb90f0ef9..0000000000 --- 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 38449e4b77..0000000000 --- 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 f3bd936043..0000000000 --- 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 ac90a7ba41..0000000000 --- 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 b2e308fce0..0000000000 --- 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 f7c6372b1a..0000000000 --- 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 bae9140029..0000000000 --- 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 7de91cc322..0000000000 --- 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 40b58b7048..0000000000 --- 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 062f54abc3..0000000000 --- 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 8e9cbc5ffc..0000000000 --- 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 0a7cd68429..0000000000 --- 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 efacebcf19..0000000000 --- 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 3ea4228e5e..0000000000 --- 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 698c9add60..0000000000 --- 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 a4caf4c857..0000000000 --- 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 9e3381b40d..0000000000 --- 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 5ef5c4d21a..0000000000 --- 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 634d03a502..0000000000 --- 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 ccedd7b755..0000000000 --- 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 af7087ba23..0000000000 --- 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 33bdc6e935..0000000000 --- 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 ba67e605a6..0000000000 --- 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 9def3e1b20..0000000000 --- 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 9ae0fb7119..0000000000 --- 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 270db9d205..0000000000 --- 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 972948c71d..0000000000 --- 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 2b00942aca..0000000000 --- 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 f158fb5f9e..0000000000 --- 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 9832a0d715..0000000000 --- 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 1e8bc52e2f..0000000000 --- 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 bbebf96b32..0000000000 --- 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 0119f4f3a4..0000000000 --- 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 942b34db33..0000000000 --- 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 8b448a0426..0000000000 --- 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 d80aa6c329..0000000000 --- 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 eb4eb0bbff..0000000000 --- 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 9e33ad1689..0000000000 --- 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 a8702488a6..0000000000 --- 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 caedd3f171..0000000000 --- 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 9c97c4c91b..0000000000 --- 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 ff2d151344..0000000000 --- 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 5e8840664e..0000000000 --- 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 be96b79f46..0000000000 --- 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/YCbCrTests.cs b/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs deleted file mode 100644 index efab45c3cb..0000000000 --- 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())); - } -} diff --git a/tests/ImageSharp.Tests/Common/BufferedReadStreamExtensionsTests.cs b/tests/ImageSharp.Tests/Common/BufferedReadStreamExtensionsTests.cs new file mode 100644 index 0000000000..6bef711931 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/BufferedReadStreamExtensionsTests.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.IO; + +namespace SixLabors.ImageSharp.Tests.Common; + +public class BufferedReadStreamExtensionsTests +{ + [Theory] + [InlineData(0L, 8UL, true)] + [InlineData(8L, 0UL, true)] + [InlineData(7L, 2UL, false)] + [InlineData(9L, 0UL, false)] + [InlineData(-1L, 1UL, false)] + [InlineData(long.MaxValue, ulong.MaxValue, false)] + [InlineData(0L, ulong.MaxValue, false)] + public void IsReadRangeValid_ChecksCompleteExtent(long offset, ulong length, bool expected) + { + using MemoryStream input = new(new byte[8]); + using BufferedReadStream stream = new(Configuration.Default, input); + + Assert.Equal(expected, stream.IsReadRangeValid(offset, length)); + Assert.Equal(0, stream.Position); + } + + [Theory] + [InlineData(0UL, true, 0)] + [InlineData(6UL, true, 6)] + [InlineData(7UL, false, 0)] + [InlineData(1073741824UL, false, 0)] + [InlineData(4294967294UL, false, 0)] + [InlineData(4294967296UL, false, 0)] + [InlineData(ulong.MaxValue, false, 0)] + public void TryGetReadLength_ReturnsResultWithoutMovingStream(ulong length, bool expected, int expectedLength) + { + using MemoryStream input = new(new byte[8]); + using BufferedReadStream stream = new(Configuration.Default, input); + stream.Position = 2; + + Assert.Equal(expected, stream.TryGetReadLength(length, out int bufferLength)); + Assert.Equal(expectedLength, bufferLength); + Assert.Equal(2, stream.Position); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Skip_CountZeroOrLower_PositionNotChanged(int count) + { + using MemoryStream input = new(new byte[8]); + using BufferedReadStream stream = new(Configuration.Default, input); + stream.Position = 4; + + stream.Skip(count); + + Assert.Equal(4, stream.Position); + Assert.Equal(0, stream.ReadByte()); + } +} diff --git a/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs b/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs index 190bfe1dc6..8421c1fd74 100644 --- a/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs @@ -10,7 +10,7 @@ public class EncoderExtensionsTests [Fact] public void GetString_EmptyBuffer_ReturnsEmptyString() { - var buffer = default(ReadOnlySpan); + ReadOnlySpan buffer = default(ReadOnlySpan); string result = Encoding.UTF8.GetString(buffer); @@ -20,7 +20,7 @@ public void GetString_EmptyBuffer_ReturnsEmptyString() [Fact] public void GetString_Buffer_ReturnsString() { - var buffer = new ReadOnlySpan(new byte[] { 73, 109, 97, 103, 101, 83, 104, 97, 114, 112 }); + ReadOnlySpan buffer = new(new byte[] { 73, 109, 97, 103, 101, 83, 104, 97, 114, 112 }); string result = Encoding.UTF8.GetString(buffer); diff --git a/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs new file mode 100644 index 0000000000..95b8d2013f --- /dev/null +++ b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear; + +namespace SixLabors.ImageSharp.Tests.Common; + +public class GaussianEliminationSolverTest +{ + [Theory] + [MemberData(nameof(MatrixTestData))] + public void CanSolve(double[][] matrix, double[] result, double[] expected) + { + GaussianEliminationSolver.Solve(matrix, result); + + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(result[i], expected[i], 4); + } + } + + public static TheoryData MatrixTestData + { + get + { + TheoryData data = []; + { + double[][] matrix = + [ + [2, 3, 4], + [1, 2, 3], + [3, -4, 0], + ]; + double[] result = [6, 4, 10]; + double[] expected = [18 / 11f, -14 / 11f, 18 / 11f]; + data.Add(matrix, result, expected); + } + + { + double[][] matrix = + [ + [1, 4, -1], + [2, 5, 8], + [1, 3, -3], + ]; + double[] result = [4, 15, 1]; + double[] expected = [1, 1, 1]; + data.Add(matrix, result, expected); + } + + { + double[][] matrix = + [ + [-1, 0, 0], + [0, 1, 0], + [0, 0, 1], + ]; + double[] result = [1, 2, 3]; + double[] expected = [-1, 2, 3]; + data.Add(matrix, result, expected); + } + + return data; + } + } +} diff --git a/tests/ImageSharp.Tests/Common/NumericsTests.cs b/tests/ImageSharp.Tests/Common/NumericsTests.cs index 2b92769436..ebee570600 100644 --- a/tests/ImageSharp.Tests/Common/NumericsTests.cs +++ b/tests/ImageSharp.Tests/Common/NumericsTests.cs @@ -28,7 +28,7 @@ public void DivideCeil_DivideZero() [InlineData(1, 100)] public void DivideCeil_RandomValues(int seed, int count) { - var rng = new Random(seed); + Random rng = new(seed); for (int i = 0; i < count; i++) { uint value = (uint)rng.Next(); diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs index ba37ee1661..cbe2e40667 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs @@ -292,7 +292,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -303,56 +303,21 @@ static void RunTest(string serialized) { int size = FeatureTestRunner.Deserialize(serialized); - // These cannot be expressed as a theory as you cannot - // use RemoteExecutor within generic methods nor pass - // IShuffle4 to the generic utils method. - WXYZShuffle4 wxyz = default; - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wxyz), - SimdUtils.Shuffle.MMShuffle2103); - - WZYXShuffle4 wzyx = default; - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wzyx), - SimdUtils.Shuffle.MMShuffle0123); + TestShuffleByte4Channel(size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle2103); - YZWXShuffle4 yzwx = default; - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, yzwx), - SimdUtils.Shuffle.MMShuffle0321); - - ZYXWShuffle4 zyxw = default; - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, zyxw), - SimdUtils.Shuffle.MMShuffle3012); + TestShuffleByte4Channel(size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle0123); - DefaultShuffle4 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, xwyz), - xwyz.Control); + TestShuffleByte4Channel(size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle0321); - DefaultShuffle4 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, yyyy), - yyyy.Control); + TestShuffleByte4Channel(size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3012); - DefaultShuffle4 wwww = new(SimdUtils.Shuffle.MMShuffle3333); - TestShuffleByte4Channel( - size, - (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wwww), - wwww.Control); + TestShuffleByte4Channel(size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle1230); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -363,38 +328,13 @@ static void RunTest(string serialized) { int size = FeatureTestRunner.Deserialize(serialized); - // These cannot be expressed as a theory as you cannot - // use RemoteExecutor within generic methods nor pass - // IShuffle3 to the generic utils method. - DefaultShuffle3 zyx = new(SimdUtils.Shuffle.MMShuffle3012); - TestShuffleByte3Channel( - size, - (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, zyx), - zyx.Control); - - DefaultShuffle3 xyz = new(SimdUtils.Shuffle.MMShuffle3210); - TestShuffleByte3Channel( - size, - (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, xyz), - xyz.Control); - - DefaultShuffle3 yyy = new(SimdUtils.Shuffle.MMShuffle3111); - TestShuffleByte3Channel( - size, - (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, yyy), - yyy.Control); - - DefaultShuffle3 zzz = new(SimdUtils.Shuffle.MMShuffle3222); - TestShuffleByte3Channel( - size, - (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, zzz), - zzz.Control); + TestShuffleByte3Channel(size, (s, d) => SimdUtils.Shuffle3(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3012); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -405,38 +345,19 @@ static void RunTest(string serialized) { int size = FeatureTestRunner.Deserialize(serialized); - // These cannot be expressed as a theory as you cannot - // use RemoteExecutor within generic methods nor pass - // IPad3Shuffle4 to the generic utils method. - XYZWPad3Shuffle4 xyzw = default; - TestPad3Shuffle4Channel( - size, - (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, xyzw), - SimdUtils.Shuffle.MMShuffle3210); + TestPad3Shuffle4Channel(size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3210); - DefaultPad3Shuffle4 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); - TestPad3Shuffle4Channel( - size, - (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, xwyz), - xwyz.Control); + TestPad3Shuffle4Channel(size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle2103); - DefaultPad3Shuffle4 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); - TestPad3Shuffle4Channel( - size, - (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, yyyy), - yyyy.Control); + TestPad3Shuffle4Channel(size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle0123); - DefaultPad3Shuffle4 wwww = new(SimdUtils.Shuffle.MMShuffle3333); - TestPad3Shuffle4Channel( - size, - (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, wwww), - wwww.Control); + TestPad3Shuffle4Channel(size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3012); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -447,38 +368,19 @@ static void RunTest(string serialized) { int size = FeatureTestRunner.Deserialize(serialized); - // These cannot be expressed as a theory as you cannot - // use RemoteExecutor within generic methods nor pass - // IShuffle4Slice3 to the generic utils method. - XYZWShuffle4Slice3 xyzw = default; - TestShuffle4Slice3Channel( - size, - (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, xyzw), - SimdUtils.Shuffle.MMShuffle3210); + TestShuffle4Slice3Channel(size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3210); - DefaultShuffle4Slice3 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); - TestShuffle4Slice3Channel( - size, - (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, xwyz), - xwyz.Control); + TestShuffle4Slice3Channel(size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle0321); - DefaultShuffle4Slice3 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); - TestShuffle4Slice3Channel( - size, - (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, yyyy), - yyyy.Control); + TestShuffle4Slice3Channel(size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle0123); - DefaultShuffle4Slice3 wwww = new(SimdUtils.Shuffle.MMShuffle3333); - TestShuffle4Slice3Channel( - size, - (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, wwww), - wwww.Control); + TestShuffle4Slice3Channel(size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span), SimdUtils.Shuffle.MMShuffle3012); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } private static void TestShuffleFloat4Channel( diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 3e8e171645..71335032c8 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -3,7 +3,10 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit.Abstractions; @@ -26,7 +29,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 +60,7 @@ private static Vector CreateRandomTestVector(int seed, float min, float m { float[] data = new float[Vector.Count]; - var rnd = new Random(seed); + Random rnd = new(seed); for (int i = 0; i < Vector.Count; i++) { @@ -112,26 +115,15 @@ private bool SkipOnNonAvx2([CallerMemberName] string testCaseName = null) 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 }; [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))] - public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat(int count) => TestImpl_BulkConvertByteToNormalizedFloat( - count, - (s, d) => SimdUtils.ExtendedIntrinsics.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; } @@ -143,7 +135,7 @@ static void RunTest(string serialized) => TestImpl_BulkConvertByteToNormalizedFl FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE41); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX); } [Theory] @@ -152,57 +144,48 @@ public void BulkConvertByteToNormalizedFloat(int count) => TestImpl_BulkConvertB count, (s, d) => SimdUtils.ByteToNormalizedFloat(s.Span, d.Span)); + [Fact] + public void VectorMultiplyAddUsesRuntimeOrderAndFusedContract() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunVectorMultiplyAddUsesRuntimeOrderAndFusedContract, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void RunVectorMultiplyAddUsesRuntimeOrderAndFusedContract() + { + Assert.Equal(Vector128.Create(11F), Vector128_.MultiplyAddEstimate(Vector128.Create(2F), Vector128.Create(3F), Vector128.Create(5F))); + Assert.Equal(Vector256.Create(11F), Vector256_.MultiplyAddEstimate(Vector256.Create(2F), Vector256.Create(3F), Vector256.Create(5F))); + Assert.Equal(Vector512.Create(11F), Vector512_.MultiplyAddEstimate(Vector512.Create(2F), Vector512.Create(3F), Vector512.Create(5F))); + + // These associated-alpha components produce an exact midpoint that a separate multiply and add rounds incorrectly. + float left = (68F / byte.MaxValue) * .625F; + float right = 1F - (160F / byte.MaxValue); + float addend = ((50F / byte.MaxValue) + (50F / byte.MaxValue)) * left; + float expected = MathF.FusedMultiplyAdd(left, right, addend); + + Assert.Equal(Vector128.Create(expected), Vector128_.FusedMultiplyAdd(Vector128.Create(left), Vector128.Create(right), Vector128.Create(addend))); + Assert.Equal(Vector256.Create(expected), Vector256_.FusedMultiplyAdd(Vector256.Create(left), Vector256.Create(right), Vector256.Create(addend))); + Assert.Equal(Vector512.Create(expected), Vector512_.FusedMultiplyAdd(Vector512.Create(left), Vector512.Create(right), Vector512.Create(addend))); + } + private static void TestImpl_BulkConvertByteToNormalizedFloat( int count, Action, Memory> convert) { - byte[] source = new Random(count).GenerateRandomByteArray(count); + byte[] source = Enumerable.Range(0, count).Select(i => (byte)i).ToArray(); float[] result = new float[count]; - float[] expected = source.Select(b => b / 255f).ToArray(); + // A double-precision oracle keeps this expectation independent from either production implementation. + float[] expected = source.Select(b => (float)(b / (double)byte.MaxValue)).ToArray(); convert(source, result); - 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) - { - int n = Vector.Count; - short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale); - float[] fData = sData.Select(u => (float)u).ToArray(); - - var source = new Vector(sData); - - var expected1 = new Vector(fData, 0); - var expected2 = new Vector(fData, n); - - // Act: - SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector actual1, out Vector actual2); - - // Assert: - Assert.Equal(expected1, actual1); - Assert.Equal(expected2, actual2); + Assert.Equal(expected, result); } [Theory] - [MemberData(nameof(ArraySizesDivisibleBy32))] + [MemberData(nameof(ArraySizesDivisibleBy64))] public void HwIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) { - if (!Sse2.IsSupported) + if (!Sse2.IsSupported && !AdvSimd.IsSupported) { return; } @@ -214,7 +197,7 @@ static void RunTest(string serialized) => TestImpl_BulkConvertNormalizedFloatToB FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2); } [Theory] @@ -236,6 +219,45 @@ public void BulkConvertNormalizedFloatToByteClampOverflows(int count) } } + [Fact] + public void BulkConvertNormalizedFloatToByteRoundsMidpointsAwayFromZero() + { + float[] midpointValues = Enumerable.Range(0, byte.MaxValue).Select(x => (x + 0.5F) / byte.MaxValue).ToArray(); + float[] source = new float[1024]; + byte[] expected = new byte[source.Length]; + byte[] actual = new byte[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + int value = i % midpointValues.Length; + source[i] = midpointValues[value]; + expected[i] = (byte)(value + 1); + } + + SimdUtils.NormalizedFloatToByteSaturate(source, actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public void BulkConvertFloatToByteRoundsMidpointsAwayFromZeroAndClampsOverflows() + { + float[] source = new float[1027]; + byte[] expected = new byte[source.Length]; + byte[] actual = new byte[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + float value = (i % 258) - .5F; + source[i] = value; + expected[i] = (byte)Math.Min(byte.MaxValue, Math.Max(0, value + .5F)); + } + + SimdUtils.FloatToByteSaturate(source, actual); + + Assert.Equal(expected, actual); + } + [Theory] [MemberData(nameof(ArbitraryArraySizes))] public void PackFromRgbPlanes_Rgb24(int count) => TestPackFromRgbPlanes( @@ -262,7 +284,7 @@ public void PackFromRgbPlanesAvx2Reduce_Rgb24() 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 +318,7 @@ public void PackFromRgbPlanesAvx2Reduce_Rgba32() 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 +344,18 @@ public void PackFromRgbPlanesAvx2Reduce_Rgba32() 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/Common/StreamExtensionsTests.cs b/tests/ImageSharp.Tests/Common/StreamExtensionsTests.cs deleted file mode 100644 index 1931109448..0000000000 --- a/tests/ImageSharp.Tests/Common/StreamExtensionsTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Tests.Common; - -public class StreamExtensionsTests -{ - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void Skip_CountZeroOrLower_PositionNotChanged(int count) - { - using (var memStream = new MemoryStream(5)) - { - memStream.Position = 4; - memStream.Skip(count); - - Assert.Equal(4, memStream.Position); - } - } - - [Fact] - public void Skip_SeekableStream_SeekIsCalled() - { - using (var seekableStream = new SeekableStream(4)) - { - seekableStream.Skip(4); - - Assert.Equal(4, seekableStream.Offset); - Assert.Equal(SeekOrigin.Current, seekableStream.Loc); - } - } - - [Fact] - public void Skip_NonSeekableStream_BytesAreRead() - { - using (var nonSeekableStream = new NonSeekableStream()) - { - nonSeekableStream.Skip(5); - - Assert.Equal(3, nonSeekableStream.Counts.Count); - - Assert.Equal(5, nonSeekableStream.Counts[0]); - Assert.Equal(3, nonSeekableStream.Counts[1]); - Assert.Equal(1, nonSeekableStream.Counts[2]); - } - } - - [Fact] - public void Skip_EofStream_NoExceptionIsThrown() - { - using (var eofStream = new EofStream(7)) - { - eofStream.Skip(7); - - Assert.Equal(0, eofStream.Position); - } - } - - private class SeekableStream : MemoryStream - { - public long Offset; - public SeekOrigin Loc; - - public SeekableStream(int capacity) - : base(capacity) - { - } - - public override long Seek(long offset, SeekOrigin loc) - { - this.Offset = offset; - this.Loc = loc; - return base.Seek(offset, loc); - } - } - - private class NonSeekableStream : MemoryStream - { - public override bool CanSeek => false; - - public List Counts = new List(); - - public NonSeekableStream() - : base(4) - { - } - - public override int Read(byte[] buffer, int offset, int count) - { - this.Counts.Add(count); - - return Math.Min(2, count); - } - } - - private class EofStream : MemoryStream - { - public override bool CanSeek => false; - - public EofStream(int capacity) - : base(capacity) - { - } - - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - } -} diff --git a/tests/ImageSharp.Tests/Common/TensorPrimitivesTests.cs b/tests/ImageSharp.Tests/Common/TensorPrimitivesTests.cs new file mode 100644 index 0000000000..b2f4dfe762 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/TensorPrimitivesTests.cs @@ -0,0 +1,580 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.Common; + +public class TensorPrimitivesTests +{ + private static readonly int[] SpanLengthValues = + [ + 0, + 1, + 3, + 4, + 5, + 7, + 8, + 9, + 15, + 16, + 17, + 31, + 32, + 33, + 63, + 64, + 65, + 127, + 128, + 129, + 2048 + ]; + + /// + /// Gets lengths that exercise scalar execution, every SIMD width, overlapping tails, and the unrolled loop. + /// + public static TheoryData SpanLengths => new(SpanLengthValues); + + /// + /// Verifies every compatibility operation while forcing the supported SIMD feature tiers in isolated processes. + /// + [Fact] + public void OperationsMatchScalarFormulasAcrossHardwareIntrinsicFeatures() + => FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunOperationsAcrossHardwareIntrinsicFeatures, + HwIntrinsics.AllowAll + | HwIntrinsics.DisableAVX512F + | HwIntrinsics.DisableAVX + | HwIntrinsics.DisableArm64Sve + | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Verifies that span-to-span operations require equal input lengths before accessing either input. + /// + [Fact] + public void AddSpanSpanRejectsMismatchedInputLengths() + { + ArgumentException exception = Assert.Throws( + () => TensorPrimitives_.Add(new int[4], new int[3], new int[4])); + + Assert.Null(exception.ParamName); + } + + /// + /// Verifies that every operation rejects a destination that cannot hold all input elements. + /// + [Fact] + public void OperationsRejectShortDestinations() + { + int[] integers = new int[4]; + float[] singles = new float[4]; + + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Add(integers, integers, new int[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Add(integers, 1, new int[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Multiply(singles, 2F, new float[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Divide(singles, 2F, new float[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Max(singles, 2F, new float[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Clamp(singles, 0F, 1F, new float[3])).ParamName); + Assert.Equal("destination", Assert.Throws(() => TensorPrimitives_.Negate(singles, new float[3])).ParamName); + } + + /// + /// Verifies that every operation rejects shifted input and destination overlap. + /// + [Fact] + public void OperationsRejectShiftedOverlap() + { + int[] integers = new int[5]; + int[] separateIntegers = new int[4]; + float[] singles = new float[5]; + + // Both inputs need independent validation because either one may alias a shifted destination. + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Add(integers.AsSpan(0, 4), separateIntegers, integers.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Add(separateIntegers, integers.AsSpan(0, 4), integers.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Add(integers.AsSpan(0, 4), 1, integers.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Multiply(singles.AsSpan(0, 4), 2F, singles.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Divide(singles.AsSpan(0, 4), 2F, singles.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Max(singles.AsSpan(0, 4), 2F, singles.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Clamp(singles.AsSpan(0, 4), 0F, 1F, singles.AsSpan(1, 4))).ParamName); + + Assert.Equal( + "destination", + Assert.Throws( + () => TensorPrimitives_.Negate(singles.AsSpan(0, 4), singles.AsSpan(1, 4))).ParamName); + } + + /// + /// Runs the TensorPrimitives compatibility assertions inside a process configured for one hardware-intrinsic tier. + /// + private static void RunOperationsAcrossHardwareIntrinsicFeatures() + { + TensorPrimitivesTests tests = new(); + + // Reuse the focused assertions so the remote feature matrix cannot drift from the normal test coverage. + foreach (int length in SpanLengthValues) + { + tests.AddByteMatchesScalarFormula(length); + tests.AddUInt32MatchesScalarFormula(length); + tests.AddScalarInt32MatchesScalarFormula(length); + tests.NegateSingleMatchesScalarFormula(length); + tests.NegateDoubleMatchesScalarFormula(length); + tests.ClampInt32MatchesScalarFormula(length); + tests.ClampSingleMatchesRuntimeFormula(length); + tests.DivideSingleMatchesScalarFormula(length); + tests.MaxSingleMatchesRuntimeFormula(length); + tests.MultiplySingleMatchesScalarFormula(length); + tests.NormalizeMatchesScalarFormula(length); + } + + tests.ClampSinglePreservesRuntimeSpecialValueSemantics(); + tests.ClampDoublePreservesRuntimeSpecialValueSemantics(); + } + + /// + /// Verifies that byte addition wraps modulo 256 and supports either input as the in-place destination. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void AddByteMatchesScalarFormula(int length) + { + byte[] x = new byte[length]; + byte[] y = new byte[length]; + byte[] expected = new byte[length]; + + for (int i = 0; i < length; i++) + { + x[i] = (byte)((i * 23) + 197); + y[i] = (byte)((i * 41) + 113); + expected[i] = unchecked((byte)(x[i] + y[i])); + } + + byte[] destination = new byte[length]; + TensorPrimitives_.Add(x, y, destination); + Assert.Equal(expected, destination); + + byte[] xInPlace = (byte[])x.Clone(); + TensorPrimitives_.Add(xInPlace, y, xInPlace); + Assert.Equal(expected, xInPlace); + + byte[] yInPlace = (byte[])y.Clone(); + TensorPrimitives_.Add(x, yInPlace, yInPlace); + Assert.Equal(expected, yInPlace); + } + + /// + /// Verifies that unsigned integer addition preserves unchecked histogram accumulation semantics. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void AddUInt32MatchesScalarFormula(int length) + { + uint[] x = new uint[length]; + uint[] y = new uint[length]; + uint[] expected = new uint[length]; + + for (int i = 0; i < length; i++) + { + x[i] = ((uint)i * 1_234_567U) + 0xF0000000U; + y[i] = ((uint)i * 7_654_321U) + 0x30000000U; + expected[i] = unchecked(x[i] + y[i]); + } + + TensorPrimitives_.Add(x, y, x); + Assert.Equal(expected, x); + } + + /// + /// Verifies that scalar integer addition produces identical results for separate and in-place destinations. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void AddScalarInt32MatchesScalarFormula(int length) + { + int[] source = new int[length]; + int[] expected = new int[length]; + const int addend = 17; + + for (int i = 0; i < length; i++) + { + source[i] = (i * 37) - 200; + expected[i] = source[i] + addend; + } + + int[] destination = new int[length]; + TensorPrimitives_.Add(source, addend, destination); + Assert.Equal(expected, destination); + + int[] inPlace = (int[])source.Clone(); + TensorPrimitives_.Add(inPlace, addend, inPlace); + Assert.Equal(expected, inPlace); + } + + /// + /// Verifies that floating-point negation preserves the scalar operator's exact bit-level behavior. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void NegateSingleMatchesScalarFormula(int length) + { + float[] values = + { + float.NaN, + -0F, + 0F, + -1F, + 1F, + float.NegativeInfinity, + float.PositiveInfinity + }; + + float[] source = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = values[i % values.Length]; + expected[i] = -source[i]; + } + + float[] destination = new float[length]; + TensorPrimitives_.Negate(source, destination); + AssertSingleBitsEqual(expected, destination); + + TensorPrimitives_.Negate(source, source); + AssertSingleBitsEqual(expected, source); + } + + /// + /// Verifies that double-precision negation preserves the scalar operator's exact bit-level behavior. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void NegateDoubleMatchesScalarFormula(int length) + { + double[] values = + { + double.NaN, + -0D, + 0D, + -1D, + 1D, + double.NegativeInfinity, + double.PositiveInfinity + }; + + double[] source = new double[length]; + double[] expected = new double[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = values[i % values.Length]; + expected[i] = -source[i]; + } + + double[] destination = new double[length]; + TensorPrimitives_.Negate(source, destination); + AssertDoubleBitsEqual(expected, destination); + + TensorPrimitives_.Negate(source, source); + AssertDoubleBitsEqual(expected, source); + } + + /// + /// Verifies that integer clamping produces identical results for separate and in-place destinations. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void ClampInt32MatchesScalarFormula(int length) + { + int[] source = new int[length]; + int[] expected = new int[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = ((i * 37) % 401) - 200; + expected[i] = Math.Clamp(source[i], -73, 91); + } + + int[] destination = new int[length]; + TensorPrimitives_.Clamp(source, -73, 91, destination); + Assert.Equal(expected, destination); + + int[] inPlace = (int[])source.Clone(); + TensorPrimitives_.Clamp(inPlace, -73, 91, inPlace); + Assert.Equal(expected, inPlace); + } + + /// + /// Verifies that floating-point clamping matches the runtime tensor formula for special values and unordered bounds. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void ClampSingleMatchesRuntimeFormula(int length) + { + float[] values = + { + float.NaN, + -0F, + 0F, + -1F, + 1F, + float.NegativeInfinity, + float.PositiveInfinity + }; + + float[] source = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = values[i % values.Length]; + + // Runtime main follows Min(Max(x, min), max) for vectorizable types, including unordered bounds. + expected[i] = float.Min(float.Max(source[i], 2F), -2F); + } + + TensorPrimitives_.Clamp(source, 2F, -2F, source); + AssertSingleBitsEqual(expected, source); + } + + /// + /// Verifies that single-precision clamping preserves the runtime's signed-zero and NaN behavior. + /// + [Fact] + public void ClampSinglePreservesRuntimeSpecialValueSemantics() + { + float[] values = + { + float.NaN, + float.NegativeInfinity, + -0F, + 0F, + float.PositiveInfinity + }; + + float[] actual = new float[129]; + float[] expected = new float[actual.Length]; + + for (int i = 0; i < actual.Length; i++) + { + actual[i] = values[i % values.Length]; + expected[i] = float.Min(float.Max(actual[i], -0F), 0F); + } + + TensorPrimitives_.Clamp(actual, -0F, 0F, actual); + AssertSingleBitsEqual(expected, actual); + } + + /// + /// Verifies that double-precision clamping preserves the runtime's signed-zero and NaN behavior. + /// + [Fact] + public void ClampDoublePreservesRuntimeSpecialValueSemantics() + { + double[] values = + { + double.NaN, + double.NegativeInfinity, + -0D, + 0D, + double.PositiveInfinity + }; + + double[] actual = new double[65]; + double[] expected = new double[actual.Length]; + + for (int i = 0; i < actual.Length; i++) + { + actual[i] = values[i % values.Length]; + expected[i] = double.Min(double.Max(actual[i], -0D), 0D); + } + + TensorPrimitives_.Clamp(actual, -0D, 0D, actual); + AssertDoubleBitsEqual(expected, actual); + } + + /// + /// Verifies that division produces identical results for separate and in-place destinations. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void DivideSingleMatchesScalarFormula(int length) + { + float[] source = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = (i - 65.25F) * 1.75F; + expected[i] = source[i] / 3.25F; + } + + float[] destination = new float[length]; + TensorPrimitives_.Divide(source, 3.25F, destination); + AssertSingleBitsEqual(expected, destination); + + float[] inPlace = (float[])source.Clone(); + TensorPrimitives_.Divide(inPlace, 3.25F, inPlace); + AssertSingleBitsEqual(expected, inPlace); + } + + /// + /// Verifies that maximum selection preserves the runtime's NaN and signed-zero semantics. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void MaxSingleMatchesRuntimeFormula(int length) + { + float[] values = + { + float.NaN, + float.NegativeInfinity, + -1F, + -0F, + 0F, + 1F, + float.PositiveInfinity + }; + + float[] actual = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < length; i++) + { + actual[i] = values[i % values.Length]; + expected[i] = float.Max(actual[i], -0F); + } + + TensorPrimitives_.Max(actual, -0F, actual); + AssertSingleBitsEqual(expected, actual); + } + + /// + /// Verifies that multiplication produces identical results for separate and in-place destinations. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void MultiplySingleMatchesScalarFormula(int length) + { + float[] source = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < source.Length; i++) + { + source[i] = (i - 65.25F) * 1.75F; + expected[i] = source[i] * 0.375F; + } + + float[] destination = new float[length]; + TensorPrimitives_.Multiply(source, 0.375F, destination); + AssertSingleBitsEqual(expected, destination); + + TensorPrimitives_.Multiply(source, 0.375F, source); + AssertSingleBitsEqual(expected, source); + } + + /// + /// Verifies that the normalization compatibility call preserves its element-wise division contract. + /// + /// The input length. + [Theory] + [MemberData(nameof(SpanLengths))] + public void NormalizeMatchesScalarFormula(int length) + { + float[] actual = new float[length]; + float[] expected = new float[length]; + + for (int i = 0; i < actual.Length; i++) + { + actual[i] = (i + 1) * 0.125F; + expected[i] = actual[i] / 7.5F; + } + + Numerics.Normalize(actual, 7.5F); + AssertSingleBitsEqual(expected, actual); + } + + /// + /// Compares floating-point results while preserving signed-zero behavior. + /// + /// The expected values. + /// The actual values. + private static void AssertSingleBitsEqual(ReadOnlySpan expected, ReadOnlySpan actual) + { + Assert.Equal(expected.Length, actual.Length); + + for (int i = 0; i < expected.Length; i++) + { + if (float.IsNaN(expected[i])) + { + Assert.True(float.IsNaN(actual[i])); + } + else + { + Assert.Equal(BitConverter.SingleToInt32Bits(expected[i]), BitConverter.SingleToInt32Bits(actual[i])); + } + } + } + + /// + /// Compares double-precision results while preserving signed-zero behavior. + /// + /// The expected values. + /// The actual values. + private static void AssertDoubleBitsEqual(ReadOnlySpan expected, ReadOnlySpan actual) + { + Assert.Equal(expected.Length, actual.Length); + + for (int i = 0; i < expected.Length; i++) + { + if (double.IsNaN(expected[i])) + { + Assert.True(double.IsNaN(actual[i])); + } + else + { + Assert.Equal(BitConverter.DoubleToInt64Bits(expected[i]), BitConverter.DoubleToInt64Bits(actual[i])); + } + } + } +} diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index c5d61726c8..ac1a18cf8e 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 = 13; public ConfigurationTests() { @@ -58,7 +58,7 @@ public void TestDefaultConfigurationMaxDegreeOfParallelism() { Assert.True(this.DefaultConfiguration.MaxDegreeOfParallelism == Environment.ProcessorCount); - var cfg = new Configuration(); + Configuration cfg = new(); Assert.True(cfg.MaxDegreeOfParallelism == Environment.ProcessorCount); } @@ -71,7 +71,7 @@ public void TestDefaultConfigurationMaxDegreeOfParallelism() [InlineData(5, false)] public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) { - var cfg = new Configuration(); + Configuration cfg = new(); if (throws) { Assert.Throws( @@ -87,8 +87,8 @@ public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeO [Fact] public void ConstructorCallConfigureOnFormatProvider() { - var provider = new Mock(); - var config = new Configuration(provider.Object); + Mock provider = new(); + Configuration config = new(provider.Object); provider.Verify(x => x.Configure(config)); } @@ -96,8 +96,8 @@ public void ConstructorCallConfigureOnFormatProvider() [Fact] public void AddFormatCallsConfig() { - var provider = new Mock(); - var config = new Configuration(); + Mock provider = new(); + Configuration config = new(); config.Configure(provider.Object); provider.Verify(x => x.Configure(config)); @@ -118,7 +118,7 @@ public void ConfigurationCannotAddDuplicates() [Fact] public void DefaultConfigurationHasCorrectFormatCount() { - var config = Configuration.CreateDefaultInstance(); + Configuration config = Configuration.CreateDefaultInstance(); Assert.Equal(this.expectedDefaultConfigurationCount, config.ImageFormats.Count()); } @@ -140,7 +140,7 @@ public void StreamBufferSize_DefaultIsCorrect() [Fact] public void StreamBufferSize_CannotGoBelowMinimum() { - var config = new Configuration(); + Configuration config = new(); Assert.Throws( () => config.StreamProcessingBufferSize = 0); @@ -150,14 +150,14 @@ public void StreamBufferSize_CannotGoBelowMinimum() public void MemoryAllocator_Setter_Roundtrips() { MemoryAllocator customAllocator = new SimpleGcMemoryAllocator(); - var config = new Configuration() { MemoryAllocator = customAllocator }; + Configuration config = new() { MemoryAllocator = customAllocator }; Assert.Same(customAllocator, config.MemoryAllocator); } [Fact] public void MemoryAllocator_SetNull_ThrowsArgumentNullException() { - var config = new Configuration(); + Configuration config = new(); Assert.Throws(() => config.MemoryAllocator = null); } @@ -168,9 +168,9 @@ public void InheritsDefaultMemoryAllocatorInstance() static void RunTest() { - var c1 = new Configuration(); - var c2 = new Configuration(new MockConfigurationModule()); - var c3 = Configuration.CreateDefaultInstance(); + Configuration c1 = new(); + Configuration c2 = new(new MockConfigurationModule()); + Configuration c3 = Configuration.CreateDefaultInstance(); Assert.Same(MemoryAllocator.Default, Configuration.Default.MemoryAllocator); Assert.Same(MemoryAllocator.Default, c1.MemoryAllocator); diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 533f0c6dee..7f87111e80 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -77,11 +77,11 @@ public void WorksWithDifferentConfigurations( PngEncoder encoder; if (provider.PixelType == PixelTypes.Rgba64) { - encoder = new() { BitDepth = PngBitDepth.Bit16 }; + encoder = new PngEncoder { BitDepth = PngBitDepth.Bit16 }; } else { - encoder = new(); + encoder = new PngEncoder(); } image.DebugSave(provider, testInfo, encoder: encoder); @@ -119,7 +119,7 @@ public void DrawImageOfDifferentPixelType(TestImageProvider prov 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 void WorksWithDifferentLocations(TestImageProvider provider, int 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)); @@ -293,4 +293,21 @@ public void Issue2603(TestImageProvider provider) appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void DrawImageAnimatedForegroundRepeatCount(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image background = provider.GetImage(); + using Image foreground = Image.Load(TestFile.Create(TestImages.Gif.Giphy).Bytes); + + Size size = new(foreground.Width / 4, foreground.Height / 4); + foreground.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic)); + + background.Mutate(x => x.DrawImage(foreground, Point.Empty, 1F, 0)); + + background.DebugSaveMultiFrame(provider); + background.CompareToReferenceOutputMultiFrame(provider, ImageComparer.TolerantPercentage(0.01f)); + } } diff --git a/tests/ImageSharp.Tests/Formats/Ani/AniDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Ani/AniDecoderTests.cs new file mode 100644 index 0000000000..93de4d2d9f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Ani/AniDecoderTests.cs @@ -0,0 +1,210 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Ani; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Ani; + +namespace SixLabors.ImageSharp.Tests.Formats.Ani; + +[Trait("Format", "Ani")] +[ValidateDisposedMemoryAllocations] +public class AniDecoderTests +{ + /// + /// Verifies that ANI animation steps and embedded CUR resolution variants are flattened with their ANI metadata. + /// + [Theory] + [WithFile(Work, PixelTypes.Rgba32, 17, 1, 6U, 6U)] + [WithFile(MultiFramesInEveryIconChunk, PixelTypes.Rgba32, 54, 3, 3U, 3U)] + [WithFile(Help, PixelTypes.Rgba32, 4, 1, 10U, 12U)] + public void AniDecoder_Decode( + TestImageProvider provider, + int expectedFrameCount, + int variantsPerStep, + uint expectedDisplayRate, + uint expectedFrameDelay) + { + using Image image = provider.GetImage(AniDecoder.Instance); + + Assert.Equal(expectedFrameCount, image.Frames.Count); + Assert.Equal(expectedDisplayRate, image.Metadata.GetAniMetadata().DisplayRate); + + for (int i = 0; i < image.Frames.Count; i++) + { + AniFrameMetadata metadata = image.Frames[i].Metadata.GetAniMetadata(); + + Assert.Equal((i / variantsPerStep) + 1, metadata.SequenceNumber); + Assert.Equal(expectedFrameDelay, metadata.FrameDelay); + Assert.Equal(AniFrameFormat.Cur, metadata.FrameFormat); + Assert.NotEqual(0, (int)metadata.BmpBitsPerPixel); + } + } + + /// + /// Verifies that identification exposes the same flattened ANI frame structure without decoding pixels. + /// + [Theory] + [InlineData(Work, 17)] + [InlineData(MultiFramesInEveryIconChunk, 54)] + [InlineData(Help, 4)] + public void AniDecoder_Identify(string path, int expectedFrameCount) + { + TestFile file = TestFile.Create(path); + using MemoryStream stream = new(file.Bytes, false); + + ImageInfo info = AniDecoder.Instance.Identify(DecoderOptions.Default, stream); + + Assert.Equal(expectedFrameCount, info.FrameMetadataCollection.Count); + + for (int i = 0; i < info.FrameMetadataCollection.Count; i++) + { + Assert.True(info.FrameMetadataCollection[i].GetAniMetadata().SequenceNumber > 0); + } + } + + /// + /// Verifies that invalid sequence metadata follows the ancillary-segment integrity policy. + /// + [Fact] + public void AniDecoder_InvalidSequenceReference_FollowsIntegrityHandling() + { + byte[] data = TestFile.Create(Help).Bytes.ToArray(); + int sequenceOffset = data.AsSpan().IndexOf("seq "u8); + Assert.True(sequenceOffset >= 0); + + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(sequenceOffset + AniConstants.ChunkHeaderSize), uint.MaxValue); + + using MemoryStream strictStream = new(data, false); + DecoderOptions strict = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => AniDecoder.Instance.Decode(strict, strictStream)); + + using MemoryStream ignoreStream = new(data, false); + DecoderOptions ignore = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreAncillary }; + using Image image = AniDecoder.Instance.Decode(ignore, ignoreStream); + + Assert.Equal(3, image.Frames.Count); + } + + /// + /// Verifies that ignored corrupt resources retain their sequence-table slot. + /// + [Fact] + public void AniDecoder_UnsupportedResource_FollowsIntegrityHandling() + { + byte[] data = TestFile.Create(Work).Bytes.ToArray(); + int resourceOffset = data.AsSpan().IndexOf("icon"u8); + Assert.True(resourceOffset >= 0); + + int iconTypeOffset = resourceOffset + AniConstants.ChunkHeaderSize + sizeof(ushort); + BinaryPrimitives.WriteUInt16LittleEndian(data.AsSpan(iconTypeOffset), ushort.MaxValue); + + using MemoryStream strictStream = new(data, false); + DecoderOptions strict = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => AniDecoder.Instance.Decode(strict, strictStream)); + + using MemoryStream ignoreStream = new(data, false); + DecoderOptions ignore = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData }; + using Image image = AniDecoder.Instance.Decode(ignore, ignoreStream); + + Assert.Equal(16, image.Frames.Count); + Assert.Equal(2, image.Frames.RootFrame.Metadata.GetAniMetadata().SequenceNumber); + } + + /// + /// Verifies that a malformed rate chunk follows the ancillary-segment integrity policy. + /// + [Fact] + public void AniDecoder_InvalidRateChunk_FollowsIntegrityHandling() + { + byte[] source = TestFile.Create(Help).Bytes.ToArray(); + int rateOffset = source.AsSpan().IndexOf("rate"u8); + Assert.True(rateOffset >= 0); + + int rateSizeOffset = rateOffset + sizeof(uint); + int rateSize = (int)BinaryPrimitives.ReadUInt32LittleEndian(source.AsSpan(rateSizeOffset)); + int rateEnd = rateOffset + AniConstants.ChunkHeaderSize + rateSize; + byte[] data = new byte[source.Length + 2]; + source.AsSpan(0, rateEnd).CopyTo(data); + source.AsSpan(rateEnd).CopyTo(data.AsSpan(rateEnd + 2)); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(rateSizeOffset), (uint)rateSize + 2); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(sizeof(uint)), (uint)data.Length - 8); + + using MemoryStream defaultStream = new(data, false); + using Image image = AniDecoder.Instance.Decode(DecoderOptions.Default, defaultStream); + + Assert.Equal(4, image.Frames.Count); + foreach (ImageFrame frame in image.Frames) + { + Assert.Equal(10U, frame.Metadata.GetAniMetadata().FrameDelay); + } + + using MemoryStream strictStream = new(data, false); + DecoderOptions strict = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => AniDecoder.Instance.Decode(strict, strictStream)); + } + + /// + /// Verifies that oversized control arrays are rejected before allocation and follow ancillary integrity handling. + /// + /// to append a sequence chunk; otherwise, a rate chunk. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AniDecoder_OversizedControlChunk_FollowsIntegrityHandling(bool sequence) + { + byte[] source = TestFile.Create(Help).Bytes.ToArray(); + int chunkOffset = (source.Length + 1) & ~1; + int payloadSize = AniConstants.MaxAncillaryChunkSize + sizeof(uint); + byte[] data = new byte[chunkOffset + AniConstants.ChunkHeaderSize + payloadSize]; + source.CopyTo(data, 0); + + ReadOnlySpan identifier = sequence ? "seq "u8 : "rate"u8; + identifier.CopyTo(data.AsSpan(chunkOffset)); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(chunkOffset + sizeof(uint)), (uint)payloadSize); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(sizeof(uint)), (uint)data.Length - AniConstants.ChunkHeaderSize); + + using MemoryStream defaultStream = new(data, false); + using Image image = AniDecoder.Instance.Decode(DecoderOptions.Default, defaultStream); + + Assert.Equal(4, image.Frames.Count); + + using MemoryStream strictStream = new(data, false); + DecoderOptions strict = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => AniDecoder.Instance.Decode(strict, strictStream)); + } + + /// + /// Verifies that oversized information text is rejected before allocation and follows ancillary integrity handling. + /// + [Fact] + public void AniDecoder_OversizedInformationText_FollowsIntegrityHandling() + { + byte[] source = TestFile.Create(Help).Bytes.ToArray(); + int listOffset = (source.Length + 1) & ~1; + int textSize = AniConstants.MaxAncillaryChunkSize + 1; + int paddedTextSize = textSize + (textSize & 1); + int listSize = sizeof(uint) + AniConstants.ChunkHeaderSize + paddedTextSize; + byte[] data = new byte[listOffset + AniConstants.ChunkHeaderSize + listSize]; + source.CopyTo(data, 0); + + "LIST"u8.CopyTo(data.AsSpan(listOffset)); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(listOffset + sizeof(uint)), (uint)listSize); + "INFO"u8.CopyTo(data.AsSpan(listOffset + AniConstants.ChunkHeaderSize)); + int textOffset = listOffset + AniConstants.ChunkHeaderSize + sizeof(uint); + "INAM"u8.CopyTo(data.AsSpan(textOffset)); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(textOffset + sizeof(uint)), (uint)textSize); + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(sizeof(uint)), (uint)data.Length - AniConstants.ChunkHeaderSize); + + using MemoryStream defaultStream = new(data, false); + using Image image = AniDecoder.Instance.Decode(DecoderOptions.Default, defaultStream); + + Assert.Equal(4, image.Frames.Count); + + using MemoryStream strictStream = new(data, false); + DecoderOptions strict = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => AniDecoder.Instance.Decode(strict, strictStream)); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Ani/AniEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Ani/AniEncoderTests.cs new file mode 100644 index 0000000000..8b0246c504 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Ani/AniEncoderTests.cs @@ -0,0 +1,242 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using SixLabors.ImageSharp.Formats.Ani; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestDataIcc; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using static SixLabors.ImageSharp.Tests.TestImages.Ani; + +namespace SixLabors.ImageSharp.Tests.Formats.Ani; + +[Trait("Format", "Ani")] +[ValidateDisposedMemoryAllocations] +public class AniEncoderTests +{ + /// + /// Verifies that ANI resources, including multi-resolution CUR resources, survive an encode/decode round trip. + /// + [Theory] + [WithFile(Work, PixelTypes.Rgba32)] + [WithFile(MultiFramesInEveryIconChunk, PixelTypes.Rgba32)] + [WithFile(Help, PixelTypes.Rgba32)] + public void AniEncoder_RoundTrips(TestImageProvider provider) + { + using Image image = provider.GetImage(AniDecoder.Instance); + using MemoryStream stream = new(); + + image.Save(stream, new AniEncoder()); + + // The RIFF size covers everything after its identifier and size field. + Assert.Equal(stream.Length - 8, BinaryPrimitives.ReadUInt32LittleEndian(stream.GetBuffer().AsSpan(4, sizeof(uint)))); + + stream.Position = 0; + using Image decoded = Image.Load(stream); + + ImageComparer.Exact.VerifySimilarity(image, decoded); + Assert.Equal(image.Frames.Count, decoded.Frames.Count); + + for (int i = 0; i < image.Frames.Count; i++) + { + AniFrameMetadata expected = image.Frames[i].Metadata.GetAniMetadata(); + AniFrameMetadata actual = decoded.Frames[i].Metadata.GetAniMetadata(); + + Assert.Equal(expected.SequenceNumber, actual.SequenceNumber); + Assert.Equal(expected.FrameDelay, actual.FrameDelay); + Assert.Equal(expected.FrameFormat, actual.FrameFormat); + Assert.Equal(expected.EncodingWidth, actual.EncodingWidth); + Assert.Equal(expected.EncodingHeight, actual.EncodingHeight); + Assert.Equal(expected.Compression, actual.Compression); + Assert.Equal(expected.BmpBitsPerPixel, actual.BmpBitsPerPixel); + Assert.Equal(expected.HotspotX, actual.HotspotX); + Assert.Equal(expected.HotspotY, actual.HotspotY); + Assert.Equal(expected.ColorTable?.ToArray(), actual.ColorTable?.ToArray()); + } + } + + /// + /// Verifies that per-step rates and RIFF information metadata are emitted and decoded. + /// + [Theory] + [WithFile(Work, PixelTypes.Rgba32)] + public void AniEncoder_WritesVariableRatesAndInformation(TestImageProvider provider) + { + using Image image = provider.GetImage(AniDecoder.Instance); + AniMetadata imageMetadata = image.Metadata.GetAniMetadata(); + imageMetadata.Name = "ImageSharp ANI"; + imageMetadata.Artist = "Six Labors"; + + for (int i = 0; i < image.Frames.Count; i++) + { + image.Frames[i].Metadata.GetAniMetadata().FrameDelay = (uint)(i + 1); + } + + using MemoryStream stream = new(); + image.Save(stream, new AniEncoder()); + + Assert.Equal(stream.Length - 8, BinaryPrimitives.ReadUInt32LittleEndian(stream.GetBuffer().AsSpan(4, sizeof(uint)))); + + stream.Position = 0; + using Image decoded = Image.Load(stream); + AniMetadata decodedMetadata = decoded.Metadata.GetAniMetadata(); + + Assert.Equal(imageMetadata.Name, decodedMetadata.Name); + Assert.Equal(imageMetadata.Artist, decodedMetadata.Artist); + + for (int i = 0; i < decoded.Frames.Count; i++) + { + Assert.Equal((uint)(i + 1), decoded.Frames[i].Metadata.GetAniMetadata().FrameDelay); + } + } + + /// + /// Verifies the encoder and decoder paths for embedded ICO and BMP resources. + /// + [Theory] + [InlineData(AniFrameFormat.Ico)] + [InlineData(AniFrameFormat.Bmp)] + public void AniEncoder_RoundTripsOtherFrameFormats(AniFrameFormat frameFormat) + { + using Image image = new(16, 16, Color.Red.ToPixel()); + AniMetadata imageMetadata = image.Metadata.GetAniMetadata(); + imageMetadata.DisplayRate = 6; + imageMetadata.BitCount = 32; + imageMetadata.Planes = 1; + + AniFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetAniMetadata(); + frameMetadata.FrameDelay = 6; + frameMetadata.SequenceNumber = 1; + frameMetadata.FrameFormat = frameFormat; + frameMetadata.Compression = IconFrameCompression.Bmp; + + using MemoryStream stream = new(); + image.Save(stream, new AniEncoder()); + + Assert.Equal(stream.Length - 8, BinaryPrimitives.ReadUInt32LittleEndian(stream.GetBuffer().AsSpan(4, sizeof(uint)))); + + if (frameFormat is AniFrameFormat.Bmp) + { + ReadOnlySpan encoded = stream.GetBuffer().AsSpan(0, (int)stream.Length); + int frameChunkOffset = encoded.IndexOf("icon"u8); + Assert.True(frameChunkOffset >= 0); + + // AF_ICON-clear resources contain a headerless BMP DIB, not a standalone file beginning with BITMAPFILEHEADER. + ReadOnlySpan frameData = encoded[(frameChunkOffset + AniConstants.ChunkHeaderSize)..]; + Assert.False(frameData.StartsWith("BM"u8)); + } + + stream.Position = 0; + using Image decoded = Image.Load(stream); + + ImageComparer.Exact.VerifySimilarity(image, decoded); + Assert.Equal(frameFormat, decoded.Frames.RootFrame.Metadata.GetAniMetadata().FrameFormat); + } + + /// + /// Verifies that metadata suppression is propagated to every embedded resource encoder. + /// + [Theory] + [InlineData(AniFrameFormat.Ico)] + [InlineData(AniFrameFormat.Cur)] + [InlineData(AniFrameFormat.Bmp)] + public void AniEncoder_SkipMetadataPropagatesToEmbeddedEncoder(AniFrameFormat frameFormat) + { + using Image image = new(16, 16, Color.Red.ToPixel()); + image.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.ProfileRandomArray); + + AniMetadata imageMetadata = image.Metadata.GetAniMetadata(); + imageMetadata.BitCount = 32; + imageMetadata.Planes = 1; + + AniFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetAniMetadata(); + frameMetadata.FrameFormat = frameFormat; + frameMetadata.Compression = IconFrameCompression.Bmp; + frameMetadata.BmpBitsPerPixel = BmpBitsPerPixel.Bit32; + + using MemoryStream stream = new(); + image.Save(stream, new AniEncoder { SkipMetadata = true }); + + ReadOnlySpan encoded = stream.GetBuffer().AsSpan(0, (int)stream.Length); + int frameChunkOffset = encoded.IndexOf("icon"u8); + Assert.True(frameChunkOffset >= 0); + + ReadOnlySpan resource = encoded[(frameChunkOffset + AniConstants.ChunkHeaderSize)..]; + int dibOffset = 0; + + if (frameFormat is not AniFrameFormat.Bmp) + { + dibOffset = checked((int)BinaryPrimitives.ReadUInt32LittleEndian(resource[(IconDir.Size + IconDirEntry.Size - sizeof(uint))..])); + } + + // Metadata-free ICO/CUR bitmaps use BITMAPINFOHEADER, while raw transparent ANI bitmaps require BITMAPV4HEADER. + int expectedHeaderSize = frameFormat is AniFrameFormat.Bmp ? BmpInfoHeader.SizeV4 : BmpInfoHeader.SizeV3; + Assert.Equal(expectedHeaderSize, BinaryPrimitives.ReadInt32LittleEndian(resource[dibOffset..])); + } + + /// + /// Verifies that an independent frame cannot collide with an explicit sequence group. + /// + [Fact] + public void AniEncoder_NonPositiveSequenceDoesNotCollideWithExplicitGroup() + { + using Image image = new(16, 16, Color.Red.ToPixel()); + image.Frames.AddFrame(image.Frames.RootFrame); + image.Frames[1].Metadata.GetAniMetadata().SequenceNumber = 1; + + using MemoryStream stream = new(); + image.Save(stream, new AniEncoder()); + + stream.Position = 0; + using Image decoded = Image.Load(stream); + + Assert.Equal(2, decoded.Frames.Count); + Assert.Equal(1, decoded.Frames[0].Metadata.GetAniMetadata().SequenceNumber); + Assert.Equal(2, decoded.Frames[1].Metadata.GetAniMetadata().SequenceNumber); + } + + /// + /// Verifies that an explicit source sequence is preserved as an identity table after playback-order expansion. + /// + [Fact] + public void AniEncoder_PreservesExplicitSequence() + { + using Image image = new(16, 16, Color.Red.ToPixel()); + image.Frames.AddFrame(image.Frames.RootFrame); + image.Metadata.GetAniMetadata().Flags = AniHeaderFlags.IsIcon | AniHeaderFlags.ContainsSequence; + + using MemoryStream stream = new(); + image.Save(stream, new AniEncoder()); + + ReadOnlySpan data = stream.GetBuffer().AsSpan(0, (int)stream.Length); + int sequenceOffset = data.IndexOf("seq "u8); + + Assert.True(sequenceOffset >= 0); + Assert.Equal((uint)(2 * sizeof(uint)), BinaryPrimitives.ReadUInt32LittleEndian(data[(sequenceOffset + sizeof(uint))..])); + Assert.Equal(0U, BinaryPrimitives.ReadUInt32LittleEndian(data[(sequenceOffset + AniConstants.ChunkHeaderSize)..])); + Assert.Equal(1U, BinaryPrimitives.ReadUInt32LittleEndian(data[(sequenceOffset + AniConstants.ChunkHeaderSize + sizeof(uint))..])); + + stream.Position = 0; + using Image decoded = Image.Load(stream); + + Assert.True(decoded.Metadata.GetAniMetadata().Flags.HasFlag(AniHeaderFlags.ContainsSequence)); + } + + /// + /// Verifies that unsupported public frame metadata is rejected before any container data is written. + /// + [Fact] + public void AniEncoder_UnsupportedFrameFormatThrowsBeforeWriting() + { + using Image image = new(16, 16); + image.Frames.RootFrame.Metadata.GetAniMetadata().FrameFormat = (AniFrameFormat)byte.MaxValue; + + using MemoryStream stream = new(); + + Assert.Throws(() => image.Save(stream, new AniEncoder())); + Assert.Equal(0, stream.Length); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Ani/AniMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Ani/AniMetadataTests.cs new file mode 100644 index 0000000000..676556827a --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Ani/AniMetadataTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Ani; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Tests.Formats.Ani; + +[Trait("Format", "Ani")] +public class AniMetadataTests +{ + /// + /// Verifies that resizing scales the ANI-owned encoding dimensions exactly once. + /// + [Fact] + public void AfterFrameApply_ScalesEncodingDimensionsOnce() + { + using Image image = new(32, 32); + AniFrameMetadata metadata = image.Frames.RootFrame.Metadata.GetAniMetadata(); + metadata.EncodingWidth = 32; + metadata.EncodingHeight = 32; + metadata.FrameFormat = AniFrameFormat.Cur; + + image.Mutate(context => context.Resize(64, 64)); + + AniFrameMetadata resized = image.Frames.RootFrame.Metadata.GetAniMetadata(); + Assert.Equal((byte)64, resized.EncodingWidth); + Assert.Equal((byte)64, resized.EncodingHeight); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 1ce794e44b..1702657892 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; @@ -33,6 +34,41 @@ public class BmpDecoderTests { RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; + [Theory] + [InlineData(SegmentIntegrityHandling.Strict, false)] + [InlineData(SegmentIntegrityHandling.IgnoreAncillary, false)] + [InlineData(SegmentIntegrityHandling.IgnoreImageData, false)] + [InlineData(SegmentIntegrityHandling.Strict, true)] + [InlineData(SegmentIntegrityHandling.IgnoreAncillary, true)] + [InlineData(SegmentIntegrityHandling.IgnoreImageData, true)] + public void Decode_WithProfileLargerThanRemainingData_RespectsOptions(SegmentIntegrityHandling integrityHandling, bool skipMetadata) + { + byte[] payload = Convert.FromHexString( + "424D8E000000000000008A0000007C0000000100000001000000010018000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000C800" + + "00000000004000000000000000"); + DecoderOptions options = new() { SegmentIntegrityHandling = integrityHandling, SkipMetadata = skipMetadata }; + + if (integrityHandling is SegmentIntegrityHandling.Strict && !skipMetadata) + { + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + else + { + using Image image = Image.Load(options, payload); + Assert.Equal(new Size(1, 1), image.Size); + Assert.Equal(new Rgba32(0, 0, 0), image[0, 0]); + Assert.Null(image.Metadata.IccProfile); + + ImageInfo info = Image.Identify(options, payload); + Assert.Equal(image.Size, info.Size); + Assert.Null(info.Metadata.IccProfile); + } + } + [Theory] [WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_MiscellaneousBitmaps(TestImageProvider provider) @@ -90,13 +126,22 @@ public void BmpDecoder_CanDecodeBitfields(TestImageProvider prov { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToReferenceOutput(provider); } [Theory] [WithFile(Bit16Inverted, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_16Bit_Inverted(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(BmpDecoder.Instance); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + + [Theory] [WithFile(Bit8Inverted, PixelTypes.Rgba32)] - public void BmpDecoder_CanDecode_Inverted(TestImageProvider provider) + public void BmpDecoder_CanDecode_8Bit_Inverted(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(BmpDecoder.Instance); @@ -112,7 +157,7 @@ public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder(BmpFormat.Instance)); } [Theory] @@ -155,7 +200,7 @@ public void BmpDecoder_CanDecode_16Bit(TestImageProvider provide { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder(BmpFormat.Instance)); } [Theory] @@ -185,12 +230,12 @@ public void BmpDecoder_CanDecode_32BitV4Header_Fast(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - RleSkippedPixelHandling skippedPixelHandling = TestEnvironment.IsWindows ? RleSkippedPixelHandling.Black : RleSkippedPixelHandling.FirstColorOfPalette; + RleSkippedPixelHandling skippedPixelHandling = RleSkippedPixelHandling.Black; BmpDecoderOptions options = new() { RleSkippedPixelHandling = skippedPixelHandling }; using Image image = provider.GetImage(BmpDecoder.Instance, options); image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToReferenceOutput(provider); } [Theory] @@ -219,12 +264,12 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRe image.DebugSave(provider); if (TestEnvironment.IsWindows) { - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder(BmpFormat.Instance)); } } + // An RLE-compressed image that uses “delta” codes, to skip over some pixels. [Theory] - [WithFile(RLE8Cut, PixelTypes.Rgba32)] [WithFile(RLE8Delta, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel @@ -232,14 +277,24 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecode 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); + } + + // An RLE-compressed image that uses “delta” codes, and early EOL & EOBMP markers, to skip over some pixels. + [Theory] + [WithFile(RLE8Cut, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDeltaAndEOL_MagickRefDecoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; + using Image image = provider.GetImage(BmpDecoder.Instance, options); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } [Theory] [WithFile(RLE8, PixelTypes.Rgba32, false)] - [WithFile(RLE8Inverted, PixelTypes.Rgba32, false)] [WithFile(RLE8, PixelTypes.Rgba32, true)] - [WithFile(RLE8Inverted, PixelTypes.Rgba32, true)] public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider, bool enforceDiscontiguousBuffers) where TPixel : unmanaged, IPixel { @@ -251,7 +306,26 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider 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] + [WithFile(RLE8Inverted, PixelTypes.Rgba32, false)] + [WithFile(RLE8Inverted, PixelTypes.Rgba32, true)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_Inverted(TestImageProvider provider, bool enforceDiscontiguousBuffers) + where TPixel : unmanaged, IPixel + { + if (enforceDiscontiguousBuffers) + { + provider.LimitAllocatorBufferCapacity().InBytesSqrt(400); + } + + BmpDecoderOptions options = new() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette }; + using Image image = provider.GetImage(BmpDecoder.Instance, options); + image.DebugSave(provider); + + // The Reference decoder does not support decoding compressed bmp which are inverted (with negative height). + image.CompareToReferenceOutput(provider); } [Theory] @@ -298,7 +372,7 @@ public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvide { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -314,7 +388,7 @@ public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageP // 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 +401,7 @@ public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider 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 +421,7 @@ public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -394,7 +468,7 @@ public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider pro { using Image image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -404,7 +478,7 @@ public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha(TestImageProvider image = provider.GetImage(BmpDecoder.Instance); image.DebugSave(provider); - image.CompareToOriginal(provider, new MagickReferenceDecoder()); + image.CompareToOriginal(provider, MagickReferenceDecoder.Png); } [Theory] @@ -477,7 +551,7 @@ public void Identify_DetectsCorrectPixelType(string imagePath, int expectedPixel 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] @@ -570,4 +644,56 @@ public void BmpDecoder_ThrowsException_Issue2696(TestImageProvider(ex.InnerException); } + + // https://github.com/SixLabors/ImageSharp/issues/3067 + [Fact] + public void BmpDecoder_ThrowsException_Issue3067() + { + // Construct minimal BMP with bitsPerPixel = 0 + byte[] bmp = new byte[54]; + bmp[0] = (byte)'B'; + bmp[1] = (byte)'M'; + BitConverter.GetBytes(54).CopyTo(bmp, 2); + BitConverter.GetBytes(54).CopyTo(bmp, 10); + BitConverter.GetBytes(40).CopyTo(bmp, 14); + BitConverter.GetBytes(1).CopyTo(bmp, 18); + BitConverter.GetBytes(1).CopyTo(bmp, 22); + BitConverter.GetBytes((short)1).CopyTo(bmp, 26); + BitConverter.GetBytes((short)0).CopyTo(bmp, 28); // bitsPerPixel = 0 + + using MemoryStream stream = new(bmp); + + Assert.Throws(() => + { + using Image image = BmpDecoder.Instance.Decode(DecoderOptions.Default, stream); + }); + } + + // https://github.com/SixLabors/ImageSharp/issues/3074 + [Fact] + public void BmpDecoder_ThrowsException_Issue3074() + { + // Crafted BMP: pixel data offset = 0x7FFFFFFF, actual file = 35 bytes + byte[] data = + [ + 0x42, 0x4D, // "BM" signature + 0x3A, 0x00, 0x00, 0x00, // file size: 58 + 0x00, 0x00, 0x00, 0x00, // reserved + 0xFF, 0xFF, 0xFF, 0x7F, // pixel offset: 0x7FFFFFFF (2,147,483,647) + 0x28, 0x00, 0x00, 0x00, // DIB header size: 40 + 0x01, 0x00, 0x00, 0x00, // width: 1 + 0x01, 0xFF, 0x00, 0x00, // height: 65281 + 0x01, 0x00, // color planes: 1 + 0x08, 0x00, // bits per pixel: 8 + 0x00, 0x00, 0x00, 0x00, // compression: RGB + 0x00, 0x00, 0x00 // (truncated) + ]; + + using MemoryStream stream = new(data); + + Assert.Throws(() => + { + using Image image = Image.Load(stream); + }); + } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 42cbd90f3b..6bd7e0103f 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.Pixel24, - BmpBitsPerPixel.Pixel32 - }; + new() + { + BmpBitsPerPixel.Bit24, + BmpBitsPerPixel.Bit32 + }; public static readonly TheoryData RatioFiles = new() @@ -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 void Encode_WorksWithDifferentSizes(TestImageProvider pro 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 void Encode_8BitGray_WithV3Header_Works(TestImageProvider 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 void Encode_4Bit_WithV3Header_Works( } [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 void Encode_4Bit_WithV4Header_Works( } [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 void Encode_2Bit_WithV3Header_Works( } [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 void Encode_2Bit_WithV4Header_Works( } [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 void Encode_8BitColor_WithWuQuantizer(TestImageProvider p using Image image = provider.GetImage(); BmpEncoder encoder = new() { - BitsPerPixel = BmpBitsPerPixel.Pixel8, + BitsPerPixel = BmpBitsPerPixel.Bit8, Quantizer = new WuQuantizer() }; @@ -287,12 +287,12 @@ public void Encode_8BitColor_WithWuQuantizer(TestImageProvider p provider, extension: "bmp", appendPixelTypeToFileName: false, - decoder: new MagickReferenceDecoder(false)); + decoder: new MagickReferenceDecoder(BmpFormat.Instance, false)); } [Theory] [WithFile(Bit32Rgb, PixelTypes.Rgba32)] - public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider provider) + public void Encode_8BitColor_WithHexadecatreeQuantizer(TestImageProvider provider) where TPixel : unmanaged, IPixel { if (!TestEnvironment.Is64BitProcess) @@ -303,8 +303,8 @@ public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider image = provider.GetImage(); BmpEncoder encoder = new() { - BitsPerPixel = BmpBitsPerPixel.Pixel8, - Quantizer = new OctreeQuantizer() + BitsPerPixel = BmpBitsPerPixel.Bit8, + Quantizer = new HexadecatreeQuantizer() }; string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false); @@ -318,12 +318,12 @@ public void Encode_8BitColor_WithOctreeQuantizer(TestImageProvider(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); @@ -332,7 +332,7 @@ public void Encode_PreservesAlpha(TestImageProvider provider, Bm public void Encode_PreservesColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image input = provider.GetImage(BmpDecoder.Instance, new()); + using Image input = provider.GetImage(BmpDecoder.Instance, new BmpDecoderOptions()); ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; byte[] expectedProfileBytes = expectedProfile.ToByteArray(); @@ -364,8 +364,8 @@ public void Encode_WorksWithSizeGreaterThen65k(int width, int height) } [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,18 +374,18 @@ public void Encode_WorksWithDiscontiguousBuffers(TestImageProvider(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel { 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, - Quantizer = KnownQuantizers.Octree + Quantizer = KnownQuantizers.Hexadecatree }; image.SaveAsBmp(reencodedStream, encoder); reencodedStream.Seek(0, SeekOrigin.Begin); @@ -397,6 +397,66 @@ public void Encode_Issue2467(TestImageProvider provider, BmpBits reencodedImage.CompareToOriginal(provider); } + [Fact] + public void Encode_WithTransparentColorBehaviorClear_Works() + { + // arrange + using Image image = new(50, 50); + BmpEncoder encoder = new() + { + BitsPerPixel = BmpBitsPerPixel.Bit32, + SupportTransparency = true, + TransparentColorMode = TransparentColorMode.Clear, + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + 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 = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } + private static void TestBmpEncoderCore( TestImageProvider provider, BmpBitsPerPixel bitsPerPixel, @@ -409,7 +469,7 @@ private static void TestBmpEncoderCore( 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()); } @@ -418,7 +478,7 @@ private static void TestBmpEncoderCore( { BitsPerPixel = bitsPerPixel, SupportTransparency = supportTransparency, - Quantizer = quantizer ?? KnownQuantizers.Octree + Quantizer = quantizer ?? KnownQuantizers.Hexadecatree }; // Does DebugSave & load reference CompareToReferenceInput(): diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs index d8ec7c9009..00a19466a3 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs @@ -11,9 +11,9 @@ public class BmpFileHeaderTests [Fact] public void TestWrite() { - var header = new BmpFileHeader(1, 2, 3, 4); + BmpFileHeader header = new(1, 2, 3, 4); - var buffer = new byte[14]; + byte[] buffer = new byte[14]; header.WriteTo(buffer); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index 1d84356713..bbfa4ac598 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -1,8 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using static SixLabors.ImageSharp.Tests.TestImages.Bmp; // ReSharper disable InconsistentNaming @@ -15,10 +18,10 @@ public class BmpMetadataTests public void CloneIsDeep() { BmpMetadata meta = new() - { BitsPerPixel = BmpBitsPerPixel.Pixel24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; - BmpMetadata clone = (BmpMetadata)meta.DeepClone(); + { BitsPerPixel = BmpBitsPerPixel.Bit24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; + BmpMetadata clone = meta.DeepClone(); - clone.BitsPerPixel = BmpBitsPerPixel.Pixel32; + clone.BitsPerPixel = BmpBitsPerPixel.Bit32; clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2; Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel)); @@ -46,7 +49,7 @@ public void Identify_DetectsCorrectBitmapInfoHeaderType(string imagePath, BmpInf } [Theory] - [WithFile(IccProfile, PixelTypes.Rgba32)] + [WithFile(TestImages.Bmp.IccProfile, PixelTypes.Rgba32)] public void Decoder_CanReadColorProfile(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -56,4 +59,40 @@ public void Decoder_CanReadColorProfile(TestImageProvider provid Assert.NotNull(metaData.IccProfile); Assert.Equal(16, metaData.IccProfile.Entries.Length); } + + [Fact] + public void Identify_MalformedIccProfile_IgnoresNonCriticalErrorsByDefault() + { + ImageInfo info = Image.Identify(CreateBmpWithMalformedIccProfile()); + Assert.Equal(1, info.Width); + Assert.Equal(1, info.Height); + } + + [Fact] + public void Decode_MalformedIccProfile_IgnoresNonCriticalErrorsByDefault() + { + using Image image = Image.Load(CreateBmpWithMalformedIccProfile()); + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + } + + [Fact] + public void Identify_MalformedIccProfile_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => Image.Identify(options, CreateBmpWithMalformedIccProfile())); + } + + [Fact] + public void Decode_MalformedIccProfile_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => + { + using Image image = Image.Load(options, CreateBmpWithMalformedIccProfile()); + }); + } + + private static byte[] CreateBmpWithMalformedIccProfile() + => CorruptedMetadataImageFactory.CreateImageWithMalformedIccProfile(new BmpEncoder()); } diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderSecurityTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderSecurityTests.cs new file mode 100644 index 0000000000..2d59c02355 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderSecurityTests.cs @@ -0,0 +1,274 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +/// +/// Security regression tests for the EXR decoder (Findings EXR-1, EXR-2, EXR-3). +/// The EXR decoder was merged to main but not yet included in a tagged NuGet release. +/// Each test demonstrates a crafted-input crash present in the unfixed code. +/// +[Trait("Format", "Exr")] +[ValidateDisposedMemoryAllocations] +public class ExrDecoderSecurityTests +{ + /// + /// EXR-1 — EXR DataWindow Integer Overflow Produces Negative Image Dimensions (DoS) + /// + /// Width and Height are computed from attacker-controlled DataWindow attributes + /// using unchecked int subtraction: + /// this.Width = XMax - XMin + 1 // overflows to -2147483647 + /// + /// The negative Width is then passed to the Image<TPixel> constructor, which calls + /// Guard.MustBeGreaterThan(width, 0) → ArgumentOutOfRangeException. + /// + /// After a fix this should throw InvalidImageContentException instead. + /// + /// Affected file: + /// src/ImageSharp/Formats/Exr/ExrDecoderCore.cs lines 600–601 + /// + [Fact] + public void Decode_DataWindowOverflow_NegativeWidth_Throws() + { + // XMin = -1073741825, XMax = 1073741823 + // Width = 1073741823 - (-1073741825) + 1 = 2^31 + 1 → wraps to -2147483647 + byte[] data = BuildMinimalExr(xMin: -1073741825, yMin: 0, xMax: 1073741823, yMax: 0); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + /// + /// EXR-2 — EXR Row Offset Table Unvalidated Seek (DoS) + /// + /// Row offsets are read from the file and used unconditionally to seek the stream: + /// ulong rowOffset = this.ReadUnsignedLong(stream); + /// stream.Position = (long)rowOffset; // no bounds check + /// + /// A crafted offset of 0xFFFFFFFFFFFFFFFF casts to −1 as long, causing + /// an ArgumentOutOfRangeException when setting stream.Position. + /// + /// After a fix this should throw InvalidImageContentException instead. + /// + /// Affected file: + /// src/ImageSharp/Formats/Exr/ExrDecoderCore.cs lines 170–175 (scanline) + /// lines 243–248 (tile) + /// + [Fact] + public void Decode_CraftedRowOffsets_OutOfBounds_Throws() + { + // Valid 2×2 image (XMin=0,YMin=0,XMax=1,YMax=1 → Width=2,Height=2). + // Row offset table immediately follows the header null byte: + // 2 rows × 8 bytes each, all set to 0xFFFFFFFFFFFFFFFF. + byte[] invalidOffsets = new byte[16]; + BinaryPrimitives.WriteUInt64LittleEndian(invalidOffsets, 0xFFFFFFFFFFFFFFFF); + BinaryPrimitives.WriteUInt64LittleEndian(invalidOffsets.AsSpan(8), 0xFFFFFFFFFFFFFFFF); + + byte[] data = BuildMinimalExr( + xMin: 0, yMin: 0, xMax: 1, yMax: 1, + rowOffsetTableAppend: invalidOffsets); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + [Fact] + public void Decode_CraftedRowOffsets_IntoHeader_Throws() + { + // Offset 0 points back into the EXR file header and must be rejected + // before the decoder seeks to attacker-controlled non-pixel data. + byte[] headerOffsets = new byte[16]; + + byte[] data = BuildMinimalExr( + xMin: 0, yMin: 0, xMax: 1, yMax: 1, + rowOffsetTableAppend: headerOffsets); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + [Fact] + public void Decode_CraftedRowOffsets_IntoOffsetTable_Throws() + { + byte[] data = BuildMinimalExr( + xMin: 0, yMin: 0, xMax: 1, yMax: 1, + rowOffsetTableAppend: new byte[16]); + + // Point the first row offset at the second row offset entry. + BinaryPrimitives.WriteUInt64LittleEndian(data.AsSpan(data.Length - 16), (ulong)(data.Length - 8)); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + /// + /// EXR-3 — Oversized EXR RGBA row sizing is rejected as invalid image content. + /// + /// With 4 RGBA HALF channels and Width = 2^29, the decoded row staging and + /// bytes-per-row arithmetic both exceed the supported buffer sizing limits. + /// The decoder must reject this as InvalidImageContentException before any allocation. + /// + /// Affected file: + /// src/ImageSharp/Formats/Exr/ExrDecoderCore.cs lines 142–150, 215–223 + /// src/ImageSharp/Formats/Exr/ExrUtils.cs CalculateBytesPerRow + /// + [Fact] + public void Decode_RgbaRowSizingExceedsBufferLimits_Throws() + { + // 4 RGBA HALF channels at this width cannot be represented by the decoder's + // int-sized row staging or block buffers. + byte[] data = BuildMinimalRgbaExr(xMin: 0, yMin: 0, xMax: 536870911, yMax: 0); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + [Fact] + public void Decode_DataWindowWidthExceedsRowBufferLimit_Throws() + { + // A single HALF channel keeps bytesPerBlock below int.MaxValue, but the decoder + // still stages four color planes and must reject widths that overflow width × 4. + byte[] data = BuildMinimalExr(xMin: 0, yMin: 0, xMax: int.MaxValue / 4, yMax: 0); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Decode(DecoderOptions.Default, stream)); + } + + [Fact] + public void Identify_RowOffsetTableExceedsStream_Throws() + { + // Identify parses the header only, so this verifies the offset table bound is + // validated before scanline decoding reads from the table. + byte[] data = BuildMinimalExr(xMin: 0, yMin: 0, xMax: 1, yMax: 1); + + using var stream = new MemoryStream(data); + Assert.Throws( + () => ExrDecoder.Instance.Identify(DecoderOptions.Default, stream)); + } + + // ------------------------------------------------------------------------- + // Helpers: construct minimal valid-enough EXR scanline files. + // + // Required attributes per ParseHeaderAttributes validation: + // channels, compression, dataWindow, displayWindow, + // lineOrder, pixelAspectRatio, screenWindowCenter, screenWindowWidth + // ------------------------------------------------------------------------- + + private static byte[] BuildMinimalExr( + int xMin, int yMin, int xMax, int yMax, + byte[] rowOffsetTableAppend = null) + { + // channels: single "R" HALF channel with xSampling=1, ySampling=1 + // Layout per ReadChannelInfo: name\0 (2) + pixelType (4) + pLinear+reserved (4) + // + xSampling (4) + ySampling (4) = 18 bytes/channel + // + list-null (1) = 19 total + byte[] channelData = + [ + 0x52, 0x00, // "R\0" + 0x01, 0x00, 0x00, 0x00, // pixelType = Half (1) + 0x00, 0x00, 0x00, 0x00, // pLinear + 3 reserved bytes + 0x01, 0x00, 0x00, 0x00, // xSampling = 1 + 0x01, 0x00, 0x00, 0x00, // ySampling = 1 + 0x00, // channel-list null terminator + ]; + + return BuildExrWithChannels(xMin, yMin, xMax, yMax, channelData, rowOffsetTableAppend); + } + + private static byte[] BuildMinimalRgbaExr(int xMin, int yMin, int xMax, int yMax) + { + // 4 HALF channels in alphabetical order (A, B, G, R) per EXR spec. + // 18 bytes per channel × 4 channels + 1 list-null = 73 bytes total. + byte[] channelData = + [ + 0x41, 0x00, // "A\0" + 0x01, 0x00, 0x00, 0x00, // pixelType = Half (1) + 0x00, 0x00, 0x00, 0x00, // pLinear + 3 reserved bytes + 0x01, 0x00, 0x00, 0x00, // xSampling = 1 + 0x01, 0x00, 0x00, 0x00, // ySampling = 1 + 0x42, 0x00, // "B\0" + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, // "G\0" + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x52, 0x00, // "R\0" + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x00, // channel-list null terminator + ]; + + return BuildExrWithChannels(xMin, yMin, xMax, yMax, channelData); + } + + private static byte[] BuildExrWithChannels( + int xMin, int yMin, int xMax, int yMax, + byte[] channelData, + byte[] rowOffsetTableAppend = null) + { + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms, System.Text.Encoding.ASCII, leaveOpen: true); + + // Magic (0x01312F76 LE) + version 2, scanline (flags = 0x00) + bw.Write(new byte[] { 0x76, 0x2F, 0x31, 0x01, 0x02, 0x00, 0x00, 0x00 }); + + void WriteAttr(string name, string type, byte[] payload) + { + foreach (char c in name) bw.Write((byte)c); + bw.Write((byte)0); + foreach (char c in type) bw.Write((byte)c); + bw.Write((byte)0); + bw.Write(payload.Length); + bw.Write(payload); + } + + WriteAttr("channels", "chlist", channelData); + + WriteAttr("compression", "compression", [0x00]); // None + + byte[] dw = new byte[16]; + BinaryPrimitives.WriteInt32LittleEndian(dw, xMin); + BinaryPrimitives.WriteInt32LittleEndian(dw.AsSpan(4), yMin); + BinaryPrimitives.WriteInt32LittleEndian(dw.AsSpan(8), xMax); + BinaryPrimitives.WriteInt32LittleEndian(dw.AsSpan(12), yMax); + WriteAttr("dataWindow", "box2i", dw); + + WriteAttr("displayWindow", "box2i", new byte[16]); // all zeros (0,0,0,0) + + WriteAttr("lineOrder", "lineOrder", [0x00]); // IncreasingY + + byte[] aspect = new byte[4]; + BinaryPrimitives.WriteSingleLittleEndian(aspect, 1.0f); + WriteAttr("pixelAspectRatio", "float", aspect); + + WriteAttr("screenWindowCenter", "v2f", new byte[8]); // (0f, 0f) + + byte[] sww = new byte[4]; + BinaryPrimitives.WriteSingleLittleEndian(sww, 1.0f); + WriteAttr("screenWindowWidth", "float", sww); + + bw.Write((byte)0x00); // end-of-header sentinel + + if (rowOffsetTableAppend is not null) + bw.Write(rowOffsetTableAppend); + + return ms.ToArray(); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs new file mode 100644 index 0000000000..1ee0cc582b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs @@ -0,0 +1,155 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.Formats.Exr.Constants; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +[Trait("Format", "Exr")] +[ValidateDisposedMemoryAllocations] +public class ExrDecoderTests +{ + private static MagickReferenceDecoder ReferenceDecoder => MagickReferenceDecoder.Exr; + + [Theory] + [WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Half(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + ExrMetadata exrMetaData = image.Metadata.GetExrMetadata(); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + Assert.Equal(ExrPixelType.Half, exrMetaData.PixelType); + } + + [Theory] + [WithFile(TestImages.Exr.UncompressedFloatRgb, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Float(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + ExrMetadata exrMetaData = image.Metadata.GetExrMetadata(); + image.DebugSave(provider); + + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + Assert.Equal(ExrPixelType.Float, exrMetaData.PixelType); + } + + [Theory] + [WithFile(TestImages.Exr.UncompressedUintRgb, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Uint(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + ExrMetadata exrMetaData = image.Metadata.GetExrMetadata(); + image.DebugSave(provider); + + // Compare to referene output, since the reference decoder does not support this pixel type. + image.CompareToReferenceOutput(provider); + Assert.Equal(ExrPixelType.UnsignedInt, exrMetaData.PixelType); + } + + [Theory] + [WithFile(TestImages.Exr.UintRgba, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Uncompressed_Rgba_ExrPixelType_Uint(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + ExrMetadata exrMetaData = image.Metadata.GetExrMetadata(); + image.DebugSave(provider); + + // Compare to referene output, since the reference decoder does not support this pixel type. + image.CompareToReferenceOutput(provider); + Assert.Equal(ExrPixelType.UnsignedInt, exrMetaData.PixelType); + } + + [Theory] + [WithFile(TestImages.Exr.Rgb, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Rgb(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Gray, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Gray(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Zip, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_ZipCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Zips, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_ZipsCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Rle, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_RunLengthCompressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Pxr24Half, PixelTypes.Rgba32)] + [WithFile(TestImages.Exr.Pxr24Float, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Pxr24Compressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder); + } + + [Theory] + [WithFile(TestImages.Exr.Pxr24Uint, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + + // Compare to Reference here instead using the reference decoder, since uint pixel type is not supported by the Reference decoder. + image.CompareToReferenceOutput(provider); + } + + [Theory] + [WithFile(TestImages.Exr.B44, PixelTypes.Rgba32)] + public void ExrDecoder_CanDecode_B44Compressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(ExrDecoder.Instance); + image.DebugSave(provider); + + // Note: There is a 0,1190% difference to the reference decoder. + image.CompareToOriginal(provider, ImageComparer.Tolerant(0.011f), ReferenceDecoder); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrEncoderTests.cs new file mode 100644 index 0000000000..2f507ea51e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ExrEncoderTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.Formats.Exr.Constants; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +[Trait("Format", "Exr")] +[ValidateDisposedMemoryAllocations] +public class ExrEncoderTests +{ + protected static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder(ExrFormat.Instance); + + [Theory] + [InlineData(null, ExrPixelType.Half)] + [InlineData(ExrPixelType.Float, ExrPixelType.Float)] + [InlineData(ExrPixelType.Half, ExrPixelType.Half)] + [InlineData(ExrPixelType.UnsignedInt, ExrPixelType.UnsignedInt)] + public void EncoderOptions_SetPixelType_Works(ExrPixelType? pixelType, ExrPixelType? expectedPixelType) + { + // arrange + ExrEncoder exrEncoder = new() { PixelType = pixelType }; + using Image input = new Image(10, 10); + using MemoryStream memStream = new(); + + // act + input.Save(memStream, exrEncoder); + + // assert + memStream.Position = 0; + using Image output = Image.Load(memStream); + ExrMetadata exrMetaData = output.Metadata.GetExrMetadata(); + Assert.Equal(expectedPixelType, exrMetaData.PixelType); + } + + [Theory] + [WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)] + public void ExrEncoder_WithNoCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestExrEncoderCore(provider, "NoCompression", compression: ExrCompression.None); + + [Theory] + [WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)] + public void ExrEncoder_WithZipCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestExrEncoderCore(provider, "ZipCompression", compression: ExrCompression.Zip); + + [Theory] + [WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)] + public void ExrEncoder_WithZipsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestExrEncoderCore(provider, "ZipsCompression", compression: ExrCompression.Zips); + + protected static void TestExrEncoderCore( + TestImageProvider provider, + object testOutputDetails, + ExrCompression compression = ExrCompression.None, + bool useExactComparer = true, + float compareTolerance = 0.001f, + IImageDecoder imageDecoder = null) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + ExrEncoder encoder = new() + { + Compression = compression, + }; + + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder( + provider, + "exr", + testOutputDetails: testOutputDetails, + encoder: encoder, + customComparer: useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), + referenceDecoder: imageDecoder ?? ReferenceDecoder); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrMetadataTests.cs new file mode 100644 index 0000000000..cb527cf3b2 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ExrMetadataTests.cs @@ -0,0 +1,129 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.Formats.Exr.Constants; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +[Trait("Format", "Exr")] +public class ExrMetadataTests +{ + [Fact] + public void CloneIsDeep() + { + ExrMetadata meta = new() + { ImageDataType = ExrImageDataType.Rgb, PixelType = ExrPixelType.Half, Compression = ExrCompression.None }; + ExrMetadata clone = (ExrMetadata)meta.DeepClone(); + + clone.ImageDataType = ExrImageDataType.Gray; + clone.PixelType = ExrPixelType.Float; + clone.Compression = ExrCompression.Zip; + + Assert.False(meta.ImageDataType.Equals(clone.ImageDataType)); + Assert.False(meta.PixelType.Equals(clone.PixelType)); + Assert.False(meta.Compression.Equals(clone.Compression)); + } + + [Theory] + [InlineData(TestImages.Exr.Uncompressed, 199, 297)] + public void Identify_DetectsCorrectWidthAndHeight(string imagePath, int expectedWidth, int expectedHeight) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + Assert.Equal(expectedWidth, imageInfo.Width); + Assert.Equal(expectedHeight, imageInfo.Height); + } + + [Theory] + [InlineData(TestImages.Exr.Uncompressed, ExrPixelType.Half)] + [InlineData(TestImages.Exr.UncompressedFloatRgb, ExrPixelType.Float)] + [InlineData(TestImages.Exr.UncompressedUintRgb, ExrPixelType.UnsignedInt)] + public void Identify_DetectsCorrectPixelType(string imagePath, ExrPixelType expectedPixelType) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + ExrMetadata metadata = imageInfo.Metadata.GetExrMetadata(); + Assert.NotNull(metadata); + Assert.Equal(expectedPixelType, metadata.PixelType); + } + + [Theory] + [InlineData(TestImages.Exr.UncompressedRgba, ExrImageDataType.Rgba)] + [InlineData(TestImages.Exr.Rgb, ExrImageDataType.Rgb)] + [InlineData(TestImages.Exr.Gray, ExrImageDataType.Gray)] + public void Identify_DetectsCorrectImageDataType(string imagePath, ExrImageDataType expectedImageDataType) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + ExrMetadata metadata = imageInfo.Metadata.GetExrMetadata(); + Assert.NotNull(metadata); + Assert.Equal(expectedImageDataType, metadata.ImageDataType); + } + + [Theory] + [InlineData(ExrImageDataType.Rgba, PixelAlphaRepresentation.Associated)] + [InlineData(ExrImageDataType.Rgb, PixelAlphaRepresentation.None)] + [InlineData(ExrImageDataType.Gray, PixelAlphaRepresentation.None)] + public void GetPixelTypeInfo_ReturnsExpectedAlphaRepresentation(ExrImageDataType imageDataType, PixelAlphaRepresentation expected) + { + ExrMetadata metadata = new() { ImageDataType = imageDataType }; + + Assert.Equal(expected, metadata.GetPixelTypeInfo().AlphaRepresentation); + } + + [Theory] + [InlineData(TestImages.Exr.UncompressedRgba, ExrCompression.None)] + [InlineData(TestImages.Exr.B44, ExrCompression.B44)] + [InlineData(TestImages.Exr.Rle, ExrCompression.RunLengthEncoded)] + public void Identify_DetectsCorrectCompression(string imagePath, ExrCompression expectedCompression) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + ExrMetadata metadata = imageInfo.Metadata.GetExrMetadata(); + Assert.NotNull(metadata); + Assert.Equal(expectedCompression, metadata.Compression); + } + + [Theory] + [InlineData(PixelColorType.Binary, 1, ExrImageDataType.Unknown, ExrPixelType.Half)] + [InlineData(PixelColorType.Indexed, 8, ExrImageDataType.Unknown, ExrPixelType.Half)] + [InlineData(PixelColorType.Luminance, 16, ExrImageDataType.Gray, ExrPixelType.Half)] + [InlineData(PixelColorType.RGB, 48, ExrImageDataType.Rgb, ExrPixelType.Float)] + [InlineData(PixelColorType.BGR, 48, ExrImageDataType.Rgb, ExrPixelType.Float)] + [InlineData(PixelColorType.RGB | PixelColorType.Alpha, 64, ExrImageDataType.Rgba, ExrPixelType.Float)] + [InlineData(PixelColorType.BGR | PixelColorType.Alpha, 64, ExrImageDataType.Rgba, ExrPixelType.Float)] + [InlineData(PixelColorType.Luminance | PixelColorType.Alpha, 32, ExrImageDataType.Rgba, ExrPixelType.Float)] + [InlineData(PixelColorType.YCbCr, 48, ExrImageDataType.Unknown, ExrPixelType.Float)] + [InlineData(PixelColorType.CMYK, 64, ExrImageDataType.Unknown, ExrPixelType.Float)] + [InlineData(PixelColorType.YCCK, 64, ExrImageDataType.Unknown, ExrPixelType.Float)] + public void FromFormatConnectingMetadata_ConvertColorTypeAsExpected(PixelColorType pixelColorType, int bitsPerPixel, ExrImageDataType expectedImageDataType, ExrPixelType expectedPixelType) + { + FormatConnectingMetadata formatConnectingMetadata = new() + { + PixelTypeInfo = new PixelTypeInfo(bitsPerPixel) + { + ColorType = pixelColorType, + }, + }; + + ExrMetadata actual = ExrMetadata.FromFormatConnectingMetadata(formatConnectingMetadata); + + Assert.Equal(expectedImageDataType, actual.ImageDataType); + Assert.Equal(expectedPixelType, actual.PixelType); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs new file mode 100644 index 0000000000..1a7632d5e3 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs @@ -0,0 +1,248 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.IO.Compression; +using System.Numerics; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Exr.Constants; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Memory; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +[Trait("Format", "Exr")] +[ValidateDisposedMemoryAllocations] +public class ExrZipDecoderTests +{ + /// + /// Incomplete and oversized blocks are rejected unless image-data recovery is enabled. + /// + /// The inflated payload length. + /// The image-data integrity policy. + [Theory] + [InlineData(0, SegmentIntegrityHandling.Strict)] + [InlineData(8, SegmentIntegrityHandling.Strict)] + [InlineData(1025, SegmentIntegrityHandling.Strict)] + [InlineData(0, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(8, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(1025, SegmentIntegrityHandling.IgnoreAncillary)] + public void Decode_InvalidInflatedBlock_Throws(int length, SegmentIntegrityHandling integrity) + { + byte[] data = BuildExr(ZlibCompress(new byte[length]), ExrPixelType.Float, 2, 0); + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + + Assert.Throws(() => Image.Load(options, data)); + } + + /// + /// Recovering an invalid image-data block must not expose partially decoded or pooled bytes. + /// + /// The stored sample type. + /// The inflated payload length. + [Theory] + [InlineData(ExrPixelType.Half, 0)] + [InlineData(ExrPixelType.Half, 8)] + [InlineData(ExrPixelType.Half, 1025)] + [InlineData(ExrPixelType.Float, 0)] + [InlineData(ExrPixelType.Float, 8)] + [InlineData(ExrPixelType.Float, 1025)] + [InlineData(ExrPixelType.UnsignedInt, 0)] + [InlineData(ExrPixelType.UnsignedInt, 8)] + [InlineData(ExrPixelType.UnsignedInt, 1025)] + public void Decode_InvalidInflatedBlock_IgnoreImageData_ClearsPixels(ExrPixelType pixelType, int length) + { + byte[] data = BuildExr(ZlibCompress(new byte[length]), pixelType, 2, 0); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = new TestMemoryAllocator(0x3F); + DecoderOptions options = new() { Configuration = configuration, SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData }; + + using Image image = Image.Load(options, data); + Assert.Equal(new Size(256, 1), image.Size); + + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(new Vector4(0, 0, 0, 1), image[x, 0].ToVector4()); + } + } + + /// + /// Missing or truncated zlib headers obey the image-data integrity policy. + /// + /// The number of available zlib header bytes. + /// The image-data integrity policy. + [Theory] + [InlineData(0, SegmentIntegrityHandling.Strict)] + [InlineData(1, SegmentIntegrityHandling.Strict)] + [InlineData(0, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(1, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(0, SegmentIntegrityHandling.IgnoreImageData)] + [InlineData(1, SegmentIntegrityHandling.IgnoreImageData)] + public void Decode_IncompleteZlibHeader_RespectsIntegrityHandling(int length, SegmentIntegrityHandling integrity) + { + byte[] header = [0x78, 0x9C]; + byte[] data = BuildExr(header[..length], ExrPixelType.Float, 2, 0); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = new TestMemoryAllocator(0x3F); + DecoderOptions options = new() { Configuration = configuration, SegmentIntegrityHandling = integrity }; + + if (integrity == SegmentIntegrityHandling.IgnoreImageData) + { + using Image image = Image.Load(options, data); + Assert.Equal(new Size(256, 1), image.Size); + + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(new Vector4(0, 0, 0, 1), image[x, 0].ToVector4()); + } + } + else + { + Assert.Throws(() => Image.Load(options, data)); + } + } + + /// + /// Missing color channels must not inherit the allocator's previous contents. + /// + /// The stored sample type. + /// The ZIP compression code. + /// The data window's first row coordinate. + [Theory] + [InlineData(ExrPixelType.Half, 2, 0)] + [InlineData(ExrPixelType.Float, 2, 0)] + [InlineData(ExrPixelType.UnsignedInt, 2, 0)] + [InlineData(ExrPixelType.Half, 3, 0)] + [InlineData(ExrPixelType.Float, 3, 0)] + [InlineData(ExrPixelType.UnsignedInt, 3, 0)] + [InlineData(ExrPixelType.Half, 3, -10)] + [InlineData(ExrPixelType.Float, 3, -10)] + [InlineData(ExrPixelType.UnsignedInt, 3, -10)] + [InlineData(ExrPixelType.Half, 3, 10)] + [InlineData(ExrPixelType.Float, 3, 10)] + [InlineData(ExrPixelType.UnsignedInt, 3, 10)] + public void Decode_SingleRedChannel_InitializesMissingColorChannels(ExrPixelType pixelType, byte compression, int yMin) + { + byte[] predicted = new byte[256 * (pixelType == ExrPixelType.Half ? 2 : 4)]; + + // A zero first byte followed by 128-valued differences reconstructs an all-zero sample plane. + predicted.AsSpan(1).Fill(128); + byte[] data = BuildExr(ZlibCompress(predicted), pixelType, compression, yMin); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = new TestMemoryAllocator(0x3F); + DecoderOptions options = new() { Configuration = configuration }; + + using Image image = Image.Load(options, data); + Assert.Equal(new Size(256, 1), image.Size); + + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(new Vector4(0, 0, 0, 1), image[x, 0].ToVector4()); + } + } + + /// + /// Compresses the predictor bytes for a scanline block. + /// + /// The predictor bytes. + /// The zlib stream. + private static byte[] ZlibCompress(byte[] data) + { + if (data.Length == 0) + { + // An empty write produces no output on some runtimes. Use a complete zlib stream + // containing an empty final DEFLATE block and Adler-32 checksum instead. + return [0x78, 0x9C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01]; + } + + using MemoryStream output = new(); + using (ZLibStream zlib = new(output, CompressionLevel.Optimal, leaveOpen: true)) + { + zlib.Write(data); + } + + return output.ToArray(); + } + + /// + /// Builds a single-row EXR containing only the red channel. + /// + /// The compressed scanline bytes. + /// The stored sample type. + /// The ZIP compression code. + /// The data window's first row coordinate. + /// The encoded image. + private static byte[] BuildExr(byte[] compressed, ExrPixelType pixelType, byte compression, int yMin) + { + const int width = 256; + const int height = 1; + + using MemoryStream output = new(); + using BinaryWriter writer = new(output); + + writer.Write(new byte[] { 0x76, 0x2F, 0x31, 0x01 }); + writer.Write((byte)2); + writer.Write(new byte[] { 0, 0, 0 }); + + using (MemoryStream channelStream = new()) + using (BinaryWriter channelWriter = new(channelStream)) + { + WriteString(channelWriter, "R"); + channelWriter.Write((int)pixelType); + channelWriter.Write((byte)0); + channelWriter.Write(new byte[] { 0, 0, 0 }); + channelWriter.Write(1); + channelWriter.Write(1); + channelWriter.Write((byte)0); + + WriteAttribute(writer, "channels", "chlist", channelStream.ToArray()); + } + + WriteAttribute(writer, "compression", "compression", [compression]); + + using (MemoryStream boxStream = new()) + using (BinaryWriter boxWriter = new(boxStream)) + { + boxWriter.Write(0); + boxWriter.Write(yMin); + boxWriter.Write(width - 1); + boxWriter.Write(yMin + height - 1); + + byte[] box = boxStream.ToArray(); + WriteAttribute(writer, "dataWindow", "box2i", box); + WriteAttribute(writer, "displayWindow", "box2i", box); + } + + WriteAttribute(writer, "lineOrder", "lineOrder", [0]); + + byte[] one = new byte[4]; + BinaryPrimitives.WriteSingleLittleEndian(one, 1F); + WriteAttribute(writer, "pixelAspectRatio", "float", one); + WriteAttribute(writer, "screenWindowCenter", "v2f", new byte[8]); + WriteAttribute(writer, "screenWindowWidth", "float", one); + writer.Write((byte)0); + + long chunkStart = output.Position + sizeof(ulong); + writer.Write((ulong)chunkStart); + writer.Write(yMin); + writer.Write((uint)compressed.Length); + writer.Write(compressed); + + return output.ToArray(); + } + + private static void WriteString(BinaryWriter writer, string value) + { + writer.Write(Encoding.ASCII.GetBytes(value)); + writer.Write((byte)0); + } + + private static void WriteAttribute(BinaryWriter writer, string name, string type, byte[] value) + { + WriteString(writer, name); + WriteString(writer, type); + writer.Write(value.Length); + writer.Write(value); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs new file mode 100644 index 0000000000..cd23e1ccd0 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Exr; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Exr; + +[Trait("Format", "Exr")] +public class ImageExtensionsTest +{ + [Fact] + public void SaveAsExr_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsExr_Path.exr"); + + using (Image image = new(10, 10)) + { + image.SaveAsExr(file); + } + + IImageFormat format = Image.DetectFormat(file); + Assert.True(format is ExrFormat); + } + + [Fact] + public async Task SaveAsExrAsync_Path() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); + string file = Path.Combine(dir, "SaveAsExrAsync_Path.exr"); + + using (Image image = new(10, 10)) + { + await image.SaveAsExrAsync(file); + } + + IImageFormat format = Image.DetectFormat(file); + Assert.True(format is ExrFormat); + } + + [Fact] + public void SaveAsExr_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsExr_Path_Encoder.exr"); + + using (Image image = new(10, 10)) + { + image.SaveAsExr(file, new ExrEncoder()); + } + + IImageFormat format = Image.DetectFormat(file); + Assert.True(format is ExrFormat); + } + + [Fact] + public async Task SaveAsExrAsync_Path_Encoder() + { + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); + string file = Path.Combine(dir, "SaveAsExrAsync_Path_Encoder.exr"); + + using (Image image = new(10, 10)) + { + await image.SaveAsExrAsync(file, new ExrEncoder()); + } + + IImageFormat format = Image.DetectFormat(file); + Assert.True(format is ExrFormat); + } + + [Fact] + public void SaveAsExr_Stream() + { + using MemoryStream memoryStream = new(); + + using (Image image = new(10, 10)) + { + image.SaveAsExr(memoryStream); + } + + memoryStream.Position = 0; + + IImageFormat format = Image.DetectFormat(memoryStream); + Assert.True(format is ExrFormat); + } + + [Fact] + public async Task SaveAsExrAsync_StreamAsync() + { + using MemoryStream memoryStream = new(); + + using (Image image = new(10, 10)) + { + await image.SaveAsExrAsync(memoryStream); + } + + memoryStream.Position = 0; + + IImageFormat format = Image.DetectFormat(memoryStream); + Assert.True(format is ExrFormat); + } + + [Fact] + public void SaveAsExr_Stream_Encoder() + { + using MemoryStream memoryStream = new(); + + using (Image image = new(10, 10)) + { + image.SaveAsExr(memoryStream, new ExrEncoder()); + } + + memoryStream.Position = 0; + + IImageFormat format = Image.DetectFormat(memoryStream); + Assert.True(format is ExrFormat); + } + + [Fact] + public async Task SaveAsExrAsync_Stream_Encoder() + { + using MemoryStream memoryStream = new(); + + using (Image image = new(10, 10)) + { + await image.SaveAsExrAsync(memoryStream, new ExrEncoder()); + } + + memoryStream.Position = 0; + + IImageFormat format = Image.DetectFormat(memoryStream); + Assert.True(format is ExrFormat); + } +} diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 27511f7beb..2b91c4dbfa 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -16,24 +16,23 @@ public class GeneralFormatTests /// A collection made up of one file for each image format. /// public static readonly IEnumerable DefaultFiles = - new[] - { - TestImages.Bmp.Car, + [ + TestImages.Bmp.Car, TestImages.Jpeg.Baseline.Calliphora, TestImages.Png.Splash, TestImages.Gif.Trans - }; + ]; /// /// The collection of image files to test against. /// - protected static readonly List Files = new() - { + protected static readonly List Files = + [ TestFile.Create(TestImages.Jpeg.Baseline.Calliphora), TestFile.Create(TestImages.Bmp.Car), TestFile.Create(TestImages.Png.Splash), - TestFile.Create(TestImages.Gif.Rings), - }; + TestFile.Create(TestImages.Gif.Rings) + ]; [Theory] [WithFileCollection(nameof(DefaultFiles), PixelTypes.Rgba32)] @@ -126,7 +125,7 @@ public void DecodeThenEncodeImageFromStreamShouldSucceed() public static readonly TheoryData QuantizerNames = new() { - nameof(KnownQuantizers.Octree), + nameof(KnownQuantizers.Hexadecatree), nameof(KnownQuantizers.WebSafe), nameof(KnownQuantizers.Werner), nameof(KnownQuantizers.Wu) @@ -151,7 +150,7 @@ public void QuantizeImageShouldPreserveMaximumColorPrecision(TestImagePr private static IQuantizer GetQuantizer(string name) { PropertyInfo property = typeof(KnownQuantizers).GetTypeInfo().GetProperty(name); - return (IQuantizer)property.GetMethod.Invoke(null, Array.Empty()); + return (IQuantizer)property.GetMethod.Invoke(null, []); } [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index f4e6487a57..8a8b4866a2 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -20,9 +20,9 @@ public class GifDecoderTests private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; public static readonly string[] MultiFrameTestFiles = - { + [ TestImages.Gif.Giphy, TestImages.Gif.Kumin - }; + ]; [Theory] [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] @@ -34,6 +34,41 @@ public void Decode_VerifyAllFrames(TestImageProvider provider) image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } + [Theory] + [WithFile(TestImages.Gif.AnimatedLoop, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.AnimatedLoopInterlaced, PixelTypes.Rgba32)] + public void Decode_Animated(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + + [Theory] + [WithFile(TestImages.Gif.AnimatedTransparentNoRestore, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.AnimatedTransparentRestorePrevious, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.AnimatedTransparentLoop, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.AnimatedTransparentFirstFrameRestorePrev, PixelTypes.Rgba32)] + public void Decode_Animated_WithTransparency(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + + [Theory] + [WithFile(TestImages.Gif.StaticNontransparent, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.StaticTransparent, PixelTypes.Rgba32)] + public void Decode_Static_No_Animation(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); + } + [Theory] [WithFile(TestImages.Gif.Issues.Issue2450_A, PixelTypes.Rgba32)] [WithFile(TestImages.Gif.Issues.Issue2450_B, PixelTypes.Rgba32)] @@ -55,7 +90,7 @@ public void GifDecoder_Decode_Resize(TestImageProvider provider) { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 }, + TargetSize = new Size { Width = 150, Height = 150 }, MaxFrames = 1 }; @@ -222,7 +257,7 @@ public void Issue2758_BadDescriptorDimensions(TestImageProvider public void Issue405_BadApplicationExtensionBlockLength(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 }); + using Image image = provider.GetImage(GifDecoder.Instance, new DecoderOptions { MaxFrames = 1 }); image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); @@ -234,7 +269,7 @@ public void Issue405_BadApplicationExtensionBlockLength(TestImageProvide public void Issue1668_InvalidColorIndex(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 }); + using Image image = provider.GetImage(GifDecoder.Instance, new DecoderOptions { MaxFrames = 1 }); image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); @@ -283,7 +318,7 @@ TestImageProvider provider public void Issue1962(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 }); + using Image image = provider.GetImage(GifDecoder.Instance, new DecoderOptions { MaxFrames = 1 }); image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); @@ -295,7 +330,7 @@ public void Issue1962(TestImageProvider provider) public void Issue2012EmptyXmp(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(GifDecoder.Instance, new() { MaxFrames = 1 }); + using Image image = provider.GetImage(GifDecoder.Instance, new DecoderOptions { MaxFrames = 1 }); image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); @@ -334,4 +369,93 @@ public void IssueTooLargeLzwBits(TestImageProvider provider) image.DebugSaveMultiFrame(provider); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } + + // https://github.com/SixLabors/ImageSharp/issues/2859 + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2859_A, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.Issues.Issue2859_B, PixelTypes.Rgba32)] + public void Issue2859_LZWPixelStackOverflow(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + + // https://github.com/SixLabors/ImageSharp/issues/2953 + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2953, PixelTypes.Rgba32)] + public void Issue2953(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // We should throw a InvalidImageContentException when trying to identify or load an invalid GIF file. + TestFile testFile = TestFile.Create(provider.SourceFileOrDescription); + + Assert.Throws(() => Image.Identify(testFile.FullPath)); + Assert.Throws(() => Image.Load(testFile.FullPath)); + + DecoderOptions options = new() { SkipMetadata = true }; + Assert.Throws(() => Image.Identify(options, testFile.FullPath)); + Assert.Throws(() => Image.Load(options, testFile.FullPath)); + } + + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2980, PixelTypes.Rgba32)] + public void Issue2980(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + + [Fact] + public void Identify_MalformedApplicationExtension_IgnoresNonCriticalErrorsByDefault() + { + ImageInfo info = Image.Identify(CreateGifWithMalformedApplicationExtension()); + Assert.Equal(1, info.Width); + Assert.Equal(1, info.Height); + } + + [Fact] + public void Decode_MalformedApplicationExtension_IgnoresNonCriticalErrorsByDefault() + { + using Image image = Image.Load(CreateGifWithMalformedApplicationExtension()); + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + } + + [Fact] + public void Identify_MalformedApplicationExtension_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => Image.Identify(options, CreateGifWithMalformedApplicationExtension())); + } + + [Fact] + public void Decode_MalformedApplicationExtension_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => + { + using Image image = Image.Load(options, CreateGifWithMalformedApplicationExtension()); + }); + } + + private static byte[] CreateGifWithMalformedApplicationExtension() + { + // The application extension declares a 5-byte application block (0x05), but GIF application + // extensions require the fixed identifier/authentication block to be 11 bytes long. + return + [ + (byte)'G', (byte)'I', (byte)'F', (byte)'8', (byte)'9', (byte)'a', + 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x21, 0xFF, 0x05, (byte)'B', (byte)'a', (byte)'d', (byte)'A', (byte)'p', 0x00, + 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x02, 0x02, 0x44, 0x01, 0x00, + 0x3B + ]; + } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index a7e16f7737..a465b073b5 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -1,11 +1,13 @@ // 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; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -55,7 +57,7 @@ public void EncodeGeneratedPatterns(TestImageProvider provider, { // Use the palette quantizer without dithering to ensure results // are consistent - Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) + Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null, TransparencyThreshold = 0 }) }; // Always save as we need to compare the encoded output. @@ -113,17 +115,17 @@ public void EncodeGlobalPaletteReturnsSmallerFile(TestImageProvider image = provider.GetImage(); GifEncoder encoder = new() { - ColorTableMode = GifColorTableMode.Global, - Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) + ColorTableMode = FrameColorTableMode.Global, + Quantizer = new HexadecatreeQuantizer(new QuantizerOptions { Dither = null }) }; // Always save as we need to compare the encoded output. provider.Utility.SaveTestOutputFile(image, "gif", encoder, "global"); - encoder = new() + encoder = new GifEncoder { - ColorTableMode = GifColorTableMode.Local, - Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }), + ColorTableMode = FrameColorTableMode.Local, + Quantizer = new HexadecatreeQuantizer(new QuantizerOptions { Dither = null }), }; provider.Utility.SaveTestOutputFile(image, "gif", encoder, "local"); @@ -148,7 +150,7 @@ public void Encode_GlobalPalette_DefaultPixelSamplingStrategy(TestImageP GifEncoder encoder = new() { - ColorTableMode = GifColorTableMode.Global, + ColorTableMode = FrameColorTableMode.Global, PixelSamplingStrategy = new DefaultPixelSamplingStrategy(maxPixels, scanRatio) }; @@ -175,10 +177,10 @@ public void NonMutatingEncodePreservesPaletteCount() 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; } @@ -190,7 +192,7 @@ public void NonMutatingEncodePreservesPaletteCount() GifEncoder encoder = new() { ColorTableMode = colorMode, - Quantizer = new OctreeQuantizer(new QuantizerOptions { MaxColors = maxColors }) + Quantizer = new HexadecatreeQuantizer(new QuantizerOptions { MaxColors = maxColors }) }; image.Save(outStream, encoder); @@ -204,7 +206,7 @@ public void NonMutatingEncodePreservesPaletteCount() // 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 +222,7 @@ public void NonMutatingEncodePreservesPaletteCount() 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); } @@ -241,33 +243,11 @@ public void OptionalExtensionsShouldBeHandledProperly(TestImageProvider< 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] @@ -305,14 +285,14 @@ public void Encode_AnimatedFormatTransform_FromPng(TestImageProvider(TestImageProvider(TestImageProvider TestImages.Gif.Animated; - [Theory(Skip = "Enable for visual animated testing")] + [Theory]//(Skip = "Enable for visual animated testing")] [WithFileCollection(nameof(Animated), PixelTypes.Rgba32)] public void Encode_Animated_VisualTest(TestImageProvider provider) where TPixel : unmanaged, IPixel @@ -382,4 +362,96 @@ public void Encode_Animated_VisualTest(TestImageProvider provide provider.Utility.SaveTestOutputFile(image, "png", new PngEncoder(), "animated"); provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder(), "animated"); } + + [Fact] + public void Encode_WithTransparentColorBehaviorClear_Works() + { + // arrange + using Image image = new(50, 50); + GifEncoder encoder = new() + { + TransparentColorMode = TransparentColorMode.Clear, + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + 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 = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } + + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2866, PixelTypes.Rgba32)] + public void GifEncoder_CanDecode_AndEncode_Issue2866(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + // Save the image for visual inspection. + provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder(), "animated"); + + // Now compare the debug output with the reference output. + // We do this because the gif encoding is lossy and encoding will lead to differences in the 10s of percent. + // From the unencoded image, we can see that the image is visually the same. + static bool Predicate(int i, int _) => i % 8 == 0; // Image has many frames, only compare a selection of them. + image.CompareDebugOutputToReferenceOutputMultiFrame(provider, ImageComparer.Exact, extension: "gif", predicate: Predicate); + } + + [Theory] + [WithFile(TestImages.Gif.Issues.Issue3142, PixelTypes.Rgba32)] + public void GifEncoder_CanDecode_AndEncode_Issue3142(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + // Save the image for visual inspection. + provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder() { Quantizer = KnownQuantizers.Wu }, "animated"); + + // Now compare the debug output with the reference output. + // We do this because the gif encoding is lossy and encoding will lead to differences in the 10s of percent. + // From the unencoded image, we can see that the image is visually the same. + static bool Predicate(int i, int _) => i % 8 == 0; // Image has many frames, only compare a selection of them. + image.CompareDebugOutputToReferenceOutputMultiFrame(provider, ImageComparer.Exact, extension: "gif", predicate: Predicate); + } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs index 774638311d..f12e6e7e50 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,18 +15,18 @@ public void CloneIsDeep() GifFrameMetadata meta = new() { FrameDelay = 1, - DisposalMethod = GifDisposalMethod.RestoreToBackground, + DisposalMode = FrameDisposalMode.RestoreToBackground, LocalColorTable = new[] { Color.Black, Color.White } }; GifFrameMetadata clone = (GifFrameMetadata)meta.DeepClone(); clone.FrameDelay = 2; - clone.DisposalMethod = GifDisposalMethod.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 b4fd87755c..dd3879703b 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -33,15 +33,15 @@ public void CloneIsDeep() GifMetadata meta = new() { RepeatCount = 1, - ColorTableMode = GifColorTableMode.Global, + ColorTableMode = FrameColorTableMode.Global, GlobalColorTable = new[] { Color.Black, Color.White }, - Comments = new List { "Foo" } + Comments = ["Foo"] }; 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)); @@ -126,7 +126,7 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut 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 void Decode_VerifyRatio(string imagePath, int xResolution, int yResolutio 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); @@ -183,14 +183,14 @@ public void Decode_VerifyRepeatCount(string imagePath, uint repeatCount) } [Theory] - [InlineData(TestImages.Gif.Cheers, 93, GifColorTableMode.Global, 256, 4, GifDisposalMethod.NotDispose)] + [InlineData(TestImages.Gif.Cheers, 93, FrameColorTableMode.Global, 256, 4, FrameDisposalMode.DoNotDispose)] public void Identify_Frames( string imagePath, int framesCount, - GifColorTableMode colorTableMode, + FrameColorTableMode colorTableMode, int globalColorTableLength, int frameDelay, - GifDisposalMethod disposalMethod) + FrameDisposalMode disposalMethod) { TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); @@ -206,13 +206,13 @@ public void Identify_Frames( Assert.Equal(colorTableMode, gifFrameMetadata.ColorTableMode); - if (colorTableMode == GifColorTableMode.Global) + if (colorTableMode == FrameColorTableMode.Global) { Assert.Equal(globalColorTableLength, gifMetadata.GlobalColorTable.Value.Length); } Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); - Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); + Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMode); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs index c602bc91bb..05dc5bc52a 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/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs new file mode 100644 index 0000000000..80d721ba9f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +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; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; + +[Trait("Format", "Cur")] +[ValidateDisposedMemoryAllocations] +public class CurDecoderTests +{ + [Fact] + public void CurFormat_HasCorrectName() + => Assert.Equal("CUR", CurFormat.Instance.Name); + + [Fact] + public void CurDetector_RejectsIco() + { + TestFile file = TestFile.Create(TestImages.Ico.Flutter); + CurImageFormatDetector detector = new(); + + Assert.False(detector.TryDetectFormat(file.Bytes, out _)); + } + + [Theory] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CurDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth.Value); + Assert.Equal(image.Height, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); + } + + [Theory] + [WithFile(CurFake, PixelTypes.Rgba32)] + [WithFile(CurReal, PixelTypes.Rgba32)] + public void CurDecoder_Decode2(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth.Value); + Assert.Equal(image.Height, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); + } + + [Fact] + public void CurFrameMetadata_DeepClonePreservesColorTable() + { + Color[] colors = [Color.Red, Color.Green]; + CurFrameMetadata metadata = new() { ColorTable = colors }; + + CurFrameMetadata clone = metadata.DeepClone(); + colors[0] = Color.Blue; + + Assert.Equal(Color.Red, clone.ColorTable.Value.Span[0]); + Assert.Equal(Color.Green, clone.ColorTable.Value.Span[1]); + } + + [Fact] + public void CurFrameMetadata_ScalesZeroEncodingDimensionsFrom256() + { + using Image source = new(256, 256); + using Image destination = new(128, 128); + CurFrameMetadata metadata = new() { EncodingWidth = 0, EncodingHeight = 0 }; + + metadata.AfterFrameApply(source.Frames.RootFrame, destination.Frames.RootFrame, Matrix4x4.Identity); + + Assert.Equal((byte)128, metadata.EncodingWidth); + Assert.Equal((byte)128, metadata.EncodingHeight); + } +} 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 0000000000..305adb9895 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -0,0 +1,163 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +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; + +[Trait("Format", "Cur")] +public class CurEncoderTests +{ + private static CurEncoder Encoder => new(); + + [Theory] + [WithFile(CurReal, PixelTypes.Rgba32)] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(CurDecoder.Instance); + using MemoryStream memStream = new(); + image.DebugSaveMultiFrame(provider); + + image.Save(memStream, Encoder); + memStream.Seek(0, SeekOrigin.Begin); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + + 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); + + // Color palettes are not preserved when transcoding. + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.TolerantPercentage(.05F), 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); + } + } + + [Fact] + public void Encode_WithTransparentColorBehaviorClear_Works() + { + // arrange + using Image image = new(50, 50); + CurEncoder encoder = new() + { + TransparentColorMode = TransparentColorMode.Clear, + + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + actual.ProcessPixelRows(accessor => + { + Rgba32 transparent = Color.Transparent.ToPixel(); + for (int y = 0; y < accessor.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + Span rowSpanOpp = accessor.GetRowSpan(accessor.Height - y - 1); + + if (y > 25) + { + expectedColor = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } + + [Fact] + public void Encode_UsesBitmapDepthInsteadOfHotspotForQuantizerSelection() + { + using Image image = new(32, 1); + for (int x = 0; x < image.Width; x++) + { + image[x, 0] = new Rgba32((byte)(x * 8), (byte)(255 - (x * 8)), (byte)(x * 4)); + } + + CurFrameMetadata metadata = image.Frames.RootFrame.Metadata.GetCurMetadata(); + metadata.Compression = IconFrameCompression.Bmp; + metadata.BmpBitsPerPixel = BmpBitsPerPixel.Bit8; + metadata.HotspotY = 32; + + CurEncoder encoder = new() + { + Quantizer = new WuQuantizer(new QuantizerOptions { MaxColors = 2 }) + }; + + using MemoryStream stream = new(); + image.Save(stream, encoder); + stream.Position = 0; + + using Image decoded = Image.Load(stream); + HashSet colors = []; + for (int x = 0; x < decoded.Width; x++) + { + colors.Add(decoded[x, 0]); + } + + Assert.InRange(colors.Count, 1, 2); + } +} 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 0000000000..bdbf67461f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -0,0 +1,414 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.Numerics; +using SixLabors.ImageSharp.Formats; +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; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; + +[Trait("Format", "Icon")] +[ValidateDisposedMemoryAllocations] +public class IcoDecoderTests +{ + [Fact] + public void IcoDetector_RejectsCur() + { + TestFile file = TestFile.Create(TestImages.Cur.WindowsMouse); + IcoImageFormatDetector detector = new(); + + Assert.False(detector.TryDetectFormat(file.Bytes, out _)); + } + + [Theory] + [WithFile(Flutter, PixelTypes.Rgba32)] + public void IcoDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider); + + Assert.Equal(10, image.Frames.Count); + } + + [Theory] + [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)] + public void Bpp1Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit1, meta.BmpBitsPerPixel); + } + + [Theory] + [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)] + public void Bpp24Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit24, meta.BmpBitsPerPixel); + } + + [Theory] + [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)] + public void Bpp32Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); + } + + [Theory] + [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)] + public void Bpp4Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit4, meta.BmpBitsPerPixel); + } + + [Theory] + [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)] This is actually 24 bit. + [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.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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit8, meta.BmpBitsPerPixel); + } + + [Theory] + [WithFile(InvalidBpp, PixelTypes.Rgba32)] + public void InvalidThrows_InvalidImageContentException(TestImageProvider provider) + => Assert.Throws(() => + { + using Image image = provider.GetImage(IcoDecoder.Instance); + }); + + [Theory] + [WithFile(InvalidAll, PixelTypes.Rgba32)] + [WithFile(InvalidCompression, PixelTypes.Rgba32)] + [WithFile(InvalidRLE4, PixelTypes.Rgba32)] + [WithFile(InvalidRLE8, PixelTypes.Rgba32)] + public void InvalidThows_NotSupportedException(TestImageProvider provider) + => Assert.Throws(() => + { + using Image image = provider.GetImage(IcoDecoder.Instance); + }); + + [Theory] + [WithFile(InvalidPng, PixelTypes.Rgba32)] + public void InvalidPngTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Png, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Bit32, meta.BmpBitsPerPixel); + } + + [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); + + Assert.True(image.Frames.Count > 1); + + 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 MultiSizeTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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.Bit32, 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 DecoderOptions { MaxFrames = 1 }); + Assert.Single(image.Frames); + } + + [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 DecoderOptions { MaxFrames = 1 }, stream); + + Assert.Single(imageInfo.FrameMetadataCollection); + } + + [Fact] + public void TruncatedEntry_FollowsImageDataIntegrityHandling() + { + byte[] data = TestFile.Create(Flutter).Bytes.ToArray(); + ushort entryCount = BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(4)); + Assert.True(entryCount > 1); + + // Limit the first resource below the PNG signature length. A bounded child decoder must not read into following data. + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(IconDir.Size + 8), 4); + + using MemoryStream defaultStream = new(data, false); + Assert.Throws(() => IcoDecoder.Instance.Decode(DecoderOptions.Default, defaultStream)); + + DecoderOptions ignore = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData }; + + using MemoryStream decodeStream = new(data, false); + using Image image = IcoDecoder.Instance.Decode(ignore, decodeStream); + Assert.Equal(entryCount - 1, image.Frames.Count); + + using MemoryStream identifyStream = new(data, false); + ImageInfo info = IcoDecoder.Instance.Identify(ignore, identifyStream); + Assert.Equal(entryCount - 1, info.FrameMetadataCollection.Count); + } + + /// + /// Verifies that a non-empty resource cannot begin at the exclusive end of its containing icon stream. + /// + [Fact] + public void EntryAtEndOfStream_ThrowsInvalidResourceRange() + { + byte[] data = TestFile.Create(Bpp32Size1x1).Bytes.ToArray(); + Assert.Equal(1, BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(4))); + + // ImageOffset is the final DWORD in the sole directory entry. The stream length is an exclusive boundary, + // so accepting this value would create an empty child stream despite the entry declaring non-empty data. + BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(IconDir.Size + 12), (uint)data.Length); + + using MemoryStream decodeStream = new(data, false); + InvalidImageContentException decodeException = Assert.Throws( + () => IcoDecoder.Instance.Decode(DecoderOptions.Default, decodeStream)); + + Assert.Equal("The icon directory contains an invalid image resource range.", decodeException.Message); + + using MemoryStream identifyStream = new(data, false); + InvalidImageContentException identifyException = Assert.Throws( + () => IcoDecoder.Instance.Identify(DecoderOptions.Default, identifyStream)); + + Assert.Equal("The icon directory contains an invalid image resource range.", identifyException.Message); + } + + [Fact] + public void IcoFrameMetadata_ScalesZeroEncodingDimensionsFrom256() + { + using Image source = new(256, 256); + using Image destination = new(128, 128); + IcoFrameMetadata metadata = new() { EncodingWidth = 0, EncodingHeight = 0 }; + + metadata.AfterFrameApply(source.Frames.RootFrame, destination.Frames.RootFrame, Matrix4x4.Identity); + + Assert.Equal((byte)128, metadata.EncodingWidth); + Assert.Equal((byte)128, metadata.EncodingHeight); + } + + [Theory] + [WithFile(MultiSizeMultiBitsA, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsB, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsC, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsD, PixelTypes.Rgba32)] + public void MultiSizeMultiBitsTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + Assert.True(image.Frames.Count > 1); + + image.DebugSaveMultiFrame(provider); + } + + [Theory] + [WithFile(IcoFake, PixelTypes.Rgba32)] + public void IcoFakeTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + 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; + + Assert.Equal(expectedWidth, meta.EncodingWidth.Value); + Assert.Equal(expectedHeight, meta.EncodingHeight.Value); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + 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 new file mode 100644 index 0000000000..08bb537ad7 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -0,0 +1,175 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +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; + +[Trait("Format", "Icon")] +public class IcoEncoderTests +{ + private static IcoEncoder Encoder => new(); + + [Theory] + [WithFile(Flutter, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(IcoDecoder.Instance); + using MemoryStream memStream = new(); + image.DebugSaveMultiFrame(provider); + + 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); + } + + [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); + } + } + + [Fact] + public void Encode_WithTransparentColorBehaviorClear_Works() + { + // arrange + using Image image = new(50, 50); + IcoEncoder encoder = new() + { + TransparentColorMode = TransparentColorMode.Clear, + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + 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 = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } + + [Fact] + public void PngEntry_PreservesExifProfile() + { + using Image image = new(16, 16); + image.Metadata.ExifProfile = new ExifProfile(); + image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); + image.Frames.RootFrame.Metadata.GetIcoMetadata().Compression = IconFrameCompression.Png; + + using MemoryStream stream = new(); + image.Save(stream, Encoder); + + stream.Position = 0; + using Image decoded = Image.Load(stream); + + Assert.NotNull(decoded.Metadata.ExifProfile); + Assert.Equal(image.Metadata.ExifProfile.Values, decoded.Metadata.ExifProfile.Values); + } + + /// + /// Verifies that the final partial AND-mask byte contains every pixel when the bitmap width is not byte-aligned. + /// + /// The bitmap width to encode. + [Theory] + [InlineData(1)] + [InlineData(7)] + [InlineData(9)] + [InlineData(15)] + public void BmpEntry_WritesPartialAlphaMaskByte(int width) + { + using Image image = new(width, 1, Color.Red.ToPixel()); + image[width - 1, 0] = Color.Transparent.ToPixel(); + + IcoFrameMetadata metadata = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + metadata.Compression = IconFrameCompression.Bmp; + metadata.BmpBitsPerPixel = BmpBitsPerPixel.Bit32; + + using MemoryStream stream = new(); + image.Save(stream, Encoder); + + // These widths produce one DWORD-aligned mask row at the end of the bitmap resource. + ReadOnlySpan mask = stream.GetBuffer().AsSpan(checked((int)stream.Length) - sizeof(uint), sizeof(uint)); + int pixelIndex = width - 1; + int byteIndex = pixelIndex / 8; + int bitIndex = pixelIndex % 8; + + Assert.Equal((byte)(0b10000000 >> bitIndex), mask[byteIndex]); + } +} diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index 324bd4783a..1cd03e0d5d 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -3,6 +3,7 @@ using Moq; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Ani; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; @@ -30,6 +31,7 @@ public ImageFormatManagerTests() [Fact] public void IfAutoLoadWellKnownFormatsIsTrueAllFormatsAreLoaded() { + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); @@ -39,6 +41,7 @@ public void IfAutoLoadWellKnownFormatsIsTrueAllFormatsAreLoaded() Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); @@ -123,7 +126,7 @@ public void DetectFormatAllocatesCleanBuffer() IImageFormat format = Image.DetectFormat(jpegImage); Assert.IsType(format); - byte[] invalidImage = { 1, 2, 3 }; + byte[] invalidImage = [1, 2, 3]; Assert.Throws(() => Image.DetectFormat(invalidImage)); } } diff --git a/tests/ImageSharp.Tests/Formats/InvalidImageDimensionsTests.cs b/tests/ImageSharp.Tests/Formats/InvalidImageDimensionsTests.cs new file mode 100644 index 0000000000..bcf7df5984 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/InvalidImageDimensionsTests.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Tests.Formats; + +public class InvalidImageDimensionsTests +{ + [Theory] + [InlineData("Qk1GAAAAAAAAADYAAAAoAAAAAgACAAAAAAABABgAAAAAABAAAAATCwAAEwsAAAAAAAAAAAAAAAD/AP8AAAAAAP8A/wAAAA==")] + [InlineData("R0lGODdhAgIAAIEAAAD/AP8AAAAA/wAAACwAAAQAAgACAAAIBwADABAQICAAOw==")] + [InlineData("AAACAAAAAAAAAAAAAgDCsRgAAgAAAP8A/wD/AAAA//8=")] + public void Load_WithNonPositiveDimensions_ThrowsInvalidImageContentException(string encodedData) + { + byte[] data = Convert.FromBase64String(encodedData); + + Assert.Throws(() => Image.Load(data)); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs index 740b410feb..aee064ccc6 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs @@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg; public class AdobeMarkerTests { // Taken from actual test image - private readonly byte[] bytes = { 0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2 }; + private readonly byte[] bytes = [0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2]; // Altered components - private readonly byte[] bytes2 = { 0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x1, 0x1, 0x1 }; + private readonly byte[] bytes2 = [0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x1, 0x1, 0x1]; [Fact] public void MarkerLengthIsCorrect() @@ -36,7 +36,7 @@ public void MarkerReturnsCorrectParsedValue() [Fact] public void MarkerIgnoresIncorrectValue() { - bool isAdobe = AdobeMarker.TryParse(new byte[] { 0, 0, 0, 0 }, out AdobeMarker marker); + bool isAdobe = AdobeMarker.TryParse([0, 0, 0, 0], out AdobeMarker marker); Assert.False(isAdobe); Assert.Equal(default, marker); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index cde9e776b2..610693c7f4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -3,6 +3,7 @@ // Uncomment this to turn unit tests into benchmarks: // #define BENCHMARKING +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -24,11 +25,22 @@ public Block8x8FTests(ITestOutputHelper output) { } - private bool SkipOnNonAvx2Runner() + private bool SkipOnNonVector256Runner() { - if (!SimdUtils.HasVector8) + if (!Vector256.IsHardwareAccelerated) { - this.Output.WriteLine("AVX2 not supported, skipping!"); + this.Output.WriteLine("Vector256 not supported, skipping!"); + return true; + } + + return false; + } + + private bool SkipOnNonVector128Runner() + { + if (!Vector128.IsHardwareAccelerated) + { + this.Output.WriteLine("Vector128 not supported, skipping!"); return true; } @@ -43,7 +55,7 @@ public void Indexer() Times, () => { - var block = default(Block8x8F); + Block8x8F block = default; for (int i = 0; i < Block8x8F.Size; i++) { @@ -56,7 +68,7 @@ public void Indexer() sum += block[i]; } }); - Assert.Equal(sum, 64f * 63f * 0.5f); + Assert.Equal(64f * 63f * 0.5f, sum); } [Fact] @@ -81,7 +93,7 @@ public void Indexer_ReferenceBenchmarkWithArray() sum += block[i]; } }); - Assert.Equal(sum, 64f * 63f * 0.5f); + Assert.Equal(64f * 63f * 0.5f, sum); } [Fact] @@ -109,7 +121,7 @@ public void Load_Store_FloatArray() } [Fact] - public void TransposeInplace() + public void TransposeInPlace() { static void RunTest() { @@ -118,7 +130,7 @@ static void RunTest() Block8x8F block8x8 = Block8x8F.Load(Create8x8FloatData()); - block8x8.TransposeInplace(); + block8x8.TransposeInPlace(); float[] actual = new float[64]; block8x8.ScaledCopyTo(actual); @@ -169,30 +181,6 @@ public void NormalizeColors() } } - [Theory] - [InlineData(1)] - [InlineData(2)] - public void NormalizeColorsAndRoundAvx2(int seed) - { - if (this.SkipOnNonAvx2Runner()) - { - return; - } - - Block8x8F source = CreateRandomFloatBlock(-200, 200, seed); - - Block8x8F expected = source; - expected.NormalizeColorsInPlace(255); - expected.RoundInPlace(); - - Block8x8F actual = source; - actual.NormalizeColorsAndRoundInPlaceVector8(255); - - this.Output.WriteLine(expected.ToString()); - this.Output.WriteLine(actual.ToString()); - this.CompareBlocks(expected, actual, 0); - } - [Theory] [InlineData(1, 2)] [InlineData(2, 1)] @@ -206,7 +194,7 @@ static void RunTest(string srcSeedSerialized, string qtSeedSerialized) Block8x8F source = CreateRandomFloatBlock(-2000, 2000, srcSeed); // Quantization code is used only in jpeg where it's guaranteed that - // qunatization valus are greater than 1 + // quantization values are greater than 1 // Quantize method supports negative numbers by very small numbers can cause troubles Block8x8F quant = CreateRandomFloatBlock(1, 2000, qtSeed); @@ -231,7 +219,7 @@ static void RunTest(string srcSeedSerialized, string qtSeedSerialized) RunTest, srcSeed, qtSeed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Fact] @@ -240,7 +228,7 @@ public void RoundInto() float[] data = Create8x8RandomFloatData(-1000, 1000); Block8x8F source = Block8x8F.Load(data); - var dest = default(Block8x8); + Block8x8 dest = default; source.RoundInto(ref dest); @@ -254,29 +242,6 @@ public void RoundInto() } } - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - public void RoundInPlaceSlow(int seed) - { - Block8x8F s = CreateRandomFloatBlock(-500, 500, seed); - - Block8x8F d = s; - d.RoundInPlace(); - - this.Output.WriteLine(s.ToString()); - this.Output.WriteLine(d.ToString()); - - for (int i = 0; i < 64; i++) - { - float expected = (float)Math.Round(s[i]); - float actual = d[i]; - - Assert.Equal(expected, actual); - } - } - [Fact] public void MultiplyInPlace_ByOtherBlock() { @@ -345,14 +310,14 @@ static void RunTest() [Fact] public void LoadFromUInt16Scalar() { - if (this.SkipOnNonAvx2Runner()) + if (this.SkipOnNonVector256Runner()) { return; } short[] data = Create8x8ShortData(); - var source = Block8x8.Load(data); + Block8x8 source = Block8x8.Load(data); Block8x8F dest = default; dest.LoadFromInt16Scalar(ref source); @@ -363,20 +328,41 @@ public void LoadFromUInt16Scalar() } } + [Fact] + public void LoadFromUInt16ExtendedVector128() + { + if (this.SkipOnNonVector128Runner()) + { + return; + } + + short[] data = Create8x8ShortData(); + + Block8x8 source = Block8x8.Load(data); + + Block8x8F dest = default; + dest.LoadFromInt16ExtendedVector128(ref source); + + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal(data[i], dest[i]); + } + } + [Fact] public void LoadFromUInt16ExtendedAvx2() { - if (this.SkipOnNonAvx2Runner()) + if (this.SkipOnNonVector256Runner()) { return; } short[] data = Create8x8ShortData(); - var source = Block8x8.Load(data); + Block8x8 source = Block8x8.Load(data); Block8x8F dest = default; - dest.LoadFromInt16ExtendedAvx2(ref source); + dest.LoadFromInt16ExtendedVector256(ref source); for (int i = 0; i < Block8x8F.Size; i++) { @@ -405,7 +391,7 @@ static void RunTest() // 3. DisableAvx2 - call fallback code of float implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs index b5d364dd38..fb1e062f20 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -21,7 +21,7 @@ public void Construct_And_Indexer_Get() { short[] data = Create8x8ShortData(); - var block = Block8x8.Load(data); + Block8x8 block = Block8x8.Load(data); for (int i = 0; i < Block8x8.Size; i++) { @@ -47,7 +47,7 @@ public void AsFloatBlock() { short[] data = Create8x8ShortData(); - var source = Block8x8.Load(data); + Block8x8 source = Block8x8.Load(data); Block8x8F dest = source.AsFloatBlock(); @@ -61,7 +61,7 @@ public void AsFloatBlock() public void ToArray() { short[] data = Create8x8ShortData(); - var block = Block8x8.Load(data); + Block8x8 block = Block8x8.Load(data); short[] result = block.ToArray(); @@ -72,8 +72,8 @@ public void ToArray() public void Equality_WhenFalse() { short[] data = Create8x8ShortData(); - var block1 = Block8x8.Load(data); - var block2 = Block8x8.Load(data); + Block8x8 block1 = Block8x8.Load(data); + Block8x8 block2 = Block8x8.Load(data); block1[0] = 42; block2[0] = 666; @@ -96,8 +96,8 @@ public void IndexerXY() public void TotalDifference() { short[] data = Create8x8ShortData(); - var block1 = Block8x8.Load(data); - var block2 = Block8x8.Load(data); + Block8x8 block1 = Block8x8.Load(data); + Block8x8 block2 = Block8x8.Load(data); block2[10] += 7; block2[63] += 8; @@ -157,7 +157,7 @@ public void GetLastNonZeroIndex_RandomFilledSingle(int seed) static void RunTest(string seedSerialized) { int seed = FeatureTestRunner.Deserialize(seedSerialized); - var rng = new Random(seed); + Random rng = new(seed); for (int i = 0; i < 1000; i++) { @@ -188,7 +188,7 @@ public void GetLastNonZeroIndex_RandomFilledPartially(int seed) static void RunTest(string seedSerialized) { int seed = FeatureTestRunner.Deserialize(seedSerialized); - var rng = new Random(seed); + Random rng = new(seed); for (int i = 0; i < 1000; i++) { @@ -223,7 +223,7 @@ public void GetLastNonZeroIndex_RandomFilledFragmented(int seed) static void RunTest(string seedSerialized) { int seed = FeatureTestRunner.Deserialize(seedSerialized); - var rng = new Random(seed); + Random rng = new(seed); for (int i = 0; i < 1000; i++) { @@ -271,7 +271,7 @@ static void RunTest() Block8x8 block8x8 = Block8x8.Load(Create8x8ShortData()); - block8x8.TransposeInplace(); + block8x8.TransposeInPlace(); short[] actual = new short[64]; block8x8.CopyTo(actual); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 5a1488c411..b17a39f388 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -46,7 +46,7 @@ public void LLM_TransformIDCT_CompareToNonOptimized(int seed) { float[] sourceArray = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var srcBlock = Block8x8F.Load(sourceArray); + Block8x8F srcBlock = Block8x8F.Load(sourceArray); // reference Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref srcBlock); @@ -62,7 +62,7 @@ public void LLM_TransformIDCT_CompareToNonOptimized(int seed) FloatingPointDCT.AdjustToIDCT(ref dequantMatrix); // IDCT implementation tranforms blocks after transposition - srcBlock.TransposeInplace(); + srcBlock.TransposeInPlace(); srcBlock.MultiplyInPlace(ref dequantMatrix); // IDCT calculation @@ -79,7 +79,7 @@ public void LLM_TransformIDCT_CompareToAccurate(int seed) { float[] sourceArray = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var srcBlock = Block8x8F.Load(sourceArray); + Block8x8F srcBlock = Block8x8F.Load(sourceArray); // reference Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref srcBlock); @@ -95,7 +95,7 @@ public void LLM_TransformIDCT_CompareToAccurate(int seed) FloatingPointDCT.AdjustToIDCT(ref dequantMatrix); // IDCT implementation tranforms blocks after transposition - srcBlock.TransposeInplace(); + srcBlock.TransposeInPlace(); srcBlock.MultiplyInPlace(ref dequantMatrix); // IDCT calculation @@ -136,7 +136,7 @@ static void RunTest(string serialized) // testee // IDCT implementation tranforms blocks after transposition - srcBlock.TransposeInplace(); + srcBlock.TransposeInPlace(); FloatingPointDCT.TransformIDCT(ref srcBlock); float[] actualDest = srcBlock.ToArray(); @@ -152,7 +152,7 @@ static void RunTest(string serialized) FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -182,7 +182,7 @@ public void TranformIDCT_4x4(int seed) // testee // IDCT implementation tranforms blocks after transposition - srcBlock.TransposeInplace(); + srcBlock.TransposeInPlace(); ScaledFloatingPointDCT.TransformIDCT_4x4(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue); Span expectedSpan = expectedDest.AsSpan(); @@ -193,7 +193,7 @@ public void TranformIDCT_4x4(int seed) { for (int x = 0; x < 4; x++) { - AssertScaledElementEquality(expectedSpan.Slice((y * 16) + (x * 2)), actualSpan.Slice((y * 8) + x)); + AssertScaledElementEquality(expectedSpan[((y * 16) + (x * 2))..], actualSpan[((y * 8) + x)..]); } } @@ -210,7 +210,7 @@ static void AssertScaledElementEquality(Span expected, Span actual } } - average2x2 = MathF.Round(average2x2 / 4f); + average2x2 /= 4f; Assert.Equal((int)average2x2, (int)actual[0]); } @@ -243,7 +243,7 @@ public void TranformIDCT_2x2(int seed) // testee // IDCT implementation tranforms blocks after transposition - srcBlock.TransposeInplace(); + srcBlock.TransposeInPlace(); ScaledFloatingPointDCT.TransformIDCT_2x2(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue); Span expectedSpan = expectedDest.AsSpan(); @@ -254,7 +254,7 @@ public void TranformIDCT_2x2(int seed) { for (int x = 0; x < 2; x++) { - AssertScaledElementEquality(expectedSpan.Slice((y * 32) + (x * 4)), actualSpan.Slice((y * 8) + x)); + AssertScaledElementEquality(expectedSpan[((y * 32) + (x * 4))..], actualSpan[((y * 8) + x)..]); } } @@ -271,7 +271,7 @@ static void AssertScaledElementEquality(Span expected, Span actual } } - average4x4 = MathF.Round(average4x4 / 16f); + average4x4 /= 16f; Assert.Equal((int)average4x4, (int)actual[0]); } @@ -311,7 +311,7 @@ public void TranformIDCT_1x1(int seed) NormalizationValue, MaxOutputValue); - float expected = MathF.Round(Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue)); + float expected = Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue); Assert.Equal((int)actual, (int)expected); } @@ -338,7 +338,7 @@ static void RunTest(string serialized) // Second transpose call is done by Quantize step // Do this manually here just to be complient to the reference implementation FloatingPointDCT.TransformFDCT(ref block); - block.TransposeInplace(); + block.TransposeInPlace(); // Part of the IDCT calculations is fused into the quantization step // We must multiply input block with adjusted no-quantization matrix @@ -352,15 +352,14 @@ static void RunTest(string serialized) Assert.Equal(expectedDest, actualDest, new ApproximateFloatComparer(1f)); } - // 4 paths: - // 1. AllowAll - call avx/fma implementation - // 2. DisableFMA - call avx without fma implementation - // 3. DisableAvx - call Vector4 implementation - // 4. DisableHWIntrinsic - call scalar fallback implementation + // 3 paths: + // 1. AllowAll - call avx implementation + // 2. DisableAvx - call Vector4 implementation + // 3. DisableHWIntrinsic - call scalar fallback implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs index b89d9ad27a..36b792fa1c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs @@ -69,7 +69,7 @@ public void GetHuffmanEncodingLength_Random(int seed) { int maxNumber = 1 << 16; - var rng = new Random(seed); + Random rng = new(seed); for (int i = 0; i < 1000; i++) { uint number = (uint)rng.Next(0, maxNumber); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs index 3b7e20eb4e..22b25a2bef 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs @@ -10,13 +10,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg; public class JFifMarkerTests { // Taken from actual test image - private readonly byte[] bytes = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x60, 0x0, 0x60, 0x0 }; + private readonly byte[] bytes = [0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x60, 0x0, 0x60, 0x0]; // Altered components - private readonly byte[] bytes2 = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x48, 0x0, 0x48, 0x0 }; + private readonly byte[] bytes2 = [0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x48, 0x0, 0x48, 0x0]; // Incorrect density values. Zero is invalid. - private readonly byte[] bytes3 = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0 }; + private readonly byte[] bytes3 = [0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0]; [Fact] public void MarkerLengthIsCorrect() @@ -40,7 +40,7 @@ public void MarkerReturnsCorrectParsedValue() [Fact] public void MarkerIgnoresIncorrectValue() { - bool isJFif = JFifMarker.TryParse(new byte[] { 0, 0, 0, 0 }, out JFifMarker marker); + bool isJFif = JFifMarker.TryParse([0, 0, 0, 0], out JFifMarker marker); Assert.False(isJFif); Assert.Equal(default, marker); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index ef9f48a890..de42e85d7c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -1,55 +1,54 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -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.ColorProfiles.Icc; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Tests.Colorspaces.Conversion; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.ColorProfiles; +using SixLabors.ImageSharp.Tests.ColorProfiles.Icc; using SixLabors.ImageSharp.Tests.TestUtilities; -using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Formats.Jpg; [Trait("Format", "Jpg")] public class JpegColorConverterTests { - private const float MaxColorChannelValue = 255f; + private const float MaxColorChannelValue = 255F; + private const float ColorProfileTolerance = 0.1F / MaxColorChannelValue; + private const float ToRgbTolerance = 0.0001F; + private const float FromRgbTolerance = 0.01F; - private const float Precision = 0.1F / 255; - - private const int TestBufferLength = 40; - - private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2; - - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision); - - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); - - public static readonly TheoryData Seeds = new() { 1, 2, 3 }; - - public JpegColorConverterTests(ITestOutputHelper output) - => this.Output = output; - - private ITestOutputHelper Output { get; } + // Independent model checks compare normalized colors at one tenth of a byte-domain sample. + private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: ColorProfileTolerance); + /// + /// Verifies that unsupported color spaces are rejected by the converter factory. + /// [Fact] public void GetConverterThrowsExceptionOnInvalidColorSpace() { - JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); + const JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); + Assert.Throws(() => JpegColorConverterBase.GetConverter(invalidColorSpace, 8)); } + /// + /// Verifies that unsupported JPEG sample precisions are rejected by the converter factory. + /// [Fact] public void GetConverterThrowsExceptionOnInvalidPrecision() { - // Valid precisions: 8 & 12 bit - int invalidPrecision = 9; + const int invalidPrecision = 9; + Assert.Throws(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, invalidPrecision)); } + /// + /// Verifies that every supported color-space and precision pair resolves to the shared operator converter. + /// + /// The JPEG color space. + /// The JPEG sample precision. [Theory] [InlineData(JpegColorSpace.Grayscale, 8)] [InlineData(JpegColorSpace.Grayscale, 12)] @@ -61,802 +60,570 @@ public void GetConverterThrowsExceptionOnInvalidPrecision() [InlineData(JpegColorSpace.RGB, 12)] [InlineData(JpegColorSpace.YCbCr, 8)] [InlineData(JpegColorSpace.YCbCr, 12)] + [InlineData(JpegColorSpace.TiffCmyk, 8)] + [InlineData(JpegColorSpace.TiffCmyk, 12)] + [InlineData(JpegColorSpace.TiffYccK, 8)] + [InlineData(JpegColorSpace.TiffYccK, 12)] internal void GetConverterReturnsValidConverter(JpegColorSpace colorSpace, int precision) { JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, precision); - Assert.NotNull(converter); Assert.True(converter.IsAvailable); Assert.Equal(colorSpace, converter.ColorSpace); Assert.Equal(precision, converter.Precision); } - [Fact] - public void GetConverterReturnsCorrectConverterWithRgbColorSpace() - { - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); - - static void RunTest(string arg) - { - // arrange - Type expectedType = typeof(JpegColorConverterBase.RgbScalar); - if (Avx.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.RgbAvx); - } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.RgbVector); - } - else if (AdvSimd.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.RgbArm); - } - - // act - JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, 8); - Type actualType = converter.GetType(); - - // assert - Assert.Equal(expectedType, actualType); - } - } - - [Fact] - public void GetConverterReturnsCorrectConverterWithGrayScaleColorSpace() - { - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); - - static void RunTest(string arg) - { - // arrange - Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar); - if (Avx.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.GrayscaleAvx); - } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.GrayScaleVector); - } - else if (AdvSimd.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.GrayscaleArm); - } - - // act - JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, 8); - Type actualType = converter.GetType(); - - // assert - Assert.Equal(expectedType, actualType); - } - } - - [Fact] - public void GetConverterReturnsCorrectConverterWithCmykColorSpace() - { - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); - - static void RunTest(string arg) - { - // arrange - Type expectedType = typeof(JpegColorConverterBase.CmykScalar); - if (Avx.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.CmykAvx); - } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.CmykVector); - } - else if (AdvSimd.Arm64.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.CmykArm64); - } - - // act - JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Cmyk, 8); - Type actualType = converter.GetType(); - - // assert - Assert.Equal(expectedType, actualType); - } - } - - [Fact] - public void GetConverterReturnsCorrectConverterWithYCbCrColorSpace() - { - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); - - static void RunTest(string arg) - { - // arrange - Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar); - if (Avx.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YCbCrAvx); - } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YCbCrVector); - } - else if (AdvSimd.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YCbCrArm); - } - - // act - JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 8); - Type actualType = converter.GetType(); - - // assert - Assert.Equal(expectedType, actualType); - } - } - - [Fact] - public void GetConverterReturnsCorrectConverterWithYcckColorSpace() + /// + /// Verifies that the converter factory closes the shared traversal over the matching color-model operator. + /// + /// The JPEG color space. + /// The expected closed converter type. + [Theory] + [InlineData(JpegColorSpace.Grayscale, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.RGB, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.Cmyk, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.YCbCr, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.Ycck, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.TiffCmyk, typeof(JpegColorConverterBase.JpegColorConverter))] + [InlineData(JpegColorSpace.TiffYccK, typeof(JpegColorConverterBase.JpegColorConverter))] + internal void GetConverterReturnsClosedOperatorConverter(JpegColorSpace colorSpace, Type expectedType) { - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); - - static void RunTest(string arg) - { - // arrange - Type expectedType = typeof(JpegColorConverterBase.YccKScalar); - if (Avx.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YccKAvx); - } - else if (Sse2.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YccKVector); - } - else if (AdvSimd.Arm64.IsSupported) - { - expectedType = typeof(JpegColorConverterBase.YccKArm64); - } - - // act - JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Ycck, 8); - Type actualType = converter.GetType(); + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, 8); - // assert - Assert.Equal(expectedType, actualType); - } + Assert.Equal(expectedType, converter.GetType()); } + /// + /// Verifies the replacement converter against independent definitions of the established JPEG color models. + /// + /// The JPEG color space. + /// The number of component planes owned by the color model. [Theory] [InlineData(JpegColorSpace.Grayscale, 1)] - [InlineData(JpegColorSpace.Ycck, 4)] - [InlineData(JpegColorSpace.Cmyk, 4)] [InlineData(JpegColorSpace.RGB, 3)] + [InlineData(JpegColorSpace.Cmyk, 4)] [InlineData(JpegColorSpace.YCbCr, 3)] - internal void ConvertToRgbWithSelectedConverter(JpegColorSpace colorSpace, int componentCount) + [InlineData(JpegColorSpace.Ycck, 4)] + internal void ConvertToRgbMatchesColorModelDefinition(JpegColorSpace colorSpace, int componentCount) { + const int length = 128; + JpegColorConverterBase.ComponentValues source = CreateRandomValues(length, componentCount, 8); + JpegColorConverterBase.ComponentValues actual = CreateRandomValues(length, componentCount, 8); JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, 8); - ValidateConversionToRgb( - converter, - componentCount, - 1); - } - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YCbCrScalar(8), 3, seed); + converter.ConvertToRgbInPlace(actual); - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrVector(int seed) - { - JpegColorConverterBase.YCbCrVector converter = new(8); - - if (!converter.IsAvailable) + for (int i = 0; i < length; i++) { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + AssertColorModelDefinition(colorSpace, source, actual, i); } - - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.YCbCrVector(8), - 3, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.YCbCrScalar(8)); } + /// + /// Verifies the adaptive traversal against each operator's scalar definition around every SIMD boundary. + /// + /// The JPEG color space. + /// The number of component planes owned by the color model. + /// The JPEG sample precision. [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.CmykScalar(8), 4, seed); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykVector(int seed) + [InlineData(JpegColorSpace.Grayscale, 1, 8)] + [InlineData(JpegColorSpace.Grayscale, 1, 12)] + [InlineData(JpegColorSpace.RGB, 3, 8)] + [InlineData(JpegColorSpace.RGB, 3, 12)] + [InlineData(JpegColorSpace.Cmyk, 4, 8)] + [InlineData(JpegColorSpace.Cmyk, 4, 12)] + [InlineData(JpegColorSpace.YCbCr, 3, 8)] + [InlineData(JpegColorSpace.YCbCr, 3, 12)] + [InlineData(JpegColorSpace.Ycck, 4, 8)] + [InlineData(JpegColorSpace.Ycck, 4, 12)] + [InlineData(JpegColorSpace.TiffCmyk, 4, 8)] + [InlineData(JpegColorSpace.TiffCmyk, 4, 12)] + [InlineData(JpegColorSpace.TiffYccK, 4, 8)] + [InlineData(JpegColorSpace.TiffYccK, 4, 12)] + internal void OperatorTraversalMatchesScalarDefinition(JpegColorSpace colorSpace, int componentCount, int precision) { - JpegColorConverterBase.CmykVector converter = new(8); - - if (!converter.IsAvailable) + switch (colorSpace) { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + case JpegColorSpace.Grayscale: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.RGB: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.Cmyk: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.YCbCr: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.Ycck: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.TiffCmyk: + ValidateOperator(componentCount, precision); + break; + case JpegColorSpace.TiffYccK: + ValidateOperator(componentCount, precision); + break; + default: + Assert.Fail($"Unexpected JPEG color space: {colorSpace}."); + break; } - - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.CmykVector(8), - 4, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.CmykScalar(8)); } + /// + /// Verifies the flattened ICC traversal against independently composed color-model and profile conversions. + /// + /// The JPEG color space. + /// The number of component planes owned by the color model. + /// The JPEG sample precision. [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleScalar(8), 1, seed); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleVector(int seed) + [InlineData(JpegColorSpace.Grayscale, 1, 8)] + [InlineData(JpegColorSpace.Grayscale, 1, 12)] + [InlineData(JpegColorSpace.RGB, 3, 8)] + [InlineData(JpegColorSpace.RGB, 3, 12)] + [InlineData(JpegColorSpace.YCbCr, 3, 8)] + [InlineData(JpegColorSpace.YCbCr, 3, 12)] + [InlineData(JpegColorSpace.Cmyk, 4, 8)] + [InlineData(JpegColorSpace.Cmyk, 4, 12)] + [InlineData(JpegColorSpace.Ycck, 4, 8)] + [InlineData(JpegColorSpace.Ycck, 4, 12)] + [InlineData(JpegColorSpace.TiffCmyk, 4, 8)] + [InlineData(JpegColorSpace.TiffCmyk, 4, 12)] + [InlineData(JpegColorSpace.TiffYccK, 4, 8)] + [InlineData(JpegColorSpace.TiffYccK, 4, 12)] + internal void ConvertToRgbWithIccMatchesColorProfileDefinition(JpegColorSpace colorSpace, int componentCount, int precision) { - JpegColorConverterBase.GrayScaleVector converter = new(8); + const int length = 8; + JpegColorConverterBase.ComponentValues source = CreateRandomValues(length, componentCount, precision); + JpegColorConverterBase.ComponentValues actual = CreateRandomValues(length, componentCount, precision); + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, precision); - if (!converter.IsAvailable) + // YccK and CMYK profiles describe the conventional CMYK values produced after JPEG inversion + // and any YccK model transform. Three-component models use an RGB profile, while grayscale + // needs a one-channel profile so the profile's declared data space agrees with the Y input. + IccProfile profile = colorSpace switch { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; - } + JpegColorSpace.Grayscale => CreateGrayProfile(), + JpegColorSpace.Cmyk or JpegColorSpace.Ycck or JpegColorSpace.TiffCmyk or JpegColorSpace.TiffYccK + => TestIccProfiles.GetProfile(TestIccProfiles.Fogra39), + _ => CompactSrgbV4Profile.Profile, + }; + + converter.ConvertToRgbInPlaceWithIcc(Configuration.Default, actual, profile); + ColorConversionOptions options = new() + { + SourceIccProfile = profile, + TargetIccProfile = CompactSrgbV4Profile.Profile, + }; - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.GrayScaleVector(8), - 1, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.GrayscaleScalar(8)); - } + ColorProfileConverter profileConverter = new(options); + ColorProfileConverter modelConverter = new(); + float maximumValue = MathF.Pow(2, precision) - 1; + Rgb[] grayscaleExpected = null; - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.RgbScalar(8), 3, seed); + if (colorSpace == JpegColorSpace.Grayscale) + { + // The profile converter has distinct scalar and span entry points. Production converts the complete + // grayscale row, so the independent reference must exercise that same public span contract. + Y[] luminance = new Y[length]; + grayscaleExpected = new Rgb[length]; - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbVector(int seed) - { - JpegColorConverterBase.RgbVector converter = new(8); + for (int i = 0; i < length; i++) + { + luminance[i] = new Y(source.Component0[i] / maximumValue); + } - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + profileConverter.Convert(luminance, grayscaleExpected); } - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.RgbVector(8), - 3, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.RgbScalar(8)); - } - - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKBasic(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YccKScalar(8), 4, seed); + for (int i = 0; i < length; i++) + { + float c0 = source.Component0[i] / maximumValue; + float c1 = componentCount >= 2 ? source.Component1[i] / maximumValue : 0; + float c2 = componentCount >= 3 ? source.Component2[i] / maximumValue : 0; + float c3 = componentCount == 4 ? source.Component3[i] / maximumValue : 0; + Rgb expected; - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKVector(int seed) - { - JpegColorConverterBase.YccKVector converter = new(8); + switch (colorSpace) + { + case JpegColorSpace.Grayscale: + expected = grayscaleExpected[i]; + break; + + case JpegColorSpace.RGB: + expected = profileConverter.Convert(new Rgb(c0, c1, c2)); + break; + + case JpegColorSpace.YCbCr: + Rgb rgb = modelConverter.Convert(new YCbCr(c0, c1, c2)); + expected = profileConverter.Convert(rgb); + break; + + case JpegColorSpace.Cmyk: + expected = profileConverter.Convert(new Cmyk(1F - c0, 1F - c1, 1F - c2, 1F - c3)); + break; + + case JpegColorSpace.Ycck: + Cmyk cmyk = modelConverter.Convert(new YccK(1F - c0, 1F - c1, 1F - c2, 1F - c3)); + expected = profileConverter.Convert(cmyk); + break; + + case JpegColorSpace.TiffCmyk: + expected = profileConverter.Convert(new Cmyk(c0, c1, c2, c3)); + break; + + case JpegColorSpace.TiffYccK: + Cmyk tiffCmyk = modelConverter.Convert(new YccK(c0, c1, c2, c3)); + expected = profileConverter.Convert(tiffCmyk); + break; + + default: + Assert.Fail($"Unexpected JPEG color space: {colorSpace}."); + return; + } - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + if (colorSpace == JpegColorSpace.Grayscale) + { + // Grayscale exposes one physical component plane through all three component views. Deinterleaving + // therefore leaves the final blue write in that plane, matching the established converter contract. + Assert.Equal(expected.B, actual.Component0[i], ToRgbTolerance); + } + else + { + Assert.Equal(expected.R, actual.Component0[i], ToRgbTolerance); + Assert.Equal(expected.G, actual.Component1[i], ToRgbTolerance); + Assert.Equal(expected.B, actual.Component2[i], ToRgbTolerance); + } } - - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - seed, - IntrinsicsConfig); - - static void RunTest(string arg) => - ValidateConversionToRgb( - new JpegColorConverterBase.YccKVector(8), - 4, - FeatureTestRunner.Deserialize(arg), - new JpegColorConverterBase.YccKScalar(8)); } - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YCbCrAvx(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYCbCrAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YCbCrAvx(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8), - precísion: 2); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromYCbCrArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YCbCrArm(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYCbCrArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YCbCrArm(8), - 3, - seed, - new JpegColorConverterBase.YCbCrScalar(8), - precísion: 2); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.CmykAvx(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToCmykAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.CmykAvx(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8), - precísion: 4); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromCmykArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.CmykArm64(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToCmykArm(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.CmykArm64(8), - 4, - seed, - new JpegColorConverterBase.CmykScalar(8), - precísion: 4); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.GrayscaleAvx(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToGrayscaleAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.GrayscaleAvx(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8), - precísion: 3); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromGrayscaleArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8)); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToGrayscaleArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8), - 1, - seed, - new JpegColorConverterBase.GrayscaleScalar(8), - precísion: 3); - - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.RgbAvx(8), - 3, - seed, - new JpegColorConverterBase.RgbScalar(8)); + /// + /// Verifies that the shared converter retains its scalar behavior when hardware intrinsics are disabled. + /// + [Fact] + public void OperatorTraversalMatchesScalarWithoutHardwareIntrinsics() + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunWithoutHardwareIntrinsics, HwIntrinsics.DisableHWIntrinsic); - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbArm(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.RgbArm(8), - 3, - seed, - new JpegColorConverterBase.RgbScalar(8)); + /// + /// Verifies TIFF YccK encoding against the canonical normalized color-profile conversion. + /// + [Fact] + public void TiffYccKOperatorFromRgbMatchesColorProfileDefinition() + { + const int maximumLength = 40; + const float maximumValue = 255F; + const float halfValue = 128F; + float[] rSeed = [0, 255, 255, 0, 0, 127, 32, 240, 0, 255, 255, 0, 0, 127, 32, 240, 0, 255, 64, 192]; + float[] gSeed = [0, 255, 0, 255, 0, 127, 160, 16, 0, 255, 0, 255, 0, 127, 160, 16, 255, 0, 128, 96]; + float[] bSeed = [0, 255, 0, 0, 255, 127, 224, 80, 255, 0, 255, 0, 0, 127, 224, 80, 0, 255, 192, 32]; + float[] r = new float[maximumLength]; + float[] g = new float[maximumLength]; + float[] b = new float[maximumLength]; + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.TiffYccK, 8); + + // Repeating the color set provides enough lanes to exercise every SIMD width and mixed-width tail. + rSeed.CopyTo(r, 0); + rSeed.CopyTo(r, rSeed.Length); + gSeed.CopyTo(g, 0); + gSeed.CopyTo(g, gSeed.Length); + bSeed.CopyTo(b, 0); + bSeed.CopyTo(b, bSeed.Length); + + ColorProfileConverter reference = new(); + int[] lengths = [1, 3, 4, 7, 8, 15, 16, 31, 32, 40]; + + foreach (int length in lengths) + { + float[] y = new float[length]; + float[] cb = new float[length]; + float[] cr = new float[length]; + float[] k = new float[length]; + JpegColorConverterBase.ComponentValues values = new(4, y, cb, cr, k); - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKAvx2(int seed) => - this.TestConversionToRgb( - new JpegColorConverterBase.YccKAvx(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8)); + converter.ConvertFromRgb(values, r.AsSpan(0, length), g.AsSpan(0, length), b.AsSpan(0, length)); - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYccKAvx2(int seed) => - this.TestConversionFromRgb( - new JpegColorConverterBase.YccKAvx(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8), - precísion: 4); + for (int i = 0; i < length; i++) + { + Rgb rgb = new(r[i] / maximumValue, g[i] / maximumValue, b[i] / maximumValue); + YccK expected = reference.Convert(rgb); - [Theory] - [MemberData(nameof(Seeds))] - public void FromYccKArm64(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YccKArm64(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8)); + Assert.Equal(expected.Y * maximumValue, y[i], ToRgbTolerance); - [Theory] - [MemberData(nameof(Seeds))] - public void FromRgbToYccKArm64(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YccKArm64(8), - 4, - seed, - new JpegColorConverterBase.YccKScalar(8), - precísion: 4); - - private void TestConversionToRgb( - JpegColorConverterBase converter, - int componentCount, - int seed, - JpegColorConverterBase baseLineConverter = null) - { - if (!converter.IsAvailable) - { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + // JPEG centers chroma on the integer midpoint, while the color-profile definition uses exactly 0.5. + Assert.Equal(halfValue + ((expected.Cb - 0.5F) * maximumValue), cb[i], ToRgbTolerance); + Assert.Equal(halfValue + ((expected.Cr - 0.5F) * maximumValue), cr[i], ToRgbTolerance); + Assert.Equal(expected.K * maximumValue, k[i], ToRgbTolerance); + } } - - ValidateConversionToRgb( - converter, - componentCount, - seed, - baseLineConverter); } - private void TestConversionFromRgb( - JpegColorConverterBase converter, - int componentCount, - int seed, - JpegColorConverterBase baseLineConverter, - int precísion) + /// + /// Runs the disabled-intrinsics scalar comparison inside the feature-test process. + /// + /// The unused feature-test argument. + private static void RunWithoutHardwareIntrinsics(string arg) + => ValidateOperator(3, 8); + + /// + /// Checks one closed operator converter at every scalar and SIMD transition length. + /// + /// The color-model operator under test. + /// The number of component planes owned by the operator. + /// The JPEG sample precision. + private static void ValidateOperator(int componentCount, int precision) + where TOperator : struct, JpegColorConverterBase.IJpegColorConverterOperator { - if (!converter.IsAvailable) + JpegColorConverterBase converter = new JpegColorConverterBase.JpegColorConverter(precision); + int[] lengths = [1, 3, 4, 7, 8, 15, 16, 31, 32, 40, 64, 128]; + + // Adjacent values around 4/8/16 lanes verify every prefix, mixed-width tail, and scalar remainder. + foreach (int length in lengths) { - this.Output.WriteLine( - $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); - return; + ValidateConversionToRgb(converter, length, componentCount, precision); + ValidateConversionFromRgb(converter, length, componentCount, precision); } - - ValidateConversionFromRgb( - converter, - componentCount, - seed, - baseLineConverter, - precísion); } - private static JpegColorConverterBase.ComponentValues CreateRandomValues( - int length, - int componentCount, - int seed) + /// + /// Compares the adaptive component-to-RGB traversal with repeated scalar operator calls. + /// + /// The color-model operator under test. + /// The adaptive converter. + /// The number of samples to convert. + /// The number of source component planes. + /// The JPEG sample precision. + private static void ValidateConversionToRgb(JpegColorConverterBase converter, int length, int componentCount, int precision) + where TOperator : struct, JpegColorConverterBase.IJpegColorConverterOperator { - var rnd = new Random(seed); + JpegColorConverterBase.ComponentValues expected = CreateRandomValues(length, componentCount, precision); + JpegColorConverterBase.ComponentValues actual = CreateRandomValues(length, componentCount, precision); + float maximumValue = MathF.Pow(2, precision) - 1; + float halfValue = MathF.Ceiling(maximumValue * 0.5F); + float scale = 1F / maximumValue; - var buffers = new Buffer2D[componentCount]; - for (int i = 0; i < componentCount; i++) + for (int i = 0; i < length; i++) { - float[] values = new float[length]; - - for (int j = 0; j < values.Length; j++) - { - values[j] = (float)rnd.NextDouble() * MaxColorChannelValue; - } + ref float c0 = ref expected.Component0[i]; + ref float c1 = ref expected.Component1[i]; + ref float c2 = ref expected.Component2[i]; + float c3 = componentCount == 4 ? expected.Component3[i] : 0; - // no need to dispose when buffer is not array owner - var memory = new Memory(values); - var source = MemoryGroup.Wrap(memory); - buffers[i] = new Buffer2D(source, values.Length, 1); + TOperator.ConvertToRgb(ref c0, ref c1, ref c2, c3, maximumValue, halfValue, scale); } - return new JpegColorConverterBase.ComponentValues(buffers, 0); - } + converter.ConvertToRgbInPlace(actual); - private static float[] CreateRandomValues(int length, Random rnd) - { - float[] values = new float[length]; - - for (int j = 0; j < values.Length; j++) - { - values[j] = (float)rnd.NextDouble() * MaxColorChannelValue; - } - - return values; + // YCbCr conversion rounds in the integer sample domain before normalization. Fused SIMD arithmetic can + // cross a half-way boundary differently from the scalar expression, so allow one source-sample quantum + // in addition to the ordinary floating-point tolerance at both supported sample precisions. + float tolerance = scale + ToRgbTolerance; + CompareSequence(expected.Component0, actual.Component0, tolerance); + CompareSequence(expected.Component1, actual.Component1, tolerance); + CompareSequence(expected.Component2, actual.Component2, tolerance); } - private static void ValidateConversionToRgb( - JpegColorConverterBase converter, - int componentCount, - int seed, - JpegColorConverterBase baseLineConverter = null) + /// + /// Compares the adaptive RGB-to-component traversal with repeated scalar operator calls. + /// + /// The color-model operator under test. + /// The adaptive converter. + /// The number of samples to convert. + /// The number of destination component planes. + /// The JPEG sample precision. + private static void ValidateConversionFromRgb(JpegColorConverterBase converter, int length, int componentCount, int precision) + where TOperator : struct, JpegColorConverterBase.IJpegColorConverterOperator { - JpegColorConverterBase.ComponentValues original = CreateRandomValues(TestBufferLength, componentCount, seed); - JpegColorConverterBase.ComponentValues actual = new( - original.ComponentCount, - original.Component0.ToArray(), - original.Component1.ToArray(), - original.Component2.ToArray(), - original.Component3.ToArray()); - - converter.ConvertToRgbInplace(actual); - - for (int i = 0; i < TestBufferLength; i++) - { - Validate(converter.ColorSpace, original, actual, i); - } - - // Compare conversion result to a baseline, should be the scalar version. - if (baseLineConverter != null) + JpegColorConverterBase.ComponentValues expected = CreateRandomValues(length, componentCount, precision); + JpegColorConverterBase.ComponentValues actual = CreateRandomValues(length, componentCount, precision); + Random random = new(precision); + float[] r = CreateRandomValues(length, random); + float[] g = CreateRandomValues(length, random); + float[] b = CreateRandomValues(length, random); + float maximumValue = MathF.Pow(2, precision) - 1; + float halfValue = MathF.Ceiling(maximumValue * 0.5F); + float scale = 1F / maximumValue; + + for (int i = 0; i < length; i++) { - JpegColorConverterBase.ComponentValues expected = new( - original.ComponentCount, - original.Component0.ToArray(), - original.Component1.ToArray(), - original.Component2.ToArray(), - original.Component3.ToArray()); - baseLineConverter.ConvertToRgbInplace(expected); - if (componentCount == 1) - { - Assert.True(expected.Component0.SequenceEqual(actual.Component0)); - } + TOperator.ConvertFromRgb(r[i], g[i], b[i], maximumValue, halfValue, scale, out expected.Component0[i], out float c1, out float c2, out float c3); - if (componentCount == 2) + if (componentCount >= 2) { - Assert.True(expected.Component1.SequenceEqual(actual.Component1)); + expected.Component1[i] = c1; } - if (componentCount == 3) + if (componentCount >= 3) { - Assert.True(expected.Component2.SequenceEqual(actual.Component2)); + expected.Component2[i] = c2; } if (componentCount == 4) { - Assert.True(expected.Component3.SequenceEqual(actual.Component3)); + expected.Component3[i] = c3; } } - } - private static void ValidateConversionFromRgb( - JpegColorConverterBase converter, - int componentCount, - int seed, - JpegColorConverterBase baseLineConverter, - int precision = 4) - { - // arrange - JpegColorConverterBase.ComponentValues actual = CreateRandomValues(TestBufferLength, componentCount, seed); - JpegColorConverterBase.ComponentValues expected = CreateRandomValues(TestBufferLength, componentCount, seed); - Random rnd = new(seed); - float[] rLane = CreateRandomValues(TestBufferLength, rnd); - float[] gLane = CreateRandomValues(TestBufferLength, rnd); - float[] bLane = CreateRandomValues(TestBufferLength, rnd); - - // act - converter.ConvertFromRgb(actual, rLane, gLane, bLane); - baseLineConverter.ConvertFromRgb(expected, rLane, gLane, bLane); - - // assert - if (componentCount == 1) - { - CompareSequenceWithTolerance(expected.Component0, actual.Component0, precision); - } + converter.ConvertFromRgb(actual, r, g, b); + CompareSequence(expected.Component0, actual.Component0, FromRgbTolerance); - if (componentCount == 2) + if (componentCount >= 2) { - CompareSequenceWithTolerance(expected.Component1, actual.Component1, precision); + CompareSequence(expected.Component1, actual.Component1, FromRgbTolerance); } - if (componentCount == 3) + if (componentCount >= 3) { - CompareSequenceWithTolerance(expected.Component2, actual.Component2, precision); + CompareSequence(expected.Component2, actual.Component2, FromRgbTolerance); } if (componentCount == 4) { - CompareSequenceWithTolerance(expected.Component3, actual.Component3, precision); + CompareSequence(expected.Component3, actual.Component3, FromRgbTolerance); } } - private static void CompareSequenceWithTolerance(Span expected, Span actual, int precision) + /// + /// Creates deterministic component planes in the configured JPEG sample domain. + /// + /// The number of samples in each plane. + /// The number of independent component planes. + /// The JPEG sample precision and deterministic random seed. + /// The generated component planes. + private static JpegColorConverterBase.ComponentValues CreateRandomValues(int length, int componentCount, int precision) { - for (int i = 0; i < expected.Length; i++) + Random random = new(precision); + float maximumValue = MathF.Pow(2, precision) - 1; + float[] c0 = CreateRandomValues(length, random, maximumValue); + float[] c1 = componentCount >= 2 ? CreateRandomValues(length, random, maximumValue) : c0; + float[] c2 = componentCount >= 3 ? CreateRandomValues(length, random, maximumValue) : c0; + float[] c3 = componentCount == 4 ? CreateRandomValues(length, random, maximumValue) : []; + + return new JpegColorConverterBase.ComponentValues(componentCount, c0, c1, c2, c3); + } + + /// + /// Creates deterministic RGB samples in ImageSharp's byte-scaled encoder domain. + /// + /// The number of samples. + /// The deterministic random source. + /// The generated samples. + private static float[] CreateRandomValues(int length, Random random) + => CreateRandomValues(length, random, 255F); + + /// + /// Creates deterministic samples between zero and the supplied inclusive domain maximum. + /// + /// The number of samples. + /// The deterministic random source. + /// The upper bound of the sample domain. + /// The generated samples. + private static float[] CreateRandomValues(int length, Random random, float maximumValue) + { + float[] values = new float[length]; + + for (int i = 0; i < values.Length; i++) { - Assert.Equal(expected[i], actual[i], precision: precision); + values[i] = random.NextSingle() * maximumValue; } + + return values; } - private static void Validate( - JpegColorSpace colorSpace, - in JpegColorConverterBase.ComponentValues original, - in JpegColorConverterBase.ComponentValues result, - int i) + /// + /// Creates an identity-transfer grayscale profile for the one-channel ICC test path. + /// + /// The grayscale ICC profile. + private static IccProfile CreateGrayProfile() { + IccProfileHeader header = new() + { + Class = IccProfileClass.InputDevice, + DataColorSpace = IccColorSpaceType.Gray, + ProfileConnectionSpace = IccColorSpaceType.CieXyz, + RenderingIntent = IccRenderingIntent.MediaRelativeColorimetric, + Version = new IccVersion(4, 3, 0), + }; + + // An empty ICC curve is the standard identity transfer function. Tagging it as GrayTrc gives + // the profile converter a complete one-channel device-to-PCS path without test-only LUT data. + IccTagDataEntry[] entries = [new IccCurveTagDataEntry(IccProfileTag.GrayTrc)]; + return new IccProfile(header, entries); + } + + /// + /// Compares one converted sample with an independent definition of its JPEG color model. + /// + /// The JPEG color space. + /// The unmodified source component planes. + /// The converted RGB planes. + /// The sample index. + private static void AssertColorModelDefinition(JpegColorSpace colorSpace, in JpegColorConverterBase.ComponentValues source, in JpegColorConverterBase.ComponentValues actual, int index) + { + float c0 = source.Component0[index]; + float c1 = source.Component1[index]; + float c2 = source.Component2[index]; + float c3 = 0; + Rgb expected; + switch (colorSpace) { case JpegColorSpace.Grayscale: - ValidateGrayScale(original, result, i); + float luminance = c0 / MaxColorChannelValue; + expected = new Rgb(luminance, luminance, luminance); break; - case JpegColorSpace.Ycck: - ValidateCyyK(original, result, i); + case JpegColorSpace.RGB: + expected = new Rgb(c0 / MaxColorChannelValue, c1 / MaxColorChannelValue, c2 / MaxColorChannelValue); + break; case JpegColorSpace.Cmyk: - ValidateCmyk(original, result, i); - break; - case JpegColorSpace.RGB: - ValidateRgb(original, result, i); + c3 = source.Component3[index] / MaxColorChannelValue; + expected = new Rgb(c0 * c3 / MaxColorChannelValue, c1 * c3 / MaxColorChannelValue, c2 * c3 / MaxColorChannelValue); + break; case JpegColorSpace.YCbCr: - ValidateYCbCr(original, result, i); - break; - default: - Assert.True(false, $"Invalid Colorspace enum value: {colorSpace}."); - break; - } - } - - private static void ValidateYCbCr(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) - { - 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 actual = 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}"); - } - - private static void ValidateCyyK(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) - { - float y = values.Component0[i]; - float cb = values.Component1[i] - 128F; - float cr = values.Component2[i] - 128F; - float k = values.Component3[i] / 255F; - - float r = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; - float g = (255F - (float)Math.Round( - y - (0.344136F * cb) - (0.714136F * cr), - MidpointRounding.AwayFromZero)) * k; - float b = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; - - r /= MaxColorChannelValue; - g /= MaxColorChannelValue; - b /= MaxColorChannelValue; - var expected = new Rgb(r, g, b); - - var actual = new Rgb(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}"); - } + c1 -= 128F; + c2 -= 128F; - private static void ValidateRgb(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) - { - 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); + // JPEG applies the BT.601 matrix in the integer sample domain and rounds before normalization. + expected = new Rgb(MathF.Round(c0 + (1.402F * c2), MidpointRounding.AwayFromZero) / MaxColorChannelValue, MathF.Round(c0 - (0.344136F * c1) - (0.714136F * c2), MidpointRounding.AwayFromZero) / MaxColorChannelValue, MathF.Round(c0 + (1.772F * c1), MidpointRounding.AwayFromZero) / MaxColorChannelValue); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + break; + case JpegColorSpace.Ycck: + c1 -= 128F; + c2 -= 128F; + c3 = source.Component3[index] / MaxColorChannelValue; - bool equal = ColorSpaceComparer.Equals(expected, actual); - Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); - } + // Adobe YccK reconstructs inverted RGB first, then applies the normalized black component. + expected = new Rgb((MaxColorChannelValue - MathF.Round(c0 + (1.402F * c2), MidpointRounding.AwayFromZero)) * c3 / MaxColorChannelValue, (MaxColorChannelValue - MathF.Round(c0 - (0.344136F * c1) - (0.714136F * c2), MidpointRounding.AwayFromZero)) * c3 / MaxColorChannelValue, (MaxColorChannelValue - MathF.Round(c0 + (1.772F * c1), MidpointRounding.AwayFromZero)) * c3 / MaxColorChannelValue); - 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); + break; + default: + Assert.Fail($"Unexpected JPEG color space: {colorSpace}."); + return; + } - var actual = new Rgb(result.Component0[i], result.Component0[i], result.Component0[i]); + // Color-space comparison intentionally clamps both sides because JPEG reconstruction can overshoot + // the normalized RGB gamut and saturation belongs to the eventual pixel conversion. + Rgb clampedExpected = Rgb.Clamp(expected); + Rgb clampedActual = Rgb.Clamp(new Rgb(actual.Component0[index], actual.Component1[index], actual.Component2[index])); - bool equal = ColorSpaceComparer.Equals(expected, actual); - Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); + Assert.True(ColorSpaceComparer.Equals(clampedExpected, clampedActual), $"Colors {clampedExpected} and {clampedActual} are not equal at index {index}."); } - private static void ValidateCmyk(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) + /// + /// Compares two component planes using an absolute floating-point tolerance. + /// + /// The scalar reference values. + /// The adaptive traversal values. + /// The maximum permitted absolute difference. + private static void CompareSequence(Span expected, Span actual, float tolerance) { - float c = values.Component0[i]; - float m = values.Component1[i]; - float y = values.Component2[i]; - float k = values.Component3[i] / MaxColorChannelValue; - - float r = c * k / MaxColorChannelValue; - float g = m * k / MaxColorChannelValue; - float b = y * k / MaxColorChannelValue; - var expected = new Rgb(r, g, b); - - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Assert.Equal(expected.Length, actual.Length); - bool equal = ColorSpaceComparer.Equals(expected, actual); - Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i], tolerance); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorPackingTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorPackingTests.cs new file mode 100644 index 0000000000..20b4f16798 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorPackingTests.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg; + +/// +/// Tests the planar and packed buffer transformations used around JPEG color-profile conversion. +/// +[Trait("Format", "Jpg")] +public class JpegColorPackingTests +{ + private static readonly int[] Lengths = [0, 1, 2, 3, 4, 5, 7, 8, 15, 16, 17, 31, 32, 33, 129]; + + /// + /// Verifies every packing operation against its scalar definition with and without hardware intrinsics. + /// + [Fact] + public void PackingOperationsMatchScalarDefinitions() + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePackingOperations, HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Exercises every SIMD transition and scalar remainder for the packing operations. + /// + private static void ValidatePackingOperations() + { + foreach (int length in Lengths) + { + ValidatePackedNormalizeInterleave3(length); + ValidateUnpackDeinterleave3(length); + ValidatePackedNormalizeInterleave4(length); + ValidatePackedInvertNormalizeInterleave4(length); + } + } + + /// + /// Compares normalized three-plane interleaving with the original scalar loop. + /// + /// The number of samples in each component plane. + private static void ValidatePackedNormalizeInterleave3(int length) + { + const float scale = 1F / 255F; + float[] x = CreateSamples(length, 1); + float[] y = CreateSamples(length, 2); + float[] z = CreateSamples(length, 3); + float[] expected = new float[length * 3]; + float[] actual = new float[length * 3]; + + for (int i = 0; i < length; i++) + { + int packedOffset = i * 3; + expected[packedOffset] = x[i] * scale; + expected[packedOffset + 1] = y[i] * scale; + expected[packedOffset + 2] = z[i] * scale; + } + + JpegColorConverterBase.PackedNormalizeInterleave3(x, y, z, actual, scale); + + Assert.Equal(expected, actual); + } + + /// + /// Compares packed three-channel deinterleaving with the original scalar loop. + /// + /// The number of packed values. + private static void ValidateUnpackDeinterleave3(int length) + { + Vector3[] packed = new Vector3[length]; + float[] expectedX = CreateSamples(length, 1); + float[] expectedY = CreateSamples(length, 2); + float[] expectedZ = CreateSamples(length, 3); + float[] actualX = new float[length]; + float[] actualY = new float[length]; + float[] actualZ = new float[length]; + + for (int i = 0; i < length; i++) + { + packed[i] = new Vector3(expectedX[i], expectedY[i], expectedZ[i]); + } + + JpegColorConverterBase.UnpackDeinterleave3(packed, actualX, actualY, actualZ); + + Assert.Equal(expectedX, actualX); + Assert.Equal(expectedY, actualY); + Assert.Equal(expectedZ, actualZ); + } + + /// + /// Compares normalized four-plane interleaving with the original scalar loop. + /// + /// The number of samples in each component plane. + private static void ValidatePackedNormalizeInterleave4(int length) + { + const float maximumValue = 255F; + const float scale = 1F / maximumValue; + float[] x = CreateSamples(length, 1); + float[] y = CreateSamples(length, 2); + float[] z = CreateSamples(length, 3); + float[] w = CreateSamples(length, 4); + float[] expected = new float[length * 4]; + float[] actual = new float[length * 4]; + + for (int i = 0; i < length; i++) + { + int packedOffset = i * 4; + expected[packedOffset] = x[i] * scale; + expected[packedOffset + 1] = y[i] * scale; + expected[packedOffset + 2] = z[i] * scale; + expected[packedOffset + 3] = w[i] * scale; + } + + JpegColorConverterBase.PackedNormalizeInterleave4(x, y, z, w, actual, maximumValue); + + Assert.Equal(expected, actual); + } + + /// + /// Compares inverted normalized four-plane interleaving with the original scalar loop. + /// + /// The number of samples in each component plane. + private static void ValidatePackedInvertNormalizeInterleave4(int length) + { + const float maximumValue = 255F; + const float scale = 1F / maximumValue; + float[] x = CreateSamples(length, 1); + float[] y = CreateSamples(length, 2); + float[] z = CreateSamples(length, 3); + float[] w = CreateSamples(length, 4); + float[] expected = new float[length * 4]; + float[] actual = new float[length * 4]; + + for (int i = 0; i < length; i++) + { + int packedOffset = i * 4; + expected[packedOffset] = (maximumValue - x[i]) * scale; + expected[packedOffset + 1] = (maximumValue - y[i]) * scale; + expected[packedOffset + 2] = (maximumValue - z[i]) * scale; + expected[packedOffset + 3] = (maximumValue - w[i]) * scale; + } + + JpegColorConverterBase.PackedInvertNormalizeInterleave4(x, y, z, w, actual, maximumValue); + + Assert.Equal(expected, actual); + } + + /// + /// Creates deterministic, non-integral sample values that expose lane-order and arithmetic mistakes. + /// + /// The number of samples to create. + /// The one-based component number used to distinguish each plane. + /// The generated sample values. + private static float[] CreateSamples(int length, int component) + { + float[] samples = new float[length]; + + for (int i = 0; i < samples.Length; i++) + { + // The relatively prime multipliers produce a distinct sequence for each plane + // while keeping every value inside the eight-bit JPEG sample domain. + samples[i] = (((i * 37) + (component * 53)) % 251) + (component * 0.125F); + } + + return samples; + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index f147e41325..e7e6d20334 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -30,7 +30,7 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) } using Image image = provider.GetImage(JpegDecoder.Instance); - image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr); + image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr, appendPixelTypeToFileName: false); provider.Utility.TestName = DecodeBaselineJpegOutputName; image.CompareToReferenceOutput( diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 85fad3f826..a4a71c6731 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg; public partial class JpegDecoderTests { public static string[] BaselineTestJpegs = - { + [ TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Ycck, @@ -41,11 +41,11 @@ public partial class JpegDecoderTests TestImages.Jpeg.Baseline.Testorig12bit, // Grayscale jpeg with 2x2 sampling factors (not a usual thing to encounter in the wild) - TestImages.Jpeg.Baseline.GrayscaleSampling2x2, - }; + TestImages.Jpeg.Baseline.GrayscaleSampling2x2 + ]; public static string[] ProgressiveTestJpegs = - { + [ TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, TestImages.Jpeg.Progressive.Festzug, @@ -61,20 +61,20 @@ public partial class JpegDecoderTests TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C - }; + ]; public static string[] UnsupportedTestJpegs = - { + [ // Invalid componentCount value (2 or > 4) TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, TestImages.Jpeg.Issues.MalformedUnsupportedComponentCount, // Lossless jpeg TestImages.Jpeg.Baseline.Lossless - }; + ]; public static string[] UnrecoverableTestJpegs = - { + [ TestImages.Jpeg.Issues.CriticalEOF214, TestImages.Jpeg.Issues.Fuzz.NullReferenceException797, TestImages.Jpeg.Issues.Fuzz.AccessViolationException798, @@ -98,8 +98,8 @@ public partial class JpegDecoderTests TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, - TestImages.Jpeg.Issues.Fuzz.NullReferenceException2085, - }; + TestImages.Jpeg.Issues.Fuzz.NullReferenceException2085 + ]; private static readonly Dictionary CustomToleranceValues = new() { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs index 6cf6250cb1..b780b14fb1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs @@ -19,7 +19,7 @@ public partial class JpegDecoderTests [InlineData(4, JpegConstants.Adobe.ColorTransformYcck, JpegColorSpace.Ycck)] internal void DeduceJpegColorSpaceAdobeMarker_ShouldReturnValidColorSpace(byte componentCount, byte adobeFlag, JpegColorSpace expectedColorSpace) { - byte[] adobeMarkerPayload = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, adobeFlag }; + byte[] adobeMarkerPayload = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, adobeFlag]; ProfileResolver.AdobeMarker.CopyTo(adobeMarkerPayload); _ = AdobeMarker.TryParse(adobeMarkerPayload, out AdobeMarker adobeMarker); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index e68dd1f879..11c7dc86d1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -146,14 +146,14 @@ public async Task Decode_VerifyQualityAsync(string imagePath, int quality) } [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); @@ -163,12 +163,12 @@ public void Identify_DetectsCorrectColorType(string imagePath, JpegEncodingColor } [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); @@ -426,6 +426,21 @@ public void EncodedStringTags_Read() 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.Comments.ElementAtOrDefault(0).ToString()); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + // https://github.com/SixLabors/ImageSharp/issues/2758 [Theory] [WithFile(TestImages.Jpeg.Issues.Issue2758, PixelTypes.L8)] @@ -446,9 +461,48 @@ public void Issue2758_DecodeWorks(TestImageProvider provider) // We want to test the encoder to ensure the determined values can be encoded but not by encoding // the full size image as it would be too slow. // We will crop the image to a smaller size and then encode it. - image.Mutate(x => x.Crop(new(0, 0, 100, 100))); + image.Mutate(x => x.Crop(new Rectangle(0, 0, 100, 100))); + + using MemoryStream ms = new(); + image.Save(ms, new JpegEncoder()); + } + + // https://github.com/SixLabors/ImageSharp/issues/2857 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2857, PixelTypes.Rgb24)] + public void Issue2857_SubSubIfds(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(JpegDecoder.Instance); + + Assert.Equal(5616, image.Width); + Assert.Equal(3744, image.Height); + + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + Assert.Equal(92, meta.LuminanceQuality); + Assert.Equal(93, meta.ChrominanceQuality); + + ExifProfile exifProfile = image.Metadata.ExifProfile; + Assert.NotNull(exifProfile); using MemoryStream ms = new(); + bool hasThumbnail = exifProfile.TryCreateThumbnail(out _); + Assert.False(hasThumbnail); + + Assert.Equal("BilderBox - Erwin Wodicka / wodicka@aon.at", exifProfile.GetValue(ExifTag.Copyright).Value); + Assert.Equal("Adobe Photoshop CS3 Windows", exifProfile.GetValue(ExifTag.Software).Value); + + Assert.Equal("Carers; seniors; caregiver; senior care; retirement home; hands; old; elderly; elderly caregiver; elder care; elderly care; geriatric care; nursing home; age; old age care; outpatient; needy; health care; home nurse; home care; sick; retirement; medical; mobile; the elderly; nursing department; nursing treatment; nursing; care services; nursing services; nursing care; nursing allowance; nursing homes; home nursing; care category; nursing class; care; nursing shortage; nursing patient care staff\0", exifProfile.GetValue(ExifTag.XPKeywords).Value); + + Assert.Equal( + new EncodedString(EncodedString.CharacterCode.ASCII, "StockSubmitter|Miscellaneous||Miscellaneous$|00|0000330000000110000000000000000|22$@NA_1005010.460@145$$@Miscellaneous.Miscellaneous$$@$@26$$@$@$@$@205$@$@$@$@$@$@$@$@$@43$@$@$@$$@Miscellaneous.Miscellaneous$$@90$$@22$@$@$@$@$@$@$|||"), + exifProfile.GetValue(ExifTag.UserComment).Value); + + // the profile contains 4 duplicated UserComment + Assert.Equal(1, exifProfile.Values.Count(t => t.Tag == ExifTag.UserComment)); + + image.Mutate(x => x.Crop(new Rectangle(0, 0, 100, 100))); + image.Save(ms, new JpegEncoder()); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index a5472e1aef..fff1e085b3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -21,7 +21,7 @@ public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(JpegDecoder.Instance); - image.DebugSave(provider); + image.DebugSave(provider, appendPixelTypeToFileName: false); provider.Utility.TestName = DecodeProgressiveJpegOutputName; image.CompareToReferenceOutput( diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 97be5d8383..2f84a1a7b7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -20,7 +19,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; @@ -45,8 +44,8 @@ private static ImageComparer GetImageComparer(TestImageProvider private static bool SkipTest(ITestImageProvider provider) { string[] largeImagesToSkipOn32Bit = - { - TestImages.Jpeg.Baseline.Jpeg420Exif, + [ + TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, TestImages.Jpeg.Issues.BadZigZagProgressive385, TestImages.Jpeg.Issues.NoEoiProgressive517, @@ -54,7 +53,7 @@ private static bool SkipTest(ITestImageProvider provider) TestImages.Jpeg.Issues.InvalidEOI695, TestImages.Jpeg.Issues.ExifResizeOutOfRange696, TestImages.Jpeg.Issues.ExifGetString750Transform - }; + ]; return !TestEnvironment.Is64BitProcess && largeImagesToSkipOn32Bit.Contains(provider.SourceFileOrDescription); } @@ -117,7 +116,7 @@ public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + DecoderOptions options = new() { TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(JpegDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; @@ -137,7 +136,7 @@ public void JpegDecoder_Decode_Resize_Bicubic(TestImageProvider { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 }, + TargetSize = new Size { Width = 150, Height = 150 }, Sampler = KnownResamplers.Bicubic }; using Image image = provider.GetImage(JpegDecoder.Instance, options); @@ -157,7 +156,7 @@ public void JpegDecoder_Decode_Resize_Bicubic(TestImageProvider public void JpegDecoder_Decode_Specialized_IDCT_Resize(TestImageProvider provider) where TPixel : unmanaged, IPixel { - DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + DecoderOptions options = new() { TargetSize = new Size { Width = 150, Height = 150 } }; JpegDecoderOptions specializedOptions = new() { GeneralOptions = options, @@ -181,7 +180,7 @@ public void JpegDecoder_Decode_Specialized_IDCT_Resize(TestImageProvider public void JpegDecoder_Decode_Specialized_Scale_Resize(TestImageProvider provider) where TPixel : unmanaged, IPixel { - DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + DecoderOptions options = new() { TargetSize = new Size { Width = 150, Height = 150 } }; JpegDecoderOptions specializedOptions = new() { GeneralOptions = options, @@ -205,7 +204,7 @@ public void JpegDecoder_Decode_Specialized_Scale_Resize(TestImageProvide public void JpegDecoder_Decode_Specialized_Combined_Resize(TestImageProvider provider) where TPixel : unmanaged, IPixel { - DecoderOptions options = new() { TargetSize = new() { Width = 150, Height = 150 } }; + DecoderOptions options = new() { TargetSize = new Size { Width = 150, Height = 150 } }; JpegDecoderOptions specializedOptions = new() { GeneralOptions = options, @@ -365,4 +364,164 @@ public void Issue2638_DecodeWorks(TestImageProvider provider) image.DebugSave(provider); image.CompareToOriginal(provider); } + + [Theory] + [WithFile(TestImages.Jpeg.ICC.CMYK, PixelTypes.Rgba32)] + public void Decode_CMYK_ICC_Jpeg(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + JpegDecoderOptions options = new() + { + GeneralOptions = new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert } + }; + + using Image image = provider.GetImage(JpegDecoder.Instance, options); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + + [Theory] + [WithFile(TestImages.Jpeg.ICC.YCCK, PixelTypes.Rgba32)] + public void Decode_YCCK_ICC_Jpeg(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + JpegDecoderOptions options = new() + { + GeneralOptions = new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert } + }; + + using Image image = provider.GetImage(JpegDecoder.Instance, options); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + + [Theory] + [WithFile(TestImages.Jpeg.ICC.SRgb, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.AdobeRgb, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.ColorMatch, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.ProPhoto, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.WideRGB, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.AppleRGB, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.SRgbGray, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.Perceptual, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.ICC.PerceptualcLUTOnly, PixelTypes.Rgba32)] + public void Decode_RGB_ICC_Jpeg(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + JpegDecoderOptions options = new() + { + GeneralOptions = new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert } + }; + + using Image image = provider.GetImage(JpegDecoder.Instance, options); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + + [Theory] + [WithFile(TestImages.Jpeg.ICC.Issue3064, PixelTypes.Rgba32)] + public void Decode_RGB_ICC_Jpeg_Issue3064(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + JpegDecoderOptions options = new() + { + GeneralOptions = new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert } + }; + + using Image image = provider.GetImage(JpegDecoder.Instance, options); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2948, PixelTypes.Rgb24)] + public void Issue2948_No_SOS_Decode_Throws_InvalidImageContentException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + => Assert.Throws(() => + { + using Image image = provider.GetImage(JpegDecoder.Instance); + }); + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [InlineData(TestImages.Jpeg.Issues.Issue2948)] + public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) + => Assert.Throws(() => _ = Image.Identify(TestFile.Create(imagePath).Bytes)); + + [Fact] + public void Issue_3071_Decode_TruncatedJpeg_Throws_InvalidImageContentException() + => Assert.Throws(() => + { + // SOI marker (FF D8) + garbage bytes — only 11 bytes + byte[] data = [0xFF, 0xD8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; + using Image image = Image.Load(data); + }); + + // https://github.com/SixLabors/ImageSharp/issues/3118 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue3118, PixelTypes.Rgb24)] + public void Issue3118_Multiple_SOF_WithSOS_DoesNotThrow(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(JpegDecoder.Instance); + image.DebugSave(provider); + } + + [Fact] + public void Identify_MalformedApp13Segment_IgnoresNonCriticalErrorsByDefault() + { + ImageInfo info = Image.Identify(CreateJpegWithMalformedApp13Segment()); + Assert.True(info.Width > 0); + Assert.True(info.Height > 0); + } + + [Fact] + public void Decode_MalformedApp13Segment_IgnoresNonCriticalErrorsByDefault() + { + using Image image = Image.Load(CreateJpegWithMalformedApp13Segment()); + Assert.True(image.Width > 0); + Assert.True(image.Height > 0); + } + + [Fact] + public void Identify_MalformedApp13Segment_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => Image.Identify(options, CreateJpegWithMalformedApp13Segment())); + } + + [Fact] + public void Decode_MalformedApp13Segment_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => + { + using Image image = Image.Load(options, CreateJpegWithMalformedApp13Segment()); + }); + } + + private static byte[] CreateJpegWithMalformedApp13Segment() + { + byte[] source = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).Bytes; + + // This APP13 segment starts with the valid "Photoshop 3.0\0" identifier, but the remaining + // payload does not begin with the required "8BIM" image resource block signature. + byte[] malformedApp13 = + [ + 0xFF, 0xED, + 0x00, 0x1D, + (byte)'P', (byte)'h', (byte)'o', (byte)'t', (byte)'o', (byte)'s', (byte)'h', (byte)'o', (byte)'p', (byte)' ', (byte)'3', (byte)'.', (byte)'0', 0x00, + (byte)'B', (byte)'a', (byte)'d', (byte)'R', (byte)'e', (byte)'s', (byte)'o', (byte)'u', (byte)'r', (byte)'c', (byte)'e', (byte)'!', (byte)'!' + ]; + + byte[] payload = new byte[source.Length + malformedApp13.Length]; + payload[0] = source[0]; + payload[1] = source[1]; + + Buffer.BlockCopy(malformedApp13, 0, payload, 2, malformedApp13.Length); + Buffer.BlockCopy(source, 2, payload, 2 + malformedApp13.Length, source.Length - 2); + + return payload; + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs index 2b721b9b51..99322687cc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Formats.Jpg; @@ -32,19 +33,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 +56,17 @@ public void Encode_PreservesIptcProfile() 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 +77,16 @@ public void Encode_PreservesExifProfile() public void Encode_PreservesIccProfile() { // arrange - using var input = new Image(1, 1); - input.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.Profile_Random_Array); + using Image input = new(1, 1); + input.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.ProfileRandomArray); // 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 +100,10 @@ public void Encode_WithValidExifProfile_DoesNotThrowException(TestImageP { 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,64 +113,119 @@ public void Encode_WithValidExifProfile_DoesNotThrowException(TestImageP [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] [MemberData(nameof(QualityFiles))] public void Encode_PreservesQuality(string imagePath, int quality) { - 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)) - { - JpegMetadata meta = output.Metadata.GetJpegMetadata(); - Assert.Equal(quality, meta.Quality); - } - } - } + 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); + JpegMetadata meta = output.Metadata.GetJpegMetadata(); + Assert.Equal(quality, meta.Quality); + } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2067_CommentMarker, PixelTypes.Rgba32)] + public void Encode_PreservesComments(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // arrange + using Image input = provider.GetImage(JpegDecoder.Instance); + using MemoryStream memStream = new(); + + // act + 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(1, actual.Comments.Count); + Assert.Equal("TEST COMMENT", actual.Comments[0].ToString()); + } + + [Fact] + public void Encode_SavesMultipleComments() + { + // arrange + using Image input = new(1, 1); + JpegMetadata meta = input.Metadata.GetJpegMetadata(); + using MemoryStream memStream = new(); + + // act + meta.Comments.Add(JpegComData.FromString("First comment")); + meta.Comments.Add(JpegComData.FromString("Second Comment")); + 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(meta.Comments[0].ToString(), actual.Comments[0].ToString()); + Assert.Equal(meta.Comments[1].ToString(), actual.Comments[1].ToString()); + } + + [Fact] + public void Encode_SaveTooLongComment() + { + // arrange + string longString = new('c', 65534); + using Image input = new(1, 1); + JpegMetadata meta = input.Metadata.GetJpegMetadata(); + using MemoryStream memStream = new(); + + // 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[0].ToString()); + Assert.Equal("c", actual.Comments[1].ToString()); } [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 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); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 5842c8e1a0..5fae85cc85 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.0164f / 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 void EncodeBaseline_Interleaved(TestImageProvider provide [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 void EncodeBaseline_NonInterleavedMode(TestImageProvider [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 void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider(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 void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestI [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,67 @@ public void EncodeBaseline_WorksWithDiscontiguousBuffers(TestImageProvid } [Theory] - [InlineData(JpegEncodingColor.YCbCrRatio420)] - [InlineData(JpegEncodingColor.YCbCrRatio444)] - public async Task Encode_IsCancellable(JpegEncodingColor colorType) + [WithFile(TestImages.Png.CalliphoraPartial, nameof(NonSubsampledEncodingSetups), PixelTypes.Rgb24)] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(SubsampledEncodingSetups), PixelTypes.Rgb24)] + [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 EncodeProgressive_DefaultNumberOfScans(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + JpegEncoder encoder = new() + { + Quality = quality, + ColorType = colorType, + Progressive = true + }; + string info = $"{colorType}-Q{quality}"; + + ImageComparer comparer = new TolerantImageComparer(tolerance); + + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg"); + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(NonSubsampledEncodingSetups), PixelTypes.Rgb24)] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(SubsampledEncodingSetups), PixelTypes.Rgb24)] + [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 EncodeProgressive_CustomNumberOfScans(TestImageProvider provider, JpegColorType colorType, int quality, float tolerance) +where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + JpegEncoder encoder = new() + { + Quality = quality, + ColorType = colorType, + Progressive = true, + ProgressiveScans = 4, + RestartInterval = 7 + }; + string info = $"{colorType}-Q{quality}"; + + using MemoryStream ms = new(); + image.SaveAsJpeg(ms, encoder); + ms.Position = 0; + + // TEMP: Save decoded output as PNG so we can do a pixel compare. + using Image image2 = Image.Load(ms); + image2.DebugSave(provider, testOutputDetails: info, extension: "png"); + + ImageComparer comparer = new TolerantImageComparer(tolerance); + image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg"); + } + + [Theory] + [InlineData(JpegColorType.YCbCrRatio420)] + [InlineData(JpegColorType.YCbCrRatio444)] + public async Task Encode_IsCancellable(JpegColorType colorType) { CancellationTokenSource cts = new(); using PausedStream pausedStream = new(new MemoryStream()); @@ -200,14 +258,14 @@ public static void Issue2595(TestImageProvider provider) using Image image = provider.GetImage(); image.Mutate(x => x.Crop(132, 1606)); - int[] quality = new int[] { 100, 50 }; - JpegEncodingColor[] colors = new[] { JpegEncodingColor.YCbCrRatio444, JpegEncodingColor.YCbCrRatio420 }; + int[] quality = [100, 50]; + JpegColorType[] colors = [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 +274,7 @@ public static void Issue2595(TestImageProvider provider) /// /// 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 +282,10 @@ private static ImageComparer GetComparer(int quality, JpegEncodingColor? colorTy { 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 +294,15 @@ private static ImageComparer GetComparer(int quality, JpegEncodingColor? colorTy 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/JpegFileMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs index df74d266cb..62362ec801 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs @@ -14,7 +14,7 @@ public void MarkerConstructorAssignsProperties() { const byte app1 = JpegConstants.Markers.APP1; const int position = 5; - var marker = new JpegFileMarker(app1, position); + JpegFileMarker marker = new(app1, position); Assert.Equal(app1, marker.Marker); Assert.Equal(position, marker.Position); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 05f22667dc..37be0f8f56 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; @@ -11,10 +12,10 @@ public class JpegMetadataTests [Fact] public void CloneIsDeep() { - var meta = new JpegMetadata { ColorType = JpegEncodingColor.Luminance }; - var clone = (JpegMetadata)meta.DeepClone(); + JpegMetadata meta = new() { ColorType = JpegColorType.Luminance }; + JpegMetadata clone = (JpegMetadata)meta.DeepClone(); - clone.ColorType = JpegEncodingColor.YCbCrRatio420; + clone.ColorType = JpegColorType.YCbCrRatio420; Assert.False(meta.ColorType.Equals(clone.ColorType)); } @@ -22,7 +23,7 @@ public void CloneIsDeep() [Fact] public void Quality_DefaultQuality() { - var meta = new JpegMetadata(); + JpegMetadata meta = new(); Assert.Equal(meta.Quality, ImageSharp.Formats.Jpeg.Components.Quantization.DefaultQualityFactor); } @@ -32,7 +33,7 @@ public void Quality_LuminanceOnlyQuality() { int quality = 50; - var meta = new JpegMetadata { LuminanceQuality = quality }; + JpegMetadata meta = new() { LuminanceQuality = quality }; Assert.Equal(meta.Quality, quality); } @@ -42,7 +43,7 @@ public void Quality_BothComponentsQuality() { int quality = 50; - var meta = new JpegMetadata { LuminanceQuality = quality, ChrominanceQuality = quality }; + JpegMetadata meta = new() { LuminanceQuality = quality, ChrominanceQuality = quality }; Assert.Equal(meta.Quality, quality); } @@ -53,8 +54,29 @@ public void Quality_ReturnsMaxQuality() int qualityLuma = 50; int qualityChroma = 30; - var meta = new JpegMetadata { LuminanceQuality = qualityLuma, ChrominanceQuality = qualityChroma }; + JpegMetadata meta = new() { LuminanceQuality = qualityLuma, ChrominanceQuality = qualityChroma }; Assert.Equal(meta.Quality, qualityLuma); } + + [Fact] + public void Comment_EmptyComment() + { + JpegMetadata meta = new(); + + Assert.True(Array.Empty().SequenceEqual(meta.Comments)); + } + + [Fact] + public void Comment_OnlyComment() + { + string comment = "test comment"; + Collection expectedCollection = new() { comment }; + + JpegMetadata meta = new(); + meta.Comments.Add(JpegComData.FromString(comment)); + + Assert.Equal(1, meta.Comments.Count); + Assert.True(expectedCollection.FirstOrDefault() == meta.Comments.FirstOrDefault().ToString()); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs index b202fd9ec3..b6a59fa93e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -27,7 +27,7 @@ public ParseStreamTests(ITestOutputHelper output) [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue) { - var expectedColorSpace = (JpegColorSpace)expectedColorSpaceValue; + JpegColorSpace expectedColorSpace = (JpegColorSpace)expectedColorSpaceValue; using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile, metaDataOnly: true)) { @@ -47,7 +47,7 @@ public void ComponentScalingIsCorrect_1ChannelJpeg() Assert.Equal(expectedSizeInBlocks, decoder.Frame.McuSize); - var uniform1 = new Size(1, 1); + Size uniform1 = new(1, 1); IJpegComponent c0 = decoder.Components[0]; VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); } @@ -62,7 +62,7 @@ public void ComponentScalingIsCorrect_1ChannelJpeg() [InlineData(TestImages.Jpeg.Baseline.Cmyk)] public void PrintComponentData(string imageFile) { - var sb = new StringBuilder(); + StringBuilder sb = new(); using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { @@ -98,8 +98,8 @@ public void ComponentScalingIsCorrect_MultiChannelJpeg( object expectedLumaFactors, object expectedChromaFactors) { - var fLuma = (Size)expectedLumaFactors; - var fChroma = (Size)expectedChromaFactors; + Size fLuma = (Size)expectedLumaFactors; + Size fChroma = (Size)expectedChromaFactors; using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { @@ -110,7 +110,7 @@ public void ComponentScalingIsCorrect_MultiChannelJpeg( IJpegComponent c1 = decoder.Components[1]; IJpegComponent c2 = decoder.Components[2]; - var uniform1 = new Size(1, 1); + Size uniform1 = new(1, 1); Size expectedLumaSizeInBlocks = decoder.Frame.McuSize.MultiplyBy(fLuma); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs index f5d7c159ba..2fcd953ae0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -53,7 +53,7 @@ public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) { float[] sourceArray = Create8x8RandomFloatData(-range, range, seed); - var source = Block8x8F.Load(sourceArray); + Block8x8F source = Block8x8F.Load(sourceArray); Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 805ee586a8..1610df6eb4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using Xunit.Abstractions; @@ -21,19 +22,19 @@ public class SpectralJpegTests private ITestOutputHelper Output { get; } public static readonly string[] BaselineTestJpegs = - { - TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, + [ + TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK - }; + ]; public static readonly string[] ProgressiveTestJpegs = - { - TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + [ + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, - TestImages.Jpeg.Progressive.Bad.ExifUndefType, - }; + TestImages.Jpeg.Progressive.Bad.ExifUndefType + ]; public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); @@ -46,13 +47,13 @@ public void Decoder_ParseStream_SaveSpectralResult(TestImageProvider(); - var scanDecoder = new HuffmanScanDecoder(bufferedStream, debugConverter, cancellationToken: default); + DebugSpectralConverter debugConverter = new(); + HuffmanScanDecoder scanDecoder = new(bufferedStream, debugConverter, cancellationToken: default); // This would parse entire image decoder.ParseStream(bufferedStream, debugConverter, cancellationToken: default); @@ -76,12 +77,12 @@ public void VerifySpectralCorrectness(TestImageProvider provider byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; JpegDecoderOptions options = new(); - using var decoder = new JpegDecoderCore(options); - using var ms = new MemoryStream(sourceBytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); + using JpegDecoderCore decoder = new(options); + using MemoryStream ms = new(sourceBytes); + using BufferedReadStream bufferedStream = new(Configuration.Default, ms); // internal scan decoder which we substitute to assert spectral correctness - var debugConverter = new DebugSpectralConverter(); + DebugSpectralConverter debugConverter = new(); // This would parse entire image decoder.ParseStream(bufferedStream, debugConverter, cancellationToken: default); @@ -118,7 +119,7 @@ private void VerifySpectralCorrectnessImpl( this.Output.WriteLine($"Component{i}: [total: {total} | average: {average}]"); averageDifference += average; totalDifference += total; - Size s = libJpegComponent.SpectralBlocks.Size(); + Size s = libJpegComponent.SpectralBlocks.Size; tolerance += s.Width * s.Height; } @@ -164,7 +165,7 @@ public LibJpegTools.SpectralData SpectralData } } - public override void ConvertStrideBaseline() + public override void ConvertStrideBaseline(IccProfile iccProfile) { // This would be called only for baseline non-interleaved images // We must copy spectral strides here @@ -197,7 +198,7 @@ public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) public override void PrepareForDecoding() { - var spectralComponents = new LibJpegTools.ComponentData[this.frame.ComponentCount]; + LibJpegTools.ComponentData[] spectralComponents = new LibJpegTools.ComponentData[this.frame.ComponentCount]; for (int i = 0; i < spectralComponents.Length; i++) { JpegComponent component = this.frame.Components[i]; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs index 25929182fb..13300ee804 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs @@ -15,12 +15,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg; public class SpectralToPixelConversionTests { public static readonly string[] BaselineTestJpegs = - { + [ TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK - }; + ]; public SpectralToPixelConversionTests(ITestOutputHelper output) => this.Output = output; @@ -33,14 +33,14 @@ public void Decoder_PixelBufferComparison(TestImageProvider prov { // Stream byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; - using var ms = new MemoryStream(sourceBytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, ms); + using MemoryStream ms = new(sourceBytes); + using BufferedReadStream bufferedStream = new(Configuration.Default, ms); // Decoding JpegDecoderOptions options = new(); - using var converter = new SpectralConverter(Configuration.Default); - using var decoder = new JpegDecoderCore(options); - var scanDecoder = new HuffmanScanDecoder(bufferedStream, converter, cancellationToken: default); + using SpectralConverter converter = new(Configuration.Default); + using JpegDecoderCore decoder = new(options); + HuffmanScanDecoder scanDecoder = new(bufferedStream, converter, cancellationToken: default); decoder.ParseStream(bufferedStream, converter, cancellationToken: default); // Test metadata @@ -48,7 +48,7 @@ public void Decoder_PixelBufferComparison(TestImageProvider prov provider.Utility.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; // Comparison - using var image = new Image(Configuration.Default, converter.GetPixelBuffer(CancellationToken.None), new ImageMetadata()); + using Image image = new(Configuration.Default, converter.GetPixelBuffer(null, CancellationToken.None), new ImageMetadata()); using Image referenceImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false); ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index a3fbe4018e..33e95c5aa0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -72,7 +72,7 @@ public static short[] Create8x8ShortData() // ReSharper disable once InconsistentNaming public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) { - var rnd = new Random(seed); + Random rnd = new(seed); int[] result = new int[64]; for (int i = 0; i < 8; i++) { @@ -87,7 +87,7 @@ public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42, int xBorder = 8, int yBorder = 8) { - var rnd = new Random(seed); + Random rnd = new(seed); float[] result = new float[64]; for (int y = 0; y < yBorder; y++) { @@ -112,7 +112,7 @@ internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, internal void Print8x8Data(Span data) { - var bld = new StringBuilder(); + StringBuilder bld = new(); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) @@ -135,7 +135,7 @@ internal void PrintLinearData(Span data, int count = -1) count = data.Length; } - var sb = new StringBuilder(); + StringBuilder sb = new(); for (int i = 0; i < count; i++) { sb.Append($"{data[i],3} "); @@ -158,7 +158,7 @@ internal void CompareBlocks(Block8x8F a, Block8x8F b, float tolerance) internal void CompareBlocks(Span a, Span b, float tolerance) { - var comparer = new ApproximateFloatComparer(tolerance); + ApproximateFloatComparer comparer = new(tolerance); double totalDifference = 0.0; bool failed = false; @@ -192,7 +192,7 @@ internal static bool CompareBlocks(Block8x8F a, Block8x8F b, float tolerance, ou internal static bool CompareBlocks(Span a, Span b, float tolerance, out float diff) { - var comparer = new ApproximateFloatComparer(tolerance); + ApproximateFloatComparer comparer = new(tolerance); bool failed = false; diff = 0; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 80365b4724..975378b5f8 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 @@ public ComponentData(int widthInBlocks, int heightInBlocks, int index) public int QuantizationTableIndex => throw new NotSupportedException(); - public Buffer2D SpectralBlocks { get; private set; } + public Buffer2D SpectralBlocks { get; } public short MinVal { get; private set; } = short.MaxValue; @@ -60,7 +60,7 @@ public ComponentData(int widthInBlocks, int heightInBlocks, int index) internal void MakeBlock(Block8x8 block, int y, int x) { - block.TransposeInplace(); + block.TransposeInPlace(); this.MakeBlock(block.ToArray(), y, x); } @@ -102,7 +102,7 @@ public void LoadSpectral(IJpegComponent c) public static ComponentData Load(JpegComponent c, int index) { - var result = new ComponentData( + ComponentData result = new( c.WidthInBlocks, c.HeightInBlocks, index); @@ -113,7 +113,7 @@ public static ComponentData Load(JpegComponent c, int index) 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 void WriteToImage(int bx, int by, Image image) { 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 @@ public override bool Equals(object obj) 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 a4bb6b7bfc..9eec547a36 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 @@ public Image TryCreateRGBSpectralImage() 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 @@ public Image TryCreateRGBSpectralImage() 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 void WriteToImage(int bx, int by, Image image) 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 @@ public bool Equals(SpectralData other) 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 @@ public bool Equals(SpectralData other) 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 @@ public override int GetHashCode() 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/Jpg/Utils/LibJpegTools.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs index bd1df33772..8884591128 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs @@ -65,7 +65,7 @@ public static void RunDumpJpegCoeffsTool(string sourceFile, string destFile) } string args = $@"""{sourceFile}"" ""{destFile}"""; - var process = new Process + Process process = new() { StartInfo = { @@ -95,21 +95,21 @@ public static SpectralData ExtractSpectralData(string inputFile) { RunDumpJpegCoeffsTool(testFile.FullPath, coeffFileFullPath); - using (var dumpStream = new FileStream(coeffFileFullPath, FileMode.Open)) - using (var rdr = new BinaryReader(dumpStream)) + using (FileStream dumpStream = new(coeffFileFullPath, FileMode.Open)) + using (BinaryReader rdr = new(dumpStream)) { int componentCount = rdr.ReadInt16(); - var result = new ComponentData[componentCount]; + ComponentData[] result = new ComponentData[componentCount]; for (int i = 0; i < componentCount; i++) { int widthInBlocks = rdr.ReadInt16(); int heightInBlocks = rdr.ReadInt16(); - var resultComponent = new ComponentData(widthInBlocks, heightInBlocks, i); + ComponentData resultComponent = new(widthInBlocks, heightInBlocks, i); result[i] = resultComponent; } - var buffer = new byte[64 * sizeof(short)]; + byte[] buffer = new byte[64 * sizeof(short)]; for (int i = 0; i < result.Length; i++) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs index b6e7b29a62..2d4db15dfc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs @@ -30,7 +30,7 @@ public static Block8x8 TransformIDCT(ref Block8x8 block) public static void TransformIDCTInplace(Span span) { - var temp = default(Block8x8); + Block8x8 temp = default(Block8x8); temp.LoadFrom(span); Block8x8 result = TransformIDCT(ref temp); result.CopyTo(span); @@ -45,7 +45,7 @@ public static Block8x8 TransformFDCT(ref Block8x8 block) public static void TransformFDCTInplace(Span span) { - var temp = default(Block8x8); + Block8x8 temp = default(Block8x8); temp.LoadFrom(span); Block8x8 result = TransformFDCT(ref temp); result.CopyTo(span); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index 0d5f3114d1..7c7f47f946 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -208,8 +208,8 @@ public static void FDCT2D8x4_32f(Span s, Span d) /*y[0] = c0 + c1; y[4] = c0 - c1;*/ - var w0 = new Vector4(0.541196f); - var w1 = new Vector4(1.306563f); + Vector4 w0 = new(0.541196f); + Vector4 w1 = new(1.306563f); _mm_store_ps(d, 16, (w0 * c2) + (w1 * c3)); @@ -242,7 +242,7 @@ public static void FDCT2D8x4_32f(Span s, Span d) _mm_store_ps(d, 40, c3 - c1); // y[5] = c3 - c1; y[3] = c0 - c2; - var invsqrt2 = new Vector4(0.707107f); + Vector4 invsqrt2 = new(0.707107f); c0 = (c0 + c2) * invsqrt2; c3 = (c3 + c1) * invsqrt2; @@ -272,7 +272,7 @@ public static void FDCT8x8_llm_sse(Span s, Span d, Span tem FDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - var c = new Vector4(0.1250f); + Vector4 c = new(0.1250f); #pragma warning disable SA1107 // Code should not contain multiple statements on one line _mm_store_ps(d, 0, _mm_load_ps(d, 0) * c); d = d.Slice(4); // 0 @@ -318,29 +318,29 @@ private static void _mm_store_ps(Span dest, int offset, Vector4 src) // Accurate variants of constants from: // https://github.com/mozilla/mozjpeg/blob/master/simd/jfdctint-altivec.c #pragma warning disable SA1309 // Field names should not begin with underscore - private static readonly Vector4 _1_175876 = new Vector4(1.175875602f); + private static readonly Vector4 _1_175876 = new(1.175875602f); - private static readonly Vector4 _1_961571 = new Vector4(-1.961570560f); + private static readonly Vector4 _1_961571 = new(-1.961570560f); - private static readonly Vector4 _0_390181 = new Vector4(-0.390180644f); + private static readonly Vector4 _0_390181 = new(-0.390180644f); - private static readonly Vector4 _0_899976 = new Vector4(-0.899976223f); + private static readonly Vector4 _0_899976 = new(-0.899976223f); - private static readonly Vector4 _2_562915 = new Vector4(-2.562915447f); + private static readonly Vector4 _2_562915 = new(-2.562915447f); - private static readonly Vector4 _0_298631 = new Vector4(0.298631336f); + private static readonly Vector4 _0_298631 = new(0.298631336f); - private static readonly Vector4 _2_053120 = new Vector4(2.053119869f); + private static readonly Vector4 _2_053120 = new(2.053119869f); - private static readonly Vector4 _3_072711 = new Vector4(3.072711026f); + private static readonly Vector4 _3_072711 = new(3.072711026f); - private static readonly Vector4 _1_501321 = new Vector4(1.501321110f); + private static readonly Vector4 _1_501321 = new(1.501321110f); - private static readonly Vector4 _0_541196 = new Vector4(0.541196100f); + private static readonly Vector4 _0_541196 = new(0.541196100f); - private static readonly Vector4 _1_847759 = new Vector4(-1.847759065f); + private static readonly Vector4 _1_847759 = new(-1.847759065f); - private static readonly Vector4 _0_765367 = new Vector4(0.765366865f); + private static readonly Vector4 _0_765367 = new(0.765366865f); #pragma warning restore SA1309 // Field names should not begin with underscore /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs index 95ab076b0b..10019c609e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs @@ -71,10 +71,10 @@ public static class StandardIntegerDCT public static Block8x8 Subtract128_TransformFDCT_Upscale8(ref Block8x8 block) { - var temp = new int[Block8x8.Size]; + int[] temp = new int[Block8x8.Size]; block.CopyTo(temp); Subtract128_TransformFDCT_Upscale8_Inplace(temp); - var result = default(Block8x8); + Block8x8 result = default(Block8x8); result.LoadFrom(temp); return result; } @@ -82,10 +82,10 @@ public static Block8x8 Subtract128_TransformFDCT_Upscale8(ref Block8x8 block) // [Obsolete("Looks like this method produces really bad results for bigger values!")] public static Block8x8 TransformIDCT(ref Block8x8 block) { - var temp = new int[Block8x8.Size]; + int[] temp = new int[Block8x8.Size]; block.CopyTo(temp); TransformIDCTInplace(temp); - var result = default(Block8x8); + Block8x8 result = default(Block8x8); result.LoadFrom(temp); return result; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs index 240a338f9b..d7c2c0bbbb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs @@ -74,7 +74,7 @@ public static void LoadFrom(this Span data, ref Vector4 v) /// A new with float values public static float[] ConvertAllToFloat(this int[] src) { - var result = new float[src.Length]; + float[] result = new float[src.Length]; for (int i = 0; i < src.Length; i++) { result[i] = src[i]; @@ -91,7 +91,7 @@ public static float[] ConvertAllToFloat(this int[] src) /// A new instance of public static Span AddScalarToAllValues(this Span src, float scalar) { - var result = new float[src.Length]; + float[] result = new float[src.Length]; for (int i = 0; i < src.Length; i++) { result[i] = src[i] + scalar; @@ -108,7 +108,7 @@ public static Span AddScalarToAllValues(this Span src, float scala /// A new instance of public static Span AddScalarToAllValues(this Span src, int scalar) { - var result = new int[src.Length]; + int[] result = new int[src.Length]; for (int i = 0; i < src.Length; i++) { result[i] = src[i] + scalar; diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs index 11dd1cd58c..778ad75897 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using static SixLabors.ImageSharp.Tests.TestImages.Pbm; // ReSharper disable InconsistentNaming @@ -26,14 +27,15 @@ public class PbmDecoderTests [InlineData(RgbPlain, PbmColorType.Rgb, PbmComponentType.Byte)] [InlineData(RgbPlainMagick, PbmColorType.Rgb, PbmComponentType.Byte)] [InlineData(RgbBinary, PbmColorType.Rgb, PbmComponentType.Byte)] + [InlineData(RgbBinaryWide, PbmColorType.Rgb, PbmComponentType.Short)] public void ImageLoadCanDecode(string imagePath, PbmColorType expectedColorType, PbmComponentType expectedComponentType) { // Arrange - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); // Act - using var image = Image.Load(stream); + using Image image = Image.Load(stream); // Assert Assert.NotNull(image); @@ -53,11 +55,11 @@ public void ImageLoadCanDecode(string imagePath, PbmColorType expectedColorType, public void ImageLoadL8CanDecode(string imagePath) { // Arrange - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); // Act - using var image = Image.Load(stream); + using Image image = Image.Load(stream); // Assert Assert.NotNull(image); @@ -70,11 +72,11 @@ public void ImageLoadL8CanDecode(string imagePath) public void ImageLoadRgb24CanDecode(string imagePath) { // Arrange - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); // Act - using var image = Image.Load(stream); + using Image image = Image.Load(stream); // Assert Assert.NotNull(image); @@ -91,6 +93,7 @@ public void ImageLoadRgb24CanDecode(string imagePath) [WithFile(RgbPlain, PixelTypes.Rgb24, "ppm")] [WithFile(RgbPlainNormalized, PixelTypes.Rgb24, "ppm")] [WithFile(RgbBinary, PixelTypes.Rgb24, "ppm")] + [WithFile(RgbBinaryWide, PixelTypes.Rgb48, "ppm")] public void DecodeReferenceImage(TestImageProvider provider, string extension) where TPixel : unmanaged, IPixel { @@ -101,6 +104,65 @@ public void DecodeReferenceImage(TestImageProvider provider, str image.CompareToReferenceOutput(provider, grayscale: isGrayscale); } + [Theory] + [WithFile(GrayscaleBinaryWide, PixelTypes.Rgb48)] + [WithFile(RgbBinaryWide, PixelTypes.Rgb48)] + public void Decode_WideBinary_MatchesReferenceDecoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PbmDecoder.Instance); + image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder(PbmFormat.Instance)); + } + + [Fact] + public void Decode_WideBinaryGrayscale_SamplesAreBigEndian() + { + // Per the Netpbm specification, 16-bit samples store the most significant byte first. + byte[] header = Encoding.ASCII.GetBytes("P5\n2 1\n65535\n"); + byte[] samples = [0x80, 0x00, 0x00, 0x80]; + byte[] data = [.. header, .. samples]; + + using Image image = Image.Load(data); + + Assert.Equal(0x8000, image[0, 0].PackedValue); + Assert.Equal(0x0080, image[1, 0].PackedValue); + } + + [Fact] + public void Decode_WideBinaryRgb_SamplesAreBigEndian() + { + // Per the Netpbm specification, 16-bit samples store the most significant byte first. + byte[] header = Encoding.ASCII.GetBytes("P6\n1 1\n65535\n"); + byte[] samples = [0x81, 0xB5, 0x84, 0x91, 0x86, 0x71]; + byte[] data = [.. header, .. samples]; + + using Image image = Image.Load(data); + + Assert.Equal(new Rgb48(0x81B5, 0x8491, 0x8671), image[0, 0]); + } + + [Fact] + public void Decode_NonStandardByteMaxPixelValue_UpscalesToFullRange() + { + byte[] data = Encoding.ASCII.GetBytes("P2\n1 1\n100\n100"); + + using Image image = Image.Load(data); + + Assert.Equal(255, image[0, 0].PackedValue); + } + + [Fact] + public void Decode_NonStandardShortMaxPixelValue_UpscalesToFullRange() + { + byte[] header = Encoding.ASCII.GetBytes("P5\n1 1\n1000\n"); + byte[] samples = [0x03, 0xE8]; + byte[] data = [.. header, .. samples]; + + using Image image = Image.Load(data); + + Assert.Equal(65535, image[0, 0].PackedValue); + } + [Theory] [WithFile(RgbPlain, PixelTypes.Rgb24)] public void PbmDecoder_Decode_Resize(TestImageProvider provider) @@ -108,7 +170,7 @@ public void PbmDecoder_Decode_Resize(TestImageProvider provider) { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(PbmDecoder.Instance, options); @@ -123,6 +185,19 @@ public void PbmDecoder_Decode_Resize(TestImageProvider provider) appendPixelTypeToFileName: false); } + [Theory] + [InlineData("P2")] + [InlineData("P3")] + [InlineData("P5")] + [InlineData("P6")] + public void Decode_MaxPixelValueZero_ThrowsInvalidImageContentException(string magic) + { + byte[] bytes = Encoding.ASCII.GetBytes($"{magic} 1 1 0\n0"); + using MemoryStream stream = new(bytes); + + Assert.Throws(() => Image.Load(stream)); + } + [Fact] public void PlainText_PrematureEof() { diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs index 05f1d963b2..5ef075404a 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmEncoderTests.cs @@ -31,6 +31,7 @@ public class PbmEncoderTests { GrayscaleBinaryWide, PbmColorType.Grayscale }, { GrayscalePlain, PbmColorType.Grayscale }, { RgbBinary, PbmColorType.Rgb }, + { RgbBinaryWide, PbmColorType.Rgb }, { RgbPlain, PbmColorType.Rgb }, }; @@ -38,16 +39,16 @@ public class PbmEncoderTests [MemberData(nameof(PbmColorTypeFiles))] public void PbmEncoder_PreserveColorType(string imagePath, PbmColorType pbmColorType) { - var options = new PbmEncoder(); + PbmEncoder options = new(); - var testFile = TestFile.Create(imagePath); + TestFile testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { input.Save(memStream, options); memStream.Position = 0; - using (var output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { PbmMetadata meta = output.Metadata.GetPbmMetadata(); Assert.Equal(pbmColorType, meta.ColorType); @@ -60,15 +61,15 @@ public void PbmEncoder_PreserveColorType(string imagePath, PbmColorType pbmColor [MemberData(nameof(PbmColorTypeFiles))] public void PbmEncoder_WithPlainEncoding_PreserveBitsPerPixel(string imagePath, PbmColorType pbmColorType) { - var options = new PbmEncoder() + PbmEncoder options = new() { Encoding = PbmEncoding.Plain }; - var testFile = TestFile.Create(imagePath); + TestFile testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { input.Save(memStream, options); @@ -78,7 +79,7 @@ public void PbmEncoder_WithPlainEncoding_PreserveBitsPerPixel(string imagePath, Assert.Equal(0x20, lastByte); memStream.Seek(0, SeekOrigin.Begin); - using (var output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { PbmMetadata meta = output.Metadata.GetPbmMetadata(); Assert.Equal(pbmColorType, meta.ColorType); @@ -122,6 +123,45 @@ public void PbmEncoder_P3_Works(TestImageProvider provider) public void PbmEncoder_P6_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestPbmEncoderCore(provider, PbmColorType.Rgb, PbmEncoding.Binary); + [Fact] + public void PbmEncoder_WideBinaryGrayscale_WritesBigEndianSamples() + { + // Per the Netpbm specification, 16-bit samples store the most significant byte first. + using Image image = new(2, 1); + image[0, 0] = new L16(0x8000); + image[1, 0] = new L16(0x1234); + + using MemoryStream memStream = new(); + image.Save(memStream, new PbmEncoder + { + ColorType = PbmColorType.Grayscale, + ComponentType = PbmComponentType.Short, + Encoding = PbmEncoding.Binary + }); + + byte[] encoded = memStream.ToArray(); + Assert.Equal(new byte[] { 0x80, 0x00, 0x12, 0x34 }, encoded[^4..]); + } + + [Fact] + public void PbmEncoder_WideBinaryRgb_WritesBigEndianSamples() + { + // Per the Netpbm specification, 16-bit samples store the most significant byte first. + using Image image = new(1, 1); + image[0, 0] = new Rgb48(0x8000, 0x1234, 0x00FF); + + using MemoryStream memStream = new(); + image.Save(memStream, new PbmEncoder + { + ColorType = PbmColorType.Rgb, + ComponentType = PbmComponentType.Short, + Encoding = PbmEncoding.Binary + }); + + byte[] encoded = memStream.ToArray(); + Assert.Equal(new byte[] { 0x80, 0x00, 0x12, 0x34, 0x00, 0xFF }, encoded[^6..]); + } + private static void TestPbmEncoderCore( TestImageProvider provider, PbmColorType colorType, @@ -132,13 +172,13 @@ private static void TestPbmEncoderCore( { using (Image image = provider.GetImage()) { - var encoder = new PbmEncoder { ColorType = colorType, Encoding = encoding }; + PbmEncoder encoder = new() { ColorType = colorType, Encoding = encoding }; - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { image.Save(memStream, encoder); memStream.Position = 0; - using (var encodedImage = (Image)Image.Load(memStream)) + using (Image encodedImage = (Image)Image.Load(memStream)) { ImageComparingUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); } diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs index a69d9d9ba7..243a62d592 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/Pbm/PbmRoundTripTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmRoundTripTests.cs index b7ce32ed8f..6fca91e0d0 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmRoundTripTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmRoundTripTests.cs @@ -21,11 +21,11 @@ public class PbmRoundTripTests public void PbmGrayscaleImageCanRoundTrip(string imagePath) { // Arrange - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); // Act - using var originalImage = Image.Load(stream); + using Image originalImage = Image.Load(stream); using Image colorImage = originalImage.CloneAs(); using Image encodedImage = this.RoundTrip(colorImage); @@ -42,11 +42,11 @@ public void PbmGrayscaleImageCanRoundTrip(string imagePath) public void PbmColorImageCanRoundTrip(string imagePath) { // Arrange - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); // Act - using var originalImage = Image.Load(stream); + using Image originalImage = Image.Load(stream); using Image encodedImage = this.RoundTrip(originalImage); // Assert @@ -54,13 +54,47 @@ public void PbmColorImageCanRoundTrip(string imagePath) ImageComparer.Exact.VerifySimilarity(originalImage, encodedImage); } + [Theory] + [InlineData(GrayscaleBinaryWide)] + public void PbmWideGrayscaleImageCanRoundTrip(string imagePath) + { + // Arrange + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + // Act + using Image originalImage = Image.Load(stream); + using Image encodedImage = this.RoundTrip(originalImage); + + // Assert + Assert.NotNull(encodedImage); + ImageComparer.Exact.VerifySimilarity(originalImage, encodedImage); + } + + [Theory] + [InlineData(RgbBinaryWide)] + public void PbmWideColorImageCanRoundTrip(string imagePath) + { + // Arrange + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + // Act + using Image originalImage = Image.Load(stream); + using Image encodedImage = this.RoundTrip(originalImage); + + // Assert + Assert.NotNull(encodedImage); + ImageComparer.Exact.VerifySimilarity(originalImage, encodedImage); + } + private Image RoundTrip(Image originalImage) where TPixel : unmanaged, IPixel { - using var decodedStream = new MemoryStream(); + using MemoryStream decodedStream = new(); originalImage.SaveAsPbm(decodedStream); decodedStream.Seek(0, SeekOrigin.Begin); - var encodedImage = Image.Load(decodedStream); + Image encodedImage = Image.Load(decodedStream); return encodedImage; } } diff --git a/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs index 1b66c4cc3c..c2b640a1de 100644 --- a/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs @@ -30,7 +30,7 @@ private static void CalculateAdlerAndCompareToReference(int length) { // arrange byte[] data = GetBuffer(length); - var adler = new SharpAdler32(); + SharpAdler32 adler = new(); adler.Update(data); long expected = adler.Value; @@ -60,7 +60,7 @@ private static byte[] GetBuffer(int length) private static void RunCalculateAdlerTest() { - int[] testData = { 0, 8, 215, 1024, 1024 + 15, 2034, 4096 }; + int[] testData = [0, 8, 215, 1024, 1024 + 15, 2034, 4096]; for (int i = 0; i < testData.Length; i++) { CalculateAdlerAndCompareToReference(testData[i]); diff --git a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs deleted file mode 100644 index ff91590f95..0000000000 --- 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]); - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs new file mode 100644 index 0000000000..863c7fc4fc --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.Formats.Png; + +[Trait("Format", "Png")] +public class PngCgbiProcessorTests +{ + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(3)] + [InlineData(4)] + [InlineData(7)] + [InlineData(8)] + [InlineData(15)] + [InlineData(16)] + [InlineData(17)] + [InlineData(31)] + [InlineData(32)] + [InlineData(33)] + [InlineData(64)] + public void ApplyTransform_RgbWithAlpha_MatchesScalar(int pixelCount) => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + serialized => AssertApplyTransformMatchesScalar(FeatureTestRunner.Deserialize(serialized)), + pixelCount, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertApplyTransformMatchesScalar(int pixelCount) + { + byte[] input = CreateBgraScanline(pixelCount); + byte[] processorOutput = (byte[])input.Clone(); + byte[] scalarOutput = (byte[])input.Clone(); + + PngCgbiProcessor.ApplyTransform(Configuration.Default, processorOutput, PngColorType.RgbWithAlpha); + ApplyCgbiTransformScalarReference(scalarOutput); + + Assert.Equal(scalarOutput, processorOutput); + } + + /// + /// Builds synthetic input for tests. Produces inputs that exercise all three alpha cases. + /// + /// Channel values laid out as a defiltered CgBI scanline in premultiplied BGRA order + private static byte[] CreateBgraScanline(int pixelCount) + { + // The distinct strides keep the channels from being equal to each other or constant across pixels + // So a bug that e.g. swaps two channels or reuses one channel's value doesn't accidentally pass + const int alphaCaseCount = 7; + const int redStride = 13; + const int greenStride = 29; + const int blueStride = 53; + + byte[] bytes = new byte[pixelCount * 4]; + for (int p = 0; p < pixelCount; p++) + { + // Cycling alpha through [0..255], and an odd partial value every pixel rotation + // ensures all three branches get covered within any scanline of 3 or more pixels + byte a = (p % alphaCaseCount) switch + { + 0 => byte.MinValue, + 1 => byte.MaxValue, + _ => (byte)(((p * 37) + 23) | 1) // Produce a spread of alpha values and make sure to never get 0 + }; + + int offset = p * 4; + bytes[offset + 0] = Premultiply((byte)(p * blueStride), a); + bytes[offset + 1] = Premultiply((byte)(p * greenStride), a); + bytes[offset + 2] = Premultiply((byte)(p * redStride), a); + bytes[offset + 3] = a; + } + + return bytes; + + // CgBI stores channels premultiplied by alpha + static byte Premultiply(byte channel, byte alpha) => (byte)(channel * alpha / byte.MaxValue); + } + + private static void ApplyCgbiTransformScalarReference(Span scanline) + { + Span pixels = MemoryMarshal.Cast(scanline); + for (int i = 0; i < pixels.Length; i++) + { + ref Rgba32 pixel = ref pixels[i]; + pixel = new Rgba32(pixel.B, pixel.G, pixel.R, pixel.A); + + byte a = pixel.A; + if (a is 0 or byte.MaxValue) + { + continue; + } + + int half = a >> 1; + byte r = (byte)Math.Min(byte.MaxValue, ((pixel.R * byte.MaxValue) + half) / a); + byte g = (byte)Math.Min(byte.MaxValue, ((pixel.G * byte.MaxValue) + half) / a); + byte b = (byte)Math.Min(byte.MaxValue, ((pixel.B * byte.MaxValue) + half) / a); + pixel = new Rgba32(r, g, b, a); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs new file mode 100644 index 0000000000..95c452c0f8 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Png; + +namespace SixLabors.ImageSharp.Tests.Formats.Png; + +[Trait("Format", "Png")] +public class PngDecoderCoreTests +{ + [Fact] + public void CalculateScanlineLength_WithLargeGrayscaleWidth_ReturnsExpectedLength() + { + int length = PngDecoderCore.CalculateScanlineLength(536_870_913, 8, 1); + + Assert.Equal(536_870_913, length); + } + + [Fact] + public void CalculateScanlineLength_WithLargeRgbaWidth_ReturnsExpectedLength() + { + int length = PngDecoderCore.CalculateScanlineLength(33_554_432, 16, 8); + + Assert.Equal(268_435_456, length); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs index f5c42c734b..6616b04773 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs @@ -13,33 +13,33 @@ private static void RunAverageFilterTest() { // arrange byte[] scanline = - { + [ 3, 39, 39, 39, 0, 4, 4, 4, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 3, 3, 3, 0, 254, 254, 254, 0, 6, 6, 6, 14, 71, 71, 71, 157, 254, 254, 254, 28, 251, 251, 251, 0, 4, 4, 4, 0, 2, 2, 2, 0, 11, 11, 11, 0, 226, 226, 226, 0, 255, 128, 234 - }; + ]; byte[] previousScanline = - { + [ 3, 74, 74, 74, 0, 73, 73, 73, 0, 73, 73, 73, 0, 74, 74, 74, 0, 74, 74, 74, 0, 73, 73, 73, 0, 72, 72, 72, 0, 72, 72, 72, 0, 73, 73, 73, 0, 74, 74, 74, 0, 73, 73, 73, 0, 72, 72, 72, 0, 72, 72, 72, 0, 74, 74, 74, 0, 72, 72, 72, 0, 73, 73, 73, 0, 75, 75, 75, 0, 73, 73, 73, 0, 74, 74, 74, 0, 72, 72, 72, 0, 73, 73, 73, 0, 73, 73, 73, 0, 72, 72, 72, 0, 74, 74, 74, 0, 61, 61, 61, 0, 101, 101, 101, 78, 197, 197, 197, 251, 152, 152, 152, 255, 155, 155, 155, 255, 162, 162, 162, 255, 175, 175, 175, 255, 160, 160, 160, 255, 139, 128, 134 - }; + ]; byte[] expected = - { + [ 3, 76, 76, 76, 0, 78, 78, 78, 0, 76, 76, 76, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 76, 76, 76, 0, 78, 78, 78, 0, 77, 77, 77, 0, 78, 78, 78, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 77, 77, 77, 0, 78, 78, 78, 0, 77, 77, 77, 0, 77, 77, 77, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 73, 73, 73, 0, 73, 73, 73, 14, 158, 158, 158, 203, 175, 175, 175, 255, 158, 158, 158, 255, 160, 160, 160, 255, 163, 163, 163, 255, 180, 180, 180, 255, 140, 140, 140, 255, 138, 6, 115 - }; + ]; // act AverageFilter.Decode(scanline, previousScanline, 4); @@ -52,25 +52,25 @@ private static void RunUpFilterTest() { // arrange byte[] scanline = - { + [ 62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23, 93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220, 149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254 - }; + ]; byte[] previousScanline = - { + [ 214, 103, 135, 26, 133, 179, 134, 168, 175, 114, 118, 99, 167, 129, 55, 105, 129, 154, 173, 235, 179, 191, 41, 137, 253, 0, 81, 198, 159, 228, 224, 245, 14, 113, 5, 45, 126, 239, 233, 179, 229, 62, 66, 155, 207, 117, 128, 56, 181, 190, 160, 96, 11, 248, 74, 23, 62, 253, 29, 132, 98, 192, 9, 202 - }; + ]; byte[] expected = - { + [ 62, 126, 65, 176, 51, 183, 83, 227, 72, 248, 20, 185, 151, 46, 246, 163, 240, 81, 250, 16, 8, 214, 134, 85, 107, 139, 90, 218, 246, 126, 144, 43, 221, 71, 45, 56, 49, 182, 240, 142, 147, 48, 178, 119, 100, 122, 137, 166, 28, 41, 135, 81, 24, 62, 34, 62, 248, 234, 68, 166, 93, 121, 237, 200 - }; + ]; // act UpFilter.Decode(scanline, previousScanline); @@ -83,18 +83,18 @@ private static void RunSubFilterTest() { // arrange byte[] scanline = - { + [ 62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23, 93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220, 149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254 - }; + ]; byte[] expected = - { + [ 62, 23, 186, 150, 174, 27, 135, 209, 71, 161, 37, 39, 55, 78, 228, 97, 166, 5, 49, 134, 251, 28, 142, 82, 105, 167, 151, 102, 192, 65, 71, 156, 143, 23, 111, 167, 66, 222, 118, 130, 240, 208, 230, 94, 133, 213, 239, 204, 236, 64, 214, 189, 249, 134, 174, 228, 179, 115, 213, 6, 174, 44, 185, 4 - }; + ]; // act SubFilter.Decode(scanline, 4); @@ -107,25 +107,25 @@ private static void RunPaethFilterTest() { // arrange byte[] scanline = - { + [ 62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23, 93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220, 149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254 - }; + ]; byte[] previousScanline = - { + [ 214, 103, 135, 26, 133, 179, 134, 168, 175, 114, 118, 99, 167, 129, 55, 105, 129, 154, 173, 235, 179, 191, 41, 137, 253, 0, 81, 198, 159, 228, 224, 245, 14, 113, 5, 45, 126, 239, 233, 179, 229, 62, 66, 155, 207, 117, 128, 56, 181, 190, 160, 96, 11, 248, 74, 23, 62, 253, 29, 132, 98, 192, 9, 202 - }; + ]; byte[] expected = - { + [ 62, 126, 65, 176, 51, 183, 14, 235, 30, 248, 172, 254, 14, 165, 53, 56, 125, 92, 250, 16, 8, 177, 10, 220, 118, 139, 50, 240, 205, 126, 144, 43, 221, 71, 45, 54, 144, 182, 240, 142, 147, 48, 178, 106, 40, 122, 187, 166, 143, 41, 162, 151, 24, 111, 34, 135, 248, 92, 68, 169, 243, 21, 1, 200 - }; + ]; // act PaethFilter.Decode(scanline, previousScanline, 4); @@ -171,7 +171,7 @@ private static void RunPaethFilterTest() public void PaethFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.AllowAll); [Fact] - public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableSSSE3); + public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index aff8bc12a2..252ea49603 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers.Binary; +using System.IO.Hashing; using System.Text; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; @@ -14,47 +15,47 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png; public partial class PngDecoderTests { // Represents ASCII string of "123456789" - private readonly byte[] check = { 49, 50, 51, 52, 53, 54, 55, 56, 57 }; + private readonly byte[] check = [49, 50, 51, 52, 53, 54, 55, 56, 57]; // Contains the png marker, IHDR and pHYs chunks of a 1x1 pixel 32bit png 1 a single black pixel. private static readonly byte[] Raw1X1PngIhdrAndpHYs = - { - // PNG Identifier - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + [ + // PNG Identifier + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, - // IHDR - 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, - 0x00, 0x00, 0x00, + // IHDR + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, + 0x00, 0x00, 0x00, - // IHDR CRC - 0x90, 0x77, 0x53, 0xDE, + // IHDR CRC + 0x90, 0x77, 0x53, 0xDE, - // pHYS - 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, - 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, + // pHYS + 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, + 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, - // pHYS CRC - 0xC7, 0x6F, 0xA8, 0x64 - }; + // pHYS CRC + 0xC7, 0x6F, 0xA8, 0x64 + ]; // Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel. private static readonly byte[] Raw1X1PngIdatAndIend = - { - // IDAT - 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, - 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x01, + [ + // IDAT + 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, + 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x01, - // IDAT CRC - 0x5C, 0xCD, 0xFF, 0x69, + // IDAT CRC + 0x5C, 0xCD, 0xFF, 0x69, - // IEND - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, + // IEND + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, - // IEND CRC - 0xAE, 0x42, 0x60, 0x82 - }; + // IEND CRC + 0xAE, 0x42, 0x60, 0x82 + ]; [Theory] [InlineData((uint)PngChunkType.Header)] // IHDR @@ -64,19 +65,232 @@ public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType { string chunkName = GetChunkTypeName(chunkType); - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { WriteHeaderChunk(memStream); WriteChunk(memStream, chunkName); WriteDataChunk(memStream); - ImageFormatException exception = + InvalidImageContentException exception = Assert.Throws(() => PngDecoder.Instance.Decode(DecoderOptions.Default, memStream)); Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); } } + // https://github.com/SixLabors/ImageSharp/issues/3078 + [Fact] + public void Decode_TruncatedPhysChunk_ExceptionIsThrown() + { + // 24 bytes — PNG signature + truncated pHYs chunk + byte[] payload = Convert.FromHexString( + "89504e470d0a1a0a3030303070485973" + + "3030303030303030"); + + using MemoryStream stream = new(payload); + InvalidImageContentException exception = Assert.Throws(() => Image.Load(stream)); + + Assert.Equal("pHYs chunk is too short", exception.Message); + } + + // https://github.com/SixLabors/ImageSharp/issues/3093 + [Fact] + public void Decode_TruncatedFrameControlChunk_ExceptionIsThrown() + { + // PNG signature + truncated frame control chunk + byte[] payload = Convert.FromHexString( + "89504e470d0a1a0a424d3a00000000007f000000000028030405060000000100" + + "000101002000000000000000000000000000ff00006663544cff190000000000" + + "010000424d000100000101002000000000"); + + using MemoryStream stream = new(payload); + InvalidImageContentException exception = Assert.Throws(() => Image.Load(stream)); + + Assert.Equal("The frame control chunk does not contain enough data!", exception.Message); + } + + [Fact] + public void DecodeAndIdentify_WithDuplicateHeader_ThrowInvalidImageContentException() + { + using MemoryStream payloadStream = new(); + payloadStream.Write(Raw1X1PngIhdrAndpHYs); + payloadStream.Write(Raw1X1PngIhdrAndpHYs.AsSpan(8, 25)); + payloadStream.Write(Raw1X1PngIdatAndIend); + byte[] payload = payloadStream.ToArray(); + + Assert.Throws(() => Image.Load(payload)); + Assert.Throws(() => Image.Identify(payload)); + } + + /// + /// Chunk recovery must not replace the header after scanline storage has been sized. + /// + /// The segment integrity policy. + [Theory] + [InlineData(SegmentIntegrityHandling.Strict)] + [InlineData(SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(SegmentIntegrityHandling.IgnoreImageData)] + public void DecodeAndIdentify_WithChunkRecovery_FollowIntegrityPolicy(SegmentIntegrityHandling integrity) + { + byte[] data = TestFile.Create(TestImages.Png.DuplicateHeaderChunkResync).Bytes; + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + + Assert.Throws(() => Image.Load(options, data)); + + if (integrity == SegmentIntegrityHandling.Strict) + { + Assert.Throws(() => Image.Identify(options, data)); + } + else + { + // Identify skips the image-data payload rather than decoding and resynchronizing within it. + Assert.Equal(new Size(1, 1), Image.Identify(options, data).Size); + } + } + + /// + /// Corrupt compressed metadata follows the ancillary policy without preventing valid pixel decoding. + /// + /// The compressed metadata chunk type. + /// The segment integrity policy. + [Theory] + [InlineData("iCCP", SegmentIntegrityHandling.Strict)] + [InlineData("iCCP", SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData("iCCP", SegmentIntegrityHandling.IgnoreImageData)] + [InlineData("zTXt", SegmentIntegrityHandling.Strict)] + [InlineData("zTXt", SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData("zTXt", SegmentIntegrityHandling.IgnoreImageData)] + [InlineData("iTXt", SegmentIntegrityHandling.Strict)] + [InlineData("iTXt", SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData("iTXt", SegmentIntegrityHandling.IgnoreImageData)] + public void Decode_InvalidCompressedMetadata_FollowsIntegrityPolicy(string chunkType, SegmentIntegrityHandling integrity) + { + // iTXt adds a compression flag and empty language/translated-keyword fields before the zlib stream. + byte[] fields = chunkType == "iTXt" ? [(byte)'p', 0, 1, 0, 0, 0] : [(byte)'p', 0, 0]; + + // The zlib header is valid, but the first deflate block uses reserved block type 3. + byte[] chunk = [.. Encoding.ASCII.GetBytes(chunkType), .. fields, 0x78, 0x9C, 0x07, 0, 0, 0, 0]; + using MemoryStream stream = new(); + stream.Write(Raw1X1PngIhdrAndpHYs); + Span buffer = stackalloc byte[4]; + BinaryPrimitives.WriteInt32BigEndian(buffer, chunk.Length - 4); + stream.Write(buffer); + stream.Write(chunk); + Crc32 crc = new(); + crc.Append(chunk); + BinaryPrimitives.WriteUInt32BigEndian(buffer, crc.GetCurrentHashAsUInt32()); + stream.Write(buffer); + stream.Write(Raw1X1PngIdatAndIend); + byte[] data = stream.ToArray(); + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + + if (integrity == SegmentIntegrityHandling.Strict) + { + InvalidImageContentException exception = Assert.Throws(() => Image.Load(options, data)); + Assert.IsType(exception.InnerException); + } + else + { + using Image image = Image.Load(options, data); + Assert.Equal(new Size(1, 1), image.Size); + Assert.Equal(default(Rgb24), image[0, 0]); + Assert.Null(image.Metadata.IccProfile); + Assert.Empty(image.Metadata.GetPngMetadata().TextData); + } + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_CompressedTxtChunk_WithTruncatedData_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 2, // chunk length + 122, 84, 88, 116, // chunk type zTXt + 1, 0, // truncated data + 100, 138, 166, 20, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_InternationalText_WithTruncatedData_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 2, // chunk length + 105, 84, 88, 116, // chunk type iTXt + 1, 0, // truncated data + 225, 200, 214, 33, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + // https://github.com/SixLabors/ImageSharp/issues/3079 + [Fact] + public void Decode_InternationalText_WithTruncatedDataAfterLanguageTag_DoesNotThrow() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, // data + 55, 110, 249, 36, // checksum + 0, 0, 0, 21, // chunk length + 105, 84, 88, 116, // chunk type iTXt + 73, 110, 116, 101, 114, 110, 97, 116, 105, 111, 110, 97, 108, 50, 0, 0, 0, 114, 117, 115, 0, // truncated data after language tag + 225, 200, 214, 33, // crc + 0, 0, 0, 10, // chunk length 10 bytes + 73, 68, 65, 84, // chunk type IDAT + 120, 1, 99, 96, 0, 0, 0, 2, 0, 1, // data + 115, 117, 1, 24, // checksum + 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // end chunk + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + + [Fact] + public void Decode_tRnsChunk_WithAlphaLengthGreaterColorTableLength_ShouldNotThrowException() + { + byte[] payload = [137, 80, 78, 71, 13, 10, 26, 10, // PNG signature + 0, 0, 0, 13, // chunk length 13 bytes + 73, 72, 68, 82, // chunk type IHDR + 0, 0, 0, 1, 0, 0, 0, 1, 8, 3, 0, 0, 0, // data + 40, 203, 52, 187, // crc + 0, 0, 0, 6, // chunk length 6 bytes + 80, 76, 84, 69, // chunk type palettte + 255, 0, 0, 0, 255, 0, // data + 210, 135, 239, 113, // crc + 0, 0, 0, 18, // chunk length + 116, 82, 78, 83, // chunk type tRns + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, // data + 0, 0, 0, 10, // chunk length + 73, 68, 65, 84, // chunk type data + 120, 156, 99, 96, 0, 0, 0, 2, 0, 1, 72, 175, 164, 113]; // alpha.Length > colorTable.Length + + using MemoryStream stream = new(payload); + using Image image = Image.Load(stream); + } + private static string GetChunkTypeName(uint value) { byte[] data = new byte[4]; @@ -94,10 +308,10 @@ private static void WriteHeaderChunk(MemoryStream memStream) => private static void WriteChunk(MemoryStream memStream, string chunkName) { // Needs a minimum length of 9 for pHYs chunk. - memStream.Write(new byte[] { 0, 0, 0, 9 }, 0, 4); + memStream.Write([0, 0, 0, 9], 0, 4); memStream.Write(Encoding.ASCII.GetBytes(chunkName), 0, 4); // 4 bytes chunk header - memStream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 9); // 9 bytes of chunk data - memStream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); // Junk Crc + memStream.Write([0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 9); // 9 bytes of chunk data + memStream.Write([0, 0, 0, 0], 0, 4); // Junk Crc } private static void WriteDataChunk(MemoryStream memStream) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Icc.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Icc.cs new file mode 100644 index 0000000000..a69e0b21cd --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Icc.cs @@ -0,0 +1,158 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Png; + +public partial class PngDecoderTests +{ + [Fact] + public void Decode_IccLutExceedsVectorChannelCount_Throws() + { + byte[] profileData = BuildLut16Profile(3, 15, 2, 2, 2); + byte[] pngData = BuildPng(profileData); + DecoderOptions options = new() { ColorProfileHandling = ColorProfileHandling.Convert }; + + Assert.Throws(() => Image.Load(options, pngData)); + } + + /// + /// Three-channel LUT conversion remains supported. + /// + [Fact] + public void Decode_IccLutWithSupportedChannelCount_ConvertsPixels() + { + byte[] pngData = BuildPng(BuildLut16Profile(3, 3, 2, 2, 2)); + DecoderOptions options = new() { ColorProfileHandling = ColorProfileHandling.Convert }; + + using Image image = Image.Load(options, pngData); + Assert.Equal(new Size(16, 16), image.Size); + + // Every CLUT node contains a nonzero XYZ value, even though the encoded pixels are black. + Assert.NotEqual(default(Rgb24), image[0, 0]); + } + + /// + /// Preserving a profile does not impose the converter's four-component storage limit on the parser. + /// + /// The number of output channels in the LUT. + [Theory] + [InlineData(3)] + [InlineData(15)] + public void Decode_IccLut_Preserve_RetainsChannels(int outputChannels) + { + byte[] pngData = BuildPng(BuildLut16Profile(3, outputChannels, 2, 2, 2)); + DecoderOptions options = new() { ColorProfileHandling = ColorProfileHandling.Preserve }; + + using Image image = Image.Load(options, pngData); + IccLut16TagDataEntry entry = Assert.IsType(Assert.Single(image.Metadata.IccProfile.Entries)); + Assert.Equal(outputChannels, entry.OutputValues.Length); + Assert.Equal(default(Rgb24), image[0, 0]); + } + + private static byte[] BuildLut16Profile(int inputChannels, int outputChannels, int clutPoints, int inputTableLength, int outputTableLength) + { + using MemoryStream stream = new(); + + byte[] header = new byte[128]; + BinaryPrimitives.WriteUInt32BigEndian(header.AsSpan(8), 0x04300000U); + Encoding.ASCII.GetBytes("mntr").CopyTo(header, 12); + Encoding.ASCII.GetBytes("RGB ").CopyTo(header, 16); + Encoding.ASCII.GetBytes("XYZ ").CopyTo(header, 20); + stream.Write(header); + + WriteUInt32(1); + stream.Write(Encoding.ASCII.GetBytes("A2B0")); + long offsetPosition = stream.Position; + WriteUInt32(0); + long sizePosition = stream.Position; + WriteUInt32(0); + + long tagStart = stream.Position; + stream.Write(Encoding.ASCII.GetBytes("mft2")); + WriteUInt32(0); + stream.WriteByte((byte)inputChannels); + stream.WriteByte((byte)outputChannels); + stream.WriteByte((byte)clutPoints); + stream.WriteByte(0); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 3; x++) + { + WriteFix16(x == y ? 1D : 0D); + } + } + + WriteUInt16((ushort)inputTableLength); + WriteUInt16((ushort)outputTableLength); + + for (int channel = 0; channel < inputChannels; channel++) + { + for (int i = 0; i < inputTableLength; i++) + { + WriteUInt16((ushort)(i == 0 ? 0 : ushort.MaxValue)); + } + } + + int clutLength = (int)Math.Pow(clutPoints, inputChannels); + for (int i = 0; i < clutLength; i++) + { + for (int channel = 0; channel < outputChannels; channel++) + { + WriteUInt16(0x8000); + } + } + + for (int channel = 0; channel < outputChannels; channel++) + { + for (int i = 0; i < outputTableLength; i++) + { + WriteUInt16((ushort)(i == 0 ? 0 : ushort.MaxValue)); + } + } + + long tagEnd = stream.Position; + byte[] result = stream.ToArray(); + BinaryPrimitives.WriteUInt32BigEndian(result.AsSpan((int)offsetPosition), (uint)tagStart); + BinaryPrimitives.WriteUInt32BigEndian(result.AsSpan((int)sizePosition), (uint)(tagEnd - tagStart)); + BinaryPrimitives.WriteUInt32BigEndian(result, (uint)result.Length); + return result; + + void WriteUInt32(uint value) + { + Span buffer = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32BigEndian(buffer, value); + stream.Write(buffer); + } + + void WriteUInt16(ushort value) + { + Span buffer = stackalloc byte[2]; + BinaryPrimitives.WriteUInt16BigEndian(buffer, value); + stream.Write(buffer); + } + + void WriteFix16(double value) + { + int rawValue = (int)Math.Round(value * 65536D); + WriteUInt32(unchecked((uint)rawValue)); + } + } + + private static byte[] BuildPng(byte[] profileData) + { + using Image image = new(16, 16); + image.Metadata.IccProfile = new IccProfile(profileData); + + using MemoryStream stream = new(); + image.SaveAsPng(stream); + return stream.ToArray(); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 11af57e39f..88ed512d12 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers.Binary; using System.Runtime.Intrinsics.X86; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; @@ -21,7 +22,7 @@ public partial class PngDecoderTests private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; public static readonly string[] CommonTestImages = - { + [ TestImages.Png.Splash, TestImages.Png.FilterVar, @@ -35,29 +36,29 @@ public partial class PngDecoderTests TestImages.Png.Rgb24BppTrans, TestImages.Png.Bad.ChunkLength1, - TestImages.Png.Bad.ChunkLength2, - }; + TestImages.Png.Bad.ChunkLength2 + ]; public static readonly string[] TestImagesIssue1014 = - { + [ TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2, TestImages.Png.Issue1014_3, TestImages.Png.Issue1014_4, TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6 - }; + ]; public static readonly string[] TestImagesIssue1177 = - { + [ TestImages.Png.Issue1177_1, TestImages.Png.Issue1177_2 - }; + ]; public static readonly string[] CorruptedTestImages = - { + [ TestImages.Png.Bad.CorruptedChunk, TestImages.Png.Bad.ZlibOverflow, TestImages.Png.Bad.ZlibOverflow2, - TestImages.Png.Bad.ZlibZtxtBadHeader, - }; + TestImages.Png.Bad.ZlibZtxtBadHeader + ]; public static readonly TheoryData PixelFormatRange = new() { @@ -79,7 +80,7 @@ public partial class PngDecoderTests }; public static readonly string[] MultiFrameTestFiles = - { + [ TestImages.Png.APng, TestImages.Png.SplitIDatZeroLength, TestImages.Png.DisposeNone, @@ -90,7 +91,7 @@ public partial class PngDecoderTests TestImages.Png.BlendOverMultiple, TestImages.Png.FrameOffset, TestImages.Png.DefaultNotAnimated - }; + ]; [Theory] [MemberData(nameof(PixelFormatRange))] @@ -140,7 +141,7 @@ public void PngDecoder_Decode_Resize(TestImageProvider provider) { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(PngDecoder.Instance, options); @@ -178,7 +179,7 @@ static void RunTest(string arg1, string notUsed) DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(PngDecoder.Instance, options); @@ -206,6 +207,22 @@ public void Decode_WithAverageFilter(TestImageProvider provider) image.CompareToOriginal(provider, ImageComparer.Exact); } + [Theory] + [WithFile(TestImages.Png.Icc.Perceptual, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Icc.PerceptualcLUTOnly, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Icc.SRgbGray, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Icc.SRgbGrayInterlacedRgba32, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Icc.SRgbGrayInterlacedRgba64, PixelTypes.Rgba32)] + public void Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance, new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert }); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + Assert.Null(image.Metadata.IccProfile); + } + [Theory] [WithFile(TestImages.Png.SubFilter3BytesPerPixel, PixelTypes.Rgba32)] [WithFile(TestImages.Png.SubFilter4BytesPerPixel, PixelTypes.Rgba32)] @@ -381,6 +398,105 @@ public void Identify(string imagePath, int expectedPixelSize) 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 { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData }, stream); + + Assert.NotNull(imageInfo); + Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); + } + + [Fact] + public void Identify_AnimatedPng_ReadsFrameCountCorrectly() + { + TestFile testFile = TestFile.Create(TestImages.Png.AnimatedFrameCount); + + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + Assert.Equal(48, imageInfo.FrameMetadataCollection.Count); + } + + [Fact] + public void Identify_AnimatedPngWithMaxFrames_ReadsFrameCountCorrectly() + { + TestFile testFile = TestFile.Create(TestImages.Png.AnimatedFrameCount); + + using MemoryStream stream = new(testFile.Bytes, false); + ImageInfo imageInfo = Image.Identify(new DecoderOptions { MaxFrames = 40 }, stream); + + Assert.NotNull(imageInfo); + Assert.Equal(40, imageInfo.FrameMetadataCollection.Count); + } + + [Fact] + public void Load_AnimatedPngWithMaxFrames_ReadsFrameCountCorrectly() + { + TestFile testFile = TestFile.Create(TestImages.Png.AnimatedFrameCount); + + using MemoryStream stream = new(testFile.Bytes, false); + using Image image = Image.Load(new DecoderOptions { MaxFrames = 40 }, stream); + + Assert.NotNull(image); + Assert.Equal(40, image.Frames.Count); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public void Identify_AnimatedPng_FrameCount_MatchesDecode(int frameCount) + { + using Image image = new(10, 10, Color.Red.ToPixel()); + for (int i = 1; i < frameCount; i++) + { + using ImageFrame frame = new(Configuration.Default, 10, 10); + image.Frames.AddFrame(frame); + } + + using MemoryStream stream = new(); + image.Save(stream, new PngEncoder()); + stream.Position = 0; + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + Assert.Equal(frameCount, imageInfo.FrameMetadataCollection.Count); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public void Decode_AnimatedPng_FrameCount(int frameCount) + { + using Image image = new(10, 10, Color.Red.ToPixel()); + for (int i = 1; i < frameCount; i++) + { + using ImageFrame frame = new(Configuration.Default, 10, 10); + image.Frames.AddFrame(frame); + } + + using MemoryStream stream = new(); + image.Save(stream, new PngEncoder()); + stream.Position = 0; + + using Image decoded = Image.Load(stream); + + Assert.Equal(frameCount, decoded.Frames.Count); + } + [Theory] [WithFile(TestImages.Png.Bad.MissingDataChunk, PixelTypes.Rgba32)] public void Decode_MissingDataChunk_ThrowsException(TestImageProvider provider) @@ -479,13 +595,13 @@ public void Decode_InvalidDataChunkCrc_ThrowsException(TestImageProvider 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() { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData }); image.DebugSave(provider); 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 +668,7 @@ public void Issue1047(TestImageProvider provider) // 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); @@ -583,7 +699,7 @@ public void Issue2209_Decode_HasTransparencyIsTrue(TestImageProvider 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 @@ -596,29 +712,136 @@ public void Issue2209_Identify_HasTransparencyIsTrue(string imagePath) 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 [Theory] - [WithFile(TestImages.Png.Bad.Issue410_MalformedApplePng, PixelTypes.Rgba32)] - public void Issue410_MalformedApplePng(TestImageProvider provider) + [WithFile(TestImages.Png.Cgbi.Issue410, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Colors, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Clocks, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Screen, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Flecks, PixelTypes.Rgb24)] + public void Decode_AppleCgBI(TestImageProvider provider) where TPixel : unmanaged, IPixel + => FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunDecodeAppleCgbi, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic, + provider, + provider.PixelType.ToString()); + + private static void RunDecodeAppleCgbi(string providerDump, string pixelType) { - Exception ex = Record.Exception( - () => - { - using Image image = provider.GetImage(PngDecoder.Instance); - image.DebugSave(provider); + if (Enum.Parse(pixelType) == PixelTypes.Rgb24) + { + TestImageProvider provider = + FeatureTestRunner.DeserializeForXunit>(providerDump); - // 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); - } - }); - Assert.NotNull(ex); - Assert.Contains("Proprietary Apple PNG detected!", ex.Message); + using Image image = provider.GetImage(PngDecoder.Instance); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider, ImageComparer.Exact); + + return; + } + + TestImageProvider rgbaProvider = + FeatureTestRunner.DeserializeForXunit>(providerDump); + + using Image rgbaImage = rgbaProvider.GetImage(PngDecoder.Instance); + rgbaImage.DebugSave(rgbaProvider); + rgbaImage.CompareToReferenceOutput(rgbaProvider, ImageComparer.Exact); + } + + [Theory] + [InlineData(TestImages.Png.Cgbi.Colors, 120, 120)] + [InlineData(TestImages.Png.Cgbi.Issue410, 42, 26)] + [InlineData(TestImages.Png.Cgbi.Flecks, 510, 512)] + public void Identify_AppleCgBI(string imagePath, int expectedWidth, int expectedHeight) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + Assert.Equal(PngFormat.Instance, imageInfo.Metadata.DecodedImageFormat); + Assert.Equal(expectedWidth, imageInfo.Width); + Assert.Equal(expectedHeight, imageInfo.Height); + } + + [Theory] + [InlineData(TestImages.Png.Cgbi.BitDepth16)] + [InlineData(TestImages.Png.Cgbi.Palette)] + public void Identify_CgBI_IncompatibleHeader_ThrowsInvalidImageContentException(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + InvalidImageContentException ex = Assert.Throws(() => Image.Identify(stream)); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + + [Theory] + [WithFile(TestImages.Png.Cgbi.BitDepth16, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Cgbi.Palette, PixelTypes.Rgba32)] + public void Decode_CgBI_IncompatibleHeader_ThrowsInvalidImageContentException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + InvalidImageContentException ex = Assert.Throws( + () => { using Image image = provider.GetImage(PngDecoder.Instance); }); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + + [Theory] + [InlineData(TestImages.Png.Cgbi.BitDepth16)] + [InlineData(TestImages.Png.Cgbi.Palette)] + public void Identify_CgBI_AfterHeader_IncompatibleHeader_ThrowsInvalidImageContentException(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + byte[] reordered = MoveFirstPngChunkAfterSecond(testFile.Bytes); + using MemoryStream stream = new(reordered, false); + + InvalidImageContentException ex = Assert.Throws(() => Image.Identify(stream)); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + + [Theory] + [InlineData(TestImages.Png.Cgbi.BitDepth16)] + [InlineData(TestImages.Png.Cgbi.Palette)] + public void Decode_CgBI_AfterHeader_IncompatibleHeader_ThrowsInvalidImageContentException(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + byte[] reordered = MoveFirstPngChunkAfterSecond(testFile.Bytes); + using MemoryStream stream = new(reordered, false); + + InvalidImageContentException ex = Assert.Throws( + () => { using Image image = PngDecoder.Instance.Decode(DecoderOptions.Default, stream); }); + Assert.Contains("CgBI is only supported for 8-bit truecolor images", ex.Message); + } + + /// + /// Moves the first PNG chunk after the second while preserving each chunk's data and CRC. + /// + /// A PNG whose first two chunks should be exchanged. + /// A copy of the PNG with its first two chunks exchanged. + private static byte[] MoveFirstPngChunkAfterSecond(byte[] source) + { + const int signatureLength = 8; + const int chunkOverheadLength = 12; + + int firstChunkLength = BinaryPrimitives.ReadInt32BigEndian(source.AsSpan(signatureLength, 4)) + chunkOverheadLength; + int secondChunkOffset = signatureLength + firstChunkLength; + int secondChunkLength = BinaryPrimitives.ReadInt32BigEndian(source.AsSpan(secondChunkOffset, 4)) + chunkOverheadLength; + byte[] reordered = new byte[source.Length]; + + source.AsSpan(0, signatureLength).CopyTo(reordered); + source.AsSpan(secondChunkOffset, secondChunkLength).CopyTo(reordered.AsSpan(signatureLength)); + source.AsSpan(signatureLength, firstChunkLength).CopyTo(reordered.AsSpan(signatureLength + secondChunkLength)); + + // Chunk CRCs cover only each chunk's type and data, so moving complete chunks + // leaves both checksums valid and isolates ordering as the tested behavior. + source.AsSpan(secondChunkOffset + secondChunkLength) + .CopyTo(reordered.AsSpan(signatureLength + secondChunkLength + firstChunkLength)); + + return reordered; } [Theory] @@ -660,7 +883,7 @@ static void RunTest(string providerDump, string nonContiguousBuffersStr) public void Binary_PrematureEof() { PngDecoder decoder = PngDecoder.Instance; - PngDecoderOptions options = new() { PngCrcChunkHandling = PngCrcChunkHandling.IgnoreData }; + PngDecoderOptions options = new() { GeneralOptions = new DecoderOptions { SegmentIntegrityHandling = SegmentIntegrityHandling.IgnoreImageData } }; using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446, decoder, options); // TODO: Try to reduce this to 1. @@ -705,10 +928,20 @@ public void Decode_BadPalette(string file) [Theory] [WithFile(TestImages.Png.Issue2752, PixelTypes.Rgba32)] public void CanDecodeJustOneFrame(TestImageProvider provider) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { DecoderOptions options = new() { MaxFrames = 1 }; using Image image = provider.GetImage(PngDecoder.Instance, options); Assert.Equal(1, image.Frames.Count); } + + [Theory] + [WithFile(TestImages.Png.Issue2924, PixelTypes.Rgba32)] + public void CanDecode_Issue2924(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs index 796d35bf72..423d54e5f3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs @@ -31,7 +31,7 @@ public void Average() { static void RunTest() { - var data = new TestData(PngFilterMethod.Average, Size); + TestData data = new(PngFilterMethod.Average, Size); data.TestFilter(); } @@ -45,13 +45,13 @@ public void AverageSse2() { static void RunTest() { - var data = new TestData(PngFilterMethod.Average, Size); + TestData data = new(PngFilterMethod.Average, Size); data.TestFilter(); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSSE3); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Fact] @@ -59,13 +59,11 @@ public void AverageSsse3() { static void RunTest() { - var data = new TestData(PngFilterMethod.Average, Size); + TestData data = new(PngFilterMethod.Average, Size); data.TestFilter(); } - FeatureTestRunner.RunWithHwIntrinsicsFeature( - RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTest, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); } [Fact] @@ -73,7 +71,7 @@ public void AverageAvx2() { static void RunTest() { - var data = new TestData(PngFilterMethod.Average, Size); + TestData data = new(PngFilterMethod.Average, Size); data.TestFilter(); } @@ -87,7 +85,7 @@ public void Paeth() { static void RunTest() { - var data = new TestData(PngFilterMethod.Paeth, Size); + TestData data = new(PngFilterMethod.Paeth, Size); data.TestFilter(); } @@ -101,7 +99,7 @@ public void PaethAvx2() { static void RunTest() { - var data = new TestData(PngFilterMethod.Paeth, Size); + TestData data = new(PngFilterMethod.Paeth, Size); data.TestFilter(); } @@ -115,7 +113,7 @@ public void PaethVector() { static void RunTest() { - var data = new TestData(PngFilterMethod.Paeth, Size); + TestData data = new(PngFilterMethod.Paeth, Size); data.TestFilter(); } @@ -129,7 +127,7 @@ public void Up() { static void RunTest() { - var data = new TestData(PngFilterMethod.Up, Size); + TestData data = new(PngFilterMethod.Up, Size); data.TestFilter(); } @@ -143,7 +141,7 @@ public void UpAvx2() { static void RunTest() { - var data = new TestData(PngFilterMethod.Up, Size); + TestData data = new(PngFilterMethod.Up, Size); data.TestFilter(); } @@ -157,7 +155,7 @@ public void UpVector() { static void RunTest() { - var data = new TestData(PngFilterMethod.Up, Size); + TestData data = new(PngFilterMethod.Up, Size); data.TestFilter(); } @@ -171,7 +169,7 @@ public void Sub() { static void RunTest() { - var data = new TestData(PngFilterMethod.Sub, Size); + TestData data = new(PngFilterMethod.Sub, Size); data.TestFilter(); } @@ -185,7 +183,7 @@ public void SubAvx2() { static void RunTest() { - var data = new TestData(PngFilterMethod.Sub, Size); + TestData data = new(PngFilterMethod.Sub, Size); data.TestFilter(); } @@ -199,7 +197,7 @@ public void SubVector() { static void RunTest() { - var data = new TestData(PngFilterMethod.Sub, Size); + TestData data = new(PngFilterMethod.Sub, Size); data.TestFilter(); } @@ -208,6 +206,95 @@ static void RunTest() HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); } + /// + /// Verifies every encoder across pixel strides, register boundaries, and hardware widths. + /// + [Fact] + public void EncodeMatchesReferencesAcrossRegisterBoundaries() + => FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertEncodersMatchReferences, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Compares every filter with its independent scalar reference across SIMD boundaries and pixel strides. + /// + private static void AssertEncodersMatchReferences() + { + int[] bytesPerPixels = [1, 2, 3, 4, 6, 8]; + int[] lengths = [8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129, 131, 132, 133, 135, 136, 137, 257]; + + foreach (int bytesPerPixel in bytesPerPixels) + { + foreach (int length in lengths) + { + if (length < bytesPerPixel) + { + continue; + } + + byte[] scanline = new byte[length]; + byte[] previousScanline = new byte[length]; + Random random = new((bytesPerPixel * 397) + length); + random.NextBytes(scanline); + random.NextBytes(previousScanline); + + AssertFilterMatchesReference(PngFilterMethod.Sub, scanline, previousScanline, bytesPerPixel); + + AssertFilterMatchesReference(PngFilterMethod.Up, scanline, previousScanline, bytesPerPixel); + + AssertFilterMatchesReference(PngFilterMethod.Average, scanline, previousScanline, bytesPerPixel); + + AssertFilterMatchesReference(PngFilterMethod.Paeth, scanline, previousScanline, bytesPerPixel); + } + } + } + + /// + /// Compares one filter result and variance sum with its scalar reference. + /// + /// The filter to evaluate. + /// The current scanline. + /// The preceding scanline. + /// The component distance between adjacent pixels. + private static void AssertFilterMatchesReference(PngFilterMethod filter, byte[] scanline, byte[] previousScanline, int bytesPerPixel) + { + byte[] expected = new byte[scanline.Length + 1]; + byte[] actual = new byte[scanline.Length + 1]; + int expectedSum; + int actualSum; + + switch (filter) + { + case PngFilterMethod.Sub: + ReferenceImplementations.EncodeSubFilter(scanline, expected, bytesPerPixel, out expectedSum); + SubFilter.Encode(scanline, actual, bytesPerPixel, out actualSum); + break; + + case PngFilterMethod.Up: + ReferenceImplementations.EncodeUpFilter(scanline, previousScanline, expected, out expectedSum); + UpFilter.Encode(scanline, previousScanline, actual, out actualSum); + break; + + case PngFilterMethod.Average: + ReferenceImplementations.EncodeAverageFilter(scanline, previousScanline, expected, bytesPerPixel, out expectedSum); + + AverageFilter.Encode(scanline, previousScanline, actual, (uint)bytesPerPixel, out actualSum); + + break; + + case PngFilterMethod.Paeth: + ReferenceImplementations.EncodePaethFilter(scanline, previousScanline, expected, bytesPerPixel, out expectedSum); + + PaethFilter.Encode(scanline, previousScanline, actual, bytesPerPixel, out actualSum); + + break; + + default: + throw new InvalidOperationException(); + } + + Assert.Equal(expectedSum, actualSum); + Assert.Equal(expected, actual); + } + public class TestData { private readonly PngFilterMethod filter; @@ -227,7 +314,7 @@ public TestData(PngFilterMethod filter, int size, int bpp = 4) this.expectedResult = new byte[1 + (size * size * bpp)]; this.resultBuffer = new byte[1 + (size * size * bpp)]; - var rng = new Random(12345678); + Random rng = new(12345678); byte[] tmp = new byte[6]; for (int i = 0; i < this.previousScanline.Length; i += bpp) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs index 76fd260dd5..fdaa70e031 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs @@ -188,15 +188,15 @@ public void IgnoreMetadata_WillExcludeAllAncillaryChunks() { PngChunkType.Data, false }, { PngChunkType.End, false } }; - List excludedChunkTypes = new() - { + List excludedChunkTypes = + [ PngChunkType.Gamma, PngChunkType.Exif, PngChunkType.Physical, PngChunkType.Text, PngChunkType.InternationalText, - PngChunkType.CompressedText, - }; + PngChunkType.CompressedText + ]; // act input.Save(memStream, encoder); @@ -252,7 +252,7 @@ public void ExcludeFilter_Works(object filterObj) { PngChunkType.Data, false }, { PngChunkType.End, false } }; - List excludedChunkTypes = new(); + List excludedChunkTypes = []; switch (chunkFilter) { case PngChunkFilter.ExcludeGammaChunk: @@ -326,8 +326,8 @@ public void ExcludeFilter_WithNone_DoesNotExcludeChunks() using Image input = testFile.CreateRgba32Image(); using MemoryStream memStream = new(); PngEncoder encoder = new() { ChunkFilter = PngChunkFilter.None, TextCompressionThreshold = 8 }; - List expectedChunkTypes = new() - { + List expectedChunkTypes = + [ PngChunkType.Header, PngChunkType.Gamma, PngChunkType.EmbeddedColorProfile, @@ -338,8 +338,8 @@ public void ExcludeFilter_WithNone_DoesNotExcludeChunks() PngChunkType.Exif, PngChunkType.Physical, PngChunkType.Data, - PngChunkType.End, - }; + PngChunkType.End + ]; // act input.Save(memStream, encoder); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index ca5aae961c..eef8d5ba84 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -88,11 +88,11 @@ public static readonly TheoryData CompressionLevels 80, 100, 120, 230 }; - public static readonly PngInterlaceMode[] InterlaceMode = new[] - { + public static readonly PngInterlaceMode[] InterlaceMode = + [ PngInterlaceMode.None, PngInterlaceMode.Adam7 - }; + ]; public static readonly TheoryData RatioFiles = new() @@ -254,49 +254,6 @@ public void WorksWithAllBitDepthsAndExcludeAllFilter(TestImageProvider(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) @@ -332,8 +289,8 @@ public void WritesFileMarker(TestImageProvider provider) byte[] data = ms.ToArray().Take(8).ToArray(); byte[] expected = - { - 0x89, // Set the high bit. + [ + 0x89, // Set the high bit. 0x50, // P 0x4E, // N 0x47, // G @@ -341,7 +298,7 @@ public void WritesFileMarker(TestImageProvider provider) 0x0A, // Line ending CRLF 0x1A, // EOF 0x0A // LF - }; + ]; Assert.Equal(expected, data); } @@ -383,16 +340,16 @@ public void Encode_PreserveBits(string imagePath, PngBitDepth pngBitDepth) [InlineData(PngColorType.Palette)] [InlineData(PngColorType.RgbWithAlpha)] [InlineData(PngColorType.GrayscaleWithAlpha)] - public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType colorType) + public void Encode_WithTransparentColorBehaviorClear_Works(PngColorType colorType) { // arrange - Image image = new(50, 50); + using Image image = new(50, 50); PngEncoder encoder = new() { - TransparentColorMode = PngTransparentColorMode.Clear, + TransparentColorMode = TransparentColorMode.Clear, ColorType = colorType }; - Rgba32 rgba32 = Color.Blue; + Rgba32 rgba32 = Color.Blue.ToPixel(); image.ProcessPixelRows(accessor => { for (int y = 0; y < image.Height; y++) @@ -407,7 +364,7 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color for (int x = 0; x < image.Width; x++) { - rowSpan[x].FromRgba32(rgba32); + rowSpan[x] = Rgba32.FromRgba32(rgba32); } } }); @@ -419,7 +376,7 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color // 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); @@ -428,13 +385,14 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color 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++) @@ -449,6 +407,7 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] [WithFile(TestImages.Png.DefaultNotAnimated, PixelTypes.Rgba32)] [WithFile(TestImages.Png.FrameOffset, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.Issue2882, PixelTypes.Rgba32)] public void Encode_APng(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -461,8 +420,8 @@ public void Encode_APng(TestImageProvider provider) using Image output = Image.Load(memStream); - // some loss from original, due to compositing - ImageComparer.TolerantPercentage(0.01f).VerifySimilarity(output, image); + // Some loss from original, due to palette matching accuracy. + ImageComparer.TolerantPercentage(0.172F).VerifySimilarity(output, image); Assert.Equal(image.Frames.Count, output.Frames.Count); @@ -478,13 +437,14 @@ public void Encode_APng(TestImageProvider provider) 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); } } [Theory] [WithFile(TestImages.Gif.Leo, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.Issues.Issue2866, PixelTypes.Rgba32)] public void Encode_AnimatedFormatTransform_FromGif(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -495,41 +455,45 @@ public void Encode_AnimatedFormatTransform_FromGif(TestImageProvider image = provider.GetImage(GifDecoder.Instance); + // Save the image for visual inspection. + provider.Utility.SaveTestOutputFile(image, "png", PngEncoder, "animated"); + + // Now compare the debug output with the reference output. + // We do this because the transcoding encoding is lossy and encoding will lead to differences. + // From the unencoded image, we can see that the image is visually the same. + static bool Predicate(int i, int _) => i % 8 == 0; // Image has many frames, only compare a selection of them. + image.CompareDebugOutputToReferenceOutputMultiFrame(provider, ImageComparer.Exact, extension: "png", encoder: PngEncoder, predicate: Predicate); + + // Now save the image and load it again to compare the metadata. using MemoryStream memStream = new(); image.Save(memStream, PngEncoder); memStream.Position = 0; - using Image output = Image.Load(memStream); - - // 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.613f).VerifySimilarity(output, image); - + using Image encoded = Image.Load(memStream); GifMetadata gif = image.Metadata.GetGifMetadata(); - PngMetadata png = output.Metadata.GetPngMetadata(); + PngMetadata png = encoded.Metadata.GetPngMetadata(); Assert.Equal(gif.RepeatCount, png.RepeatCount); for (int i = 0; i < image.Frames.Count; i++) { GifFrameMetadata gifF = image.Frames[i].Metadata.GetGifMetadata(); - PngFrameMetadata pngF = output.Frames[i].Metadata.GetPngMetadata(); + PngFrameMetadata pngF = encoded.Frames[i].Metadata.GetPngMetadata(); Assert.Equal(gifF.FrameDelay, (int)(pngF.FrameDelay.ToDouble() * 100)); - switch (gifF.DisposalMethod) + switch (gifF.DisposalMode) { - case GifDisposalMethod.RestoreToBackground: - Assert.Equal(PngDisposalMethod.RestoreToBackground, pngF.DisposalMethod); + case FrameDisposalMode.RestoreToBackground: + Assert.Equal(FrameDisposalMode.RestoreToBackground, pngF.DisposalMode); break; - case GifDisposalMethod.RestoreToPrevious: - Assert.Equal(PngDisposalMethod.RestoreToPrevious, pngF.DisposalMethod); + case FrameDisposalMode.RestoreToPrevious: + Assert.Equal(FrameDisposalMode.RestoreToPrevious, pngF.DisposalMode); break; - case GifDisposalMethod.Unspecified: - case GifDisposalMethod.NotDispose: + case FrameDisposalMode.Unspecified: + case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(PngDisposalMethod.DoNotDispose, pngF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, pngF.DisposalMode); break; } } @@ -566,25 +530,25 @@ public void Encode_AnimatedFormatTransform_FromWebp(TestImageProvider(TestImageProvider(TestImageProvider provider) @@ -699,6 +663,39 @@ public void Issue2668_Quantized_Encode_Alpha(TestImageProvider p encoded.CompareToReferenceOutput(ImageComparer.Exact, provider); } + [Fact] + public void Issue_2862() + { + // Create a grayscale palette (or any other palette with colors that are very close to each other): + Rgba32[] palette = [.. Enumerable.Range(0, 256).Select(i => new Rgba32((byte)i, (byte)i, (byte)i))]; + + using Image image = new(254, 4); + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + image[x, y] = palette[x]; + } + } + + PaletteQuantizer quantizer = new( + palette.Select(Color.FromPixel).ToArray(), + new QuantizerOptions { ColorMatchingMode = ColorMatchingMode.Exact }); + + using MemoryStream ms = new(); + image.Save(ms, new PngEncoder + { + ColorType = PngColorType.Palette, + BitDepth = PngBitDepth.Bit8, + Quantizer = quantizer + }); + + ms.Position = 0; + + using Image encoded = Image.Load(ms); + ImageComparer.Exact.VerifySimilarity(image, encoded); + } + private static void TestPngEncoderCore( TestImageProvider provider, PngColorType pngColorType, diff --git a/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngFrameMetadataTests.cs index 9ba2617281..efc18c16ae 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; @@ -13,23 +14,23 @@ public void CloneIsDeep() { PngFrameMetadata meta = new() { - FrameDelay = new(1, 0), - DisposalMethod = PngDisposalMethod.RestoreToBackground, - BlendMethod = PngBlendMethod.Over, + FrameDelay = new Rational(1, 0), + 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.FrameDelay = new Rational(2, 1); + 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/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 225e4deef2..42048426ee 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -31,16 +32,16 @@ public void CloneIsDeep() ColorType = PngColorType.GrayscaleWithAlpha, InterlaceMethod = PngInterlaceMode.Adam7, Gamma = 2, - TextData = new List { new PngTextData("name", "value", "foo", "bar") }, + TextData = [new("name", "value", "foo", "bar")], RepeatCount = 123, AnimateRootFrame = false }; - PngMetadata clone = (PngMetadata)meta.DeepClone(); + PngMetadata clone = meta.DeepClone(); - Assert.True(meta.BitDepth == clone.BitDepth); - Assert.True(meta.ColorType == clone.ColorType); - Assert.True(meta.InterlaceMethod == clone.InterlaceMethod); + Assert.Equal(meta.BitDepth, clone.BitDepth); + Assert.Equal(meta.ColorType, clone.ColorType); + Assert.Equal(meta.InterlaceMethod, clone.InterlaceMethod); Assert.True(meta.Gamma.Equals(clone.Gamma)); Assert.False(meta.TextData.Equals(clone.TextData)); Assert.True(meta.TextData.SequenceEqual(clone.TextData)); @@ -53,15 +54,47 @@ public void CloneIsDeep() clone.Gamma = 1; clone.RepeatCount = 321; - Assert.False(meta.BitDepth == clone.BitDepth); - Assert.False(meta.ColorType == clone.ColorType); - Assert.False(meta.InterlaceMethod == clone.InterlaceMethod); + Assert.NotEqual(meta.BitDepth, clone.BitDepth); + Assert.NotEqual(meta.ColorType, clone.ColorType); + Assert.NotEqual(meta.InterlaceMethod, clone.InterlaceMethod); Assert.False(meta.Gamma.Equals(clone.Gamma)); Assert.False(meta.TextData.Equals(clone.TextData)); Assert.True(meta.TextData.SequenceEqual(clone.TextData)); Assert.False(meta.RepeatCount == clone.RepeatCount); } + [Theory] + [WithFile(TestImages.Png.IptcMetadata, PixelTypes.Rgba32)] + public void Decoder_CanReadIptcProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + Assert.NotNull(image.Metadata.IptcProfile); + Assert.Equal("test1, test2", image.Metadata.IptcProfile.GetValues(IptcTag.Keywords)[0].Value); + Assert.Equal("\0\u0004", image.Metadata.IptcProfile.GetValues(IptcTag.RecordVersion)[0].Value); + } + + [Theory] + [WithFile(TestImages.Png.IptcMetadata, PixelTypes.Rgba32)] + public void Encoder_CanWriteIptcProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + Assert.NotNull(image.Metadata.IptcProfile); + Assert.Equal("test1, test2", image.Metadata.IptcProfile.GetValues(IptcTag.Keywords)[0].Value); + Assert.Equal("\0\u0004", image.Metadata.IptcProfile.GetValues(IptcTag.RecordVersion)[0].Value); + + using MemoryStream memoryStream = new(); + image.Save(memoryStream, new PngEncoder()); + + memoryStream.Position = 0; + + using Image decoded = PngDecoder.Instance.Decode(DecoderOptions.Default, memoryStream); + Assert.NotNull(decoded.Metadata.IptcProfile); + Assert.Equal("test1, test2", decoded.Metadata.IptcProfile.GetValues(IptcTag.Keywords)[0].Value); + Assert.Equal("\0\u0004", decoded.Metadata.IptcProfile.GetValues(IptcTag.RecordVersion)[0].Value); + } + [Theory] [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] public void Decoder_CanReadTextData(TestImageProvider provider) @@ -336,4 +369,28 @@ public void Identify_ReadsLegacyExifData(string imagePath) Assert.Equal(42, (int)exif.GetValue(ExifTag.ImageNumber).Value); } + + [Theory] + [InlineData(PixelColorType.Binary, PngColorType.Palette)] + [InlineData(PixelColorType.Indexed, PngColorType.Palette)] + [InlineData(PixelColorType.Luminance, PngColorType.Grayscale)] + [InlineData(PixelColorType.RGB, PngColorType.Rgb)] + [InlineData(PixelColorType.BGR, PngColorType.Rgb)] + [InlineData(PixelColorType.YCbCr, PngColorType.RgbWithAlpha)] + [InlineData(PixelColorType.CMYK, PngColorType.RgbWithAlpha)] + [InlineData(PixelColorType.YCCK, PngColorType.RgbWithAlpha)] + public void FromFormatConnectingMetadata_ConvertColorTypeAsExpected(PixelColorType pixelColorType, PngColorType expectedPngColorType) + { + FormatConnectingMetadata formatConnectingMetadata = new() + { + PixelTypeInfo = new PixelTypeInfo(24) + { + ColorType = pixelColorType, + }, + }; + + PngMetadata actual = PngMetadata.FromFormatConnectingMetadata(formatConnectingMetadata); + + Assert.Equal(expectedPngColorType, actual.ColorType); + } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index a4288a3d84..3ca7730eb1 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 void Resize(TestImageProvider provider) { // 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); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTextDataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngTextDataTests.cs index 878f3fb8d4..4c46692f36 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngTextDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngTextDataTests.cs @@ -17,8 +17,8 @@ public class PngTextDataTests [Fact] public void AreEqual() { - var property1 = new PngTextData("Foo", "Bar", "foo", "bar"); - var property2 = new PngTextData("Foo", "Bar", "foo", "bar"); + PngTextData property1 = new("Foo", "Bar", "foo", "bar"); + PngTextData property2 = new("Foo", "Bar", "foo", "bar"); Assert.Equal(property1, property2); Assert.True(property1 == property2); @@ -30,10 +30,10 @@ public void AreEqual() [Fact] public void AreNotEqual() { - var property1 = new PngTextData("Foo", "Bar", "foo", "bar"); - var property2 = new PngTextData("Foo", "Foo", string.Empty, string.Empty); - var property3 = new PngTextData("Bar", "Bar", "unit", "test"); - var property4 = new PngTextData("Foo", null, "test", "case"); + PngTextData property1 = new("Foo", "Bar", "foo", "bar"); + PngTextData property2 = new("Foo", "Foo", string.Empty, string.Empty); + PngTextData property3 = new("Bar", "Bar", "unit", "test"); + PngTextData property4 = new("Foo", null, "test", "case"); Assert.NotEqual(property1, property2); Assert.True(property1 != property2); @@ -59,7 +59,7 @@ public void ConstructorThrowsWhenKeywordIsNullOrEmpty() [Fact] public void ConstructorAssignsProperties() { - var property = new PngTextData("Foo", null, "unit", "test"); + PngTextData property = new("Foo", null, "unit", "test"); Assert.Equal("Foo", property.Keyword); Assert.Null(property.Value); Assert.Equal("unit", property.LanguageTag); diff --git a/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs index 31ec27da0c..5ec23b1464 100644 --- a/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs +++ b/tests/ImageSharp.Tests/Formats/Qoi/ImageExtensionsTest.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Qoi; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Qoi; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.Formats.Qoi; diff --git a/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs index d57b597b06..9da9ad3275 100644 --- a/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Qoi; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -24,7 +25,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,11 +35,69 @@ public static void Encode(TestImageProvider provider, QoiChannel 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); Assert.Equal(qoiMetadata.Channels, channels); Assert.Equal(qoiMetadata.ColorSpace, colorSpace); } + + [Fact] + public void Encode_WithTransparentColorBehaviorClear_Works() + { + // arrange + using Image image = new(50, 50); + QoiEncoder encoder = new() + { + TransparentColorMode = TransparentColorMode.Clear, + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + 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 = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index da5de8e898..03669908ed 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,10 +723,8 @@ public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider image = provider.GetImage(TgaDecoder.Instance)) { - // Using here the reference output instead of the 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); } } @@ -752,7 +749,7 @@ public void TgaDecoder_Decode_Resize(TestImageProvider provider) { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(TgaDecoder.Instance, options); @@ -771,6 +768,19 @@ public void TgaDecoder_Decode_Resize(TestImageProvider provider) 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/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 709a3207aa..adf0b4353d 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -10,109 +11,102 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga; [Trait("Format", "Tga")] +[ValidateDisposedMemoryAllocations] 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] [MemberData(nameof(TgaBitsPerPixelFiles))] public void TgaEncoder_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) { - var options = new TgaEncoder(); + TgaEncoder options = new(); - var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, options); - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - TgaMetadata meta = output.Metadata.GetTgaMetadata(); - Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); - } - } - } + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + + input.Save(memStream, options); + memStream.Position = 0; + + using Image output = Image.Load(memStream); + TgaMetadata meta = output.Metadata.GetTgaMetadata(); + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); } [Theory] [MemberData(nameof(TgaBitsPerPixelFiles))] public void TgaEncoder_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) { - var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; - var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateRgba32Image()) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, options); - memStream.Position = 0; - using (var output = Image.Load(memStream)) - { - TgaMetadata meta = output.Metadata.GetTgaMetadata(); - Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); - } - } - } + TgaEncoder options = new() { Compression = TgaCompression.RunLength }; + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + + input.Save(memStream, options); + memStream.Position = 0; + + using Image output = Image.Load(memStream); + TgaMetadata meta = output.Metadata.GetTgaMetadata(); + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); } [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] @@ -121,16 +115,14 @@ public void TgaEncoder_DoesNotAlwaysUseRunLengthPackets(TestImageProvide 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. @@ -138,22 +130,18 @@ public void TgaEncoder_DoesNotAlwaysUseRunLengthPackets(TestImageProvide [Fact] public void TgaEncoder_RunLengthDoesNotCrossRowBoundaries() { - var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; + TgaEncoder options = new() { Compression = TgaCompression.RunLength }; - using (var input = new Image(30, 30)) - { - using (var memStream = new MemoryStream()) - { - input.Save(memStream, options); - byte[] imageBytes = memStream.ToArray(); - Assert.Equal(138, imageBytes.Length); - } - } + using Image input = new(30, 30); + using MemoryStream memStream = new(); + input.Save(memStream, options); + byte[] imageBytes = memStream.ToArray(); + Assert.Equal(138, imageBytes.Length); } [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 { @@ -161,6 +149,65 @@ public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider image = new(50, 50); + TgaEncoder encoder = new() + { + BitsPerPixel = TgaBitsPerPixel.Bit32, + TransparentColorMode = TransparentColorMode.Clear, + }; + Rgba32 rgba32 = Color.Blue.ToPixel(); + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < image.Height; y++) + { + Span rowSpan = accessor.GetRowSpan(y); + + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x] = Rgba32.FromRgba32(rgba32); + } + } + }); + + // act + using MemoryStream memStream = new(); + image.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using Image actual = Image.Load(memStream); + Rgba32 expectedColor = Color.Blue.ToPixel(); + + 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 = transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } + } + }); + } + private static void TestTgaEncoderCore( TestImageProvider provider, TgaBitsPerPixel bitsPerPixel, @@ -169,19 +216,15 @@ private static void TestTgaEncoderCore( float compareTolerance = 0.01f) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - var encoder = new TgaEncoder { BitsPerPixel = bitsPerPixel, Compression = compression }; + using Image image = provider.GetImage(); + TgaEncoder encoder = new() { BitsPerPixel = bitsPerPixel, Compression = compression }; - using (var memStream = new MemoryStream()) - { - image.Save(memStream, encoder); - memStream.Position = 0; - using (var encodedImage = (Image)Image.Load(memStream)) - { - ImageComparingUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); - } - } - } + using MemoryStream memStream = new(); + image.DebugSave(provider, encoder); + image.Save(memStream, encoder); + memStream.Position = 0; + + using Image encodedImage = (Image)Image.Load(memStream); + ImageComparingUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index bf24ba3500..efbdb8eebb 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. @@ -33,10 +35,10 @@ public void ImageLoad_WithNoValidTgaHeaderBytes_Throws_UnknownImageFormatExcepti } [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/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs index 8e90b1dd57..769ee00a82 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -65,7 +65,7 @@ public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, 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); @@ -115,4 +115,17 @@ public void TiffDecoder_SubIfd8(TestImageProvider provider) Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripOffsets)); Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripByteCounts)); } + + [Fact] + public void TiffDecoder_DirectoryEntryCountExceedsAvailableData_Throws() + { + byte[] data = + [ + 0x49, 0x49, 0x2B, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00, + ]; + + Assert.Throws(() => Image.Load(data)); + } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 4646de7f82..6aebf7b9c3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -16,7 +16,7 @@ public class BigTiffMetadataTests [Fact] public void ExifLong8() { - var long8 = new ExifLong8(ExifTagValue.StripByteCounts); + ExifLong8 long8 = new(ExifTagValue.StripByteCounts); Assert.True(long8.TrySetValue(0)); Assert.Equal(0UL, long8.GetValue()); @@ -34,7 +34,7 @@ public void ExifLong8() [Fact] public void ExifSignedLong8() { - var long8 = new ExifSignedLong8(ExifTagValue.ImageID); + ExifSignedLong8 long8 = new(ExifTagValue.ImageID); Assert.False(long8.TrySetValue(0)); @@ -53,7 +53,7 @@ public void ExifSignedLong8() [Fact] public void ExifLong8Array() { - var long8 = new ExifLong8Array(ExifTagValue.StripOffsets); + ExifLong8Array long8 = new(ExifTagValue.StripOffsets); Assert.True(long8.TrySetValue((short)-123)); Assert.Equal(new[] { 0UL }, long8.GetValue()); @@ -88,10 +88,25 @@ public void ExifLong8Array() Assert.Equal(ExifDataType.Long8, long8.DataType); } + [Fact] + public void ExifLong8Array_CanWriteValuesAsLong() + { + ExifLong8Array long8 = new(ExifTagValue.StripOffsets); + Assert.True(long8.TrySetValue(new long[] { 1, uint.MaxValue })); + Assert.Equal(ExifDataType.Long, long8.DataType); + + byte[] buffer = new byte[8]; + int written = ExifWriter.WriteValue(long8, buffer, 0); + + Assert.Equal(buffer.Length, written); + Assert.Equal(1U, BinaryPrimitives.ReadUInt32LittleEndian(buffer)); + Assert.Equal(uint.MaxValue, BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(4))); + } + [Fact] public void ExifSignedLong8Array() { - var long8 = new ExifSignedLong8Array(ExifTagValue.StripOffsets); + ExifSignedLong8Array long8 = new(ExifTagValue.StripOffsets); Assert.True(long8.TrySetValue(new[] { 0L })); Assert.Equal(new[] { 0L }, long8.GetValue()); @@ -105,9 +120,9 @@ public void ExifSignedLong8Array() [Fact] public void NotCoveredTags() { - using var input = new Image(10, 10); + using Image input = new(10, 10); - var testTags = new Dictionary + Dictionary testTags = new() { { new ExifTag((ExifTagValue)0xdd01), (ExifDataType.SingleFloat, new float[] { 1.2f, 2.3f, 4.5f }) }, { new ExifTag((ExifTagValue)0xdd02), (ExifDataType.SingleFloat, 2.345f) }, @@ -122,7 +137,7 @@ public void NotCoveredTags() }; // arrange - var values = new List(); + List values = new(); foreach (KeyValuePair tag in testTags) { ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); @@ -134,13 +149,13 @@ public void NotCoveredTags() input.Frames.RootFrame.Metadata.ExifProfile = new ExifProfile(values, Array.Empty()); // act - var encoder = new TiffEncoder(); - using var memStream = new MemoryStream(); + TiffEncoder encoder = new(); + using MemoryStream memStream = new(); input.Save(memStream, encoder); // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); ImageFrameMetadata loadedFrameMetadata = output.Frames.RootFrame.Metadata; foreach (KeyValuePair tag in testTags) { @@ -158,7 +173,7 @@ public void NotCoveredTags() [Fact] public void NotCoveredTags64bit() { - var testTags = new Dictionary + Dictionary testTags = new() { { new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, { new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, @@ -167,7 +182,7 @@ public void NotCoveredTags64bit() ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, }; - var values = new List(); + List values = new(); foreach (KeyValuePair tag in testTags) { ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); @@ -179,7 +194,7 @@ public void NotCoveredTags64bit() // act byte[] inputBytes = WriteIfdTags64Bit(values); Configuration config = Configuration.Default; - var reader = new EntryReader( + EntryReader reader = new( new MemoryStream(inputBytes), BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian, config.MemoryAllocator); @@ -205,8 +220,8 @@ public void NotCoveredTags64bit() private static byte[] WriteIfdTags64Bit(List values) { byte[] buffer = new byte[8]; - var ms = new MemoryStream(); - var writer = new TiffStreamWriter(ms); + MemoryStream ms = new(); + TiffStreamWriter writer = new(ms); WriteLong8(writer, buffer, (ulong)values.Count); foreach (IExifValue entry in values) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/CcittTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/CcittTiffCompressionTests.cs new file mode 100644 index 0000000000..b27fbbc2b4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/CcittTiffCompressionTests.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO.Compression; +using SixLabors.ImageSharp.Formats; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression; + +[Trait("Format", "Tiff")] +public class CcittTiffCompressionTests +{ + private const string T4PocGzipBase64 = + "H4sICBOogmoCA3BvYy10aWZmLXQ0LnRpZgDty7ENwjAQhlFfjKKUoYGSPlPQZopskGVYiJFgA2zpFGUG9Kz3S19xXtelTKX0xaVEq2dbZPcNUY+u2bVtzO7vmvd72+3095792vJyfsQH" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgH33fP9Zj5xoBYAEA"; + + private const string ModifiedHuffmanPocGzipBase64 = + "H4sICBSogmoCA3BvYy10aWZmLW1oLnRpZgDty7ENgzAQhlEfRBElNKRMnylomSIbZBkWYiTYAFs6RZkhetb7pa84r+urDKW0xa1EraUustu66L/dZ3d19+z2prz/1M0/fx/Z2zsvx2cc" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+jcL3Buw0IBYAEA"; + + private const string TiledT4ReporterPocGzipBase64 = + "H4sIAAAAAAACCu3UKw7CUBCG0RkuIeC6AxQGj8TwEHWshyWxOhI2QC8Z0QWAIDnNmeQXran4xnEf64jYROQyMvo8RtYepltk++x+rXabblW7P6fZ++fZvtS+T3et/djVR8M2n/Btr1v6C/zCQbQQLUQLRAvRQrRAtBAtRAtEC9FCtEC0EC3+Mlpv/6RrkiQmAAA="; + + [Theory] + [InlineData(T4PocGzipBase64)] + [InlineData(ModifiedHuffmanPocGzipBase64)] + public void Decode_CcittStripWithOversizedRun_ThrowsImageFormatException(string gzipBase64) + { + using MemoryStream tiffStream = CreateTiffStream(gzipBase64); + + ImageFormatException exception = Assert.Throws(() => Image.Load(tiffStream)); + + Assert.Equal("CCITT compression parsing error: decoded more pixels than the image width.", exception.Message); + } + + [Fact] + public void Decode_TiledCcittReporterSample_ThrowsImageFormatException() + { + // This is the reporter's exact tiled T4 TIFF sample, gzip-compressed only to keep the test source small. + using MemoryStream tiffStream = CreateTiffStream(TiledT4ReporterPocGzipBase64); + + ImageFormatException exception = Assert.Throws(() => Image.Load(tiffStream)); + + Assert.Equal("CCITT compression parsing error: decoded more pixels than the image width.", exception.Message); + } + + private static MemoryStream CreateTiffStream(string gzipBase64) + { + // Keep the TIFF payloads compressed so the regression source remains small. + byte[] compressed = Convert.FromBase64String(gzipBase64); + using MemoryStream compressedStream = new(compressed); + using GZipStream gzipStream = new(compressedStream, CompressionMode.Decompress); + MemoryStream tiffStream = new(); + + gzipStream.CopyTo(tiffStream); + tiffStream.Position = 0; + + return tiffStream; + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 1b12adac23..cc2faeab71 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -23,7 +23,7 @@ public void Compress_Decompress_Roundtrip_Works(byte[] data) using BufferedReadStream stream = CreateCompressedStream(data); byte[] buffer = new byte[data.Length]; - using var decompressor = new DeflateTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false); + using DeflateTiffCompression decompressor = new(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0, 0); decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 635a3a33e4..25c9633c14 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -37,7 +37,7 @@ public void Compress_Decompress_Roundtrip_Works(byte[] data) using BufferedReadStream stream = CreateCompressedStream(data); byte[] buffer = new byte[data.Length]; - using var decompressor = new LzwTiffCompression(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false); + using LzwTiffCompression decompressor = new(Configuration.Default.MemoryAllocator, 10, 8, TiffColorType.BlackIsZero8, TiffPredictor.None, false, false, 0, 0); decompressor.Decompress(stream, 0, (uint)stream.Length, 1, buffer, default); Assert.Equal(data, buffer); @@ -47,7 +47,7 @@ private static BufferedReadStream CreateCompressedStream(byte[] inputData) { Stream compressedStream = new MemoryStream(); - using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator)) + using (TiffLzwEncoder encoder = new(Configuration.Default.MemoryAllocator)) { encoder.Encode(inputData, compressedStream); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index e79ed7ce77..b911d1b177 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -14,11 +14,11 @@ public class NoneTiffCompressionTests [InlineData(new byte[] { 10, 15, 20, 25, 30, 35, 40, 45 }, 5, new byte[] { 10, 15, 20, 25, 30 })] public void Decompress_ReadsData(byte[] inputData, uint byteCount, byte[] expectedResult) { - using var memoryStream = new MemoryStream(inputData); - using var stream = new BufferedReadStream(Configuration.Default, memoryStream); + using MemoryStream memoryStream = new(inputData); + using BufferedReadStream stream = new(Configuration.Default, memoryStream); byte[] buffer = new byte[expectedResult.Length]; - using var decompressor = new NoneTiffCompression(default, default, default); + using NoneTiffCompression decompressor = new(default, default, default); decompressor.Decompress(stream, 0, byteCount, 1, buffer, default); Assert.Equal(expectedResult, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index c91ab0e7fe..8f71cdda74 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -22,11 +22,11 @@ public class PackBitsTiffCompressionTests [InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult) { - using var memoryStream = new MemoryStream(inputData); - using var stream = new BufferedReadStream(Configuration.Default, memoryStream); + using MemoryStream memoryStream = new(inputData); + using BufferedReadStream stream = new(Configuration.Default, memoryStream); byte[] buffer = new byte[expectedResult.Length]; - using var decompressor = new PackBitsTiffCompression(MemoryAllocator.Create(), default, default); + using PackBitsTiffCompression decompressor = new(MemoryAllocator.Create(), default, default); decompressor.Decompress(stream, 0, (uint)inputData.Length, 1, buffer, default); Assert.Equal(expectedResult, buffer); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 0fa7e01e8e..54c8172b86 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -20,100 +20,100 @@ public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase private static readonly Rgba32 Bit1 = new(255, 255, 255, 255); private static readonly byte[] BilevelBytes4X4 = - { + [ 0b01010000, 0b11110000, 0b01110000, 0b10010000 - }; + ]; private static readonly Rgba32[][] BilevelResult4X4 = - { - new[] { Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit1 }, - new[] { Bit1, Bit0, Bit0, Bit1 } - }; + [ + [Bit0, Bit1, Bit0, Bit1], + [Bit1, Bit1, Bit1, Bit1], + [Bit0, Bit1, Bit1, Bit1], + [Bit1, Bit0, Bit0, Bit1] + ]; private static readonly byte[] BilevelBytes12X4 = - { + [ 0b01010101, 0b01010000, 0b11111111, 0b11111111, 0b01101001, 0b10100000, 0b10010000, 0b01100000 - }; + ]; private static readonly Rgba32[][] BilevelResult12X4 = - { - new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, - new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 } - }; + [ + [Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1], + [Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1], + [Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0], + [Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0] + ]; private static readonly byte[] Grayscale4Bytes4X4 = - { + [ 0x8F, 0x0F, 0xFF, 0xFF, 0x08, 0x8F, 0xF0, 0xF8 - }; + ]; private static readonly Rgba32[][] Grayscale4Result4X4 = - { - new[] { Gray8, GrayF, Gray0, GrayF }, - new[] { GrayF, GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8, GrayF }, - new[] { GrayF, Gray0, GrayF, Gray8 } - }; + [ + [Gray8, GrayF, Gray0, GrayF], + [GrayF, GrayF, GrayF, GrayF], + [Gray0, Gray8, Gray8, GrayF], + [GrayF, Gray0, GrayF, Gray8] + ]; private static readonly byte[] Grayscale4Bytes3X4 = - { + [ 0x8F, 0x00, 0xFF, 0xF0, 0x08, 0x80, 0xF0, 0xF0 - }; + ]; private static readonly Rgba32[][] Grayscale4Result3X4 = - { - new[] { Gray8, GrayF, Gray0 }, - new[] { GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8 }, - new[] { GrayF, Gray0, GrayF } - }; + [ + [Gray8, GrayF, Gray0], + [GrayF, GrayF, GrayF], + [Gray0, Gray8, Gray8], + [GrayF, Gray0, GrayF] + ]; private static readonly byte[] Grayscale8Bytes4X4 = - { + [ 128, 255, 000, 255, 255, 255, 255, 255, 000, 128, 128, 255, 255, 000, 255, 128 - }; + ]; private static readonly Rgba32[][] Grayscale8Result4X4 = - { - new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 } - }; + [ + [Gray128, Gray255, Gray000, Gray255], + [Gray255, Gray255, Gray255, Gray255], + [Gray000, Gray128, Gray128, Gray255], + [Gray255, Gray000, Gray255, Gray128] + ]; public static IEnumerable BilevelData { get { - yield return new object[] { BilevelBytes4X4, 1, 0, 0, 4, 4, BilevelResult4X4 }; - yield return new object[] { BilevelBytes4X4, 1, 0, 0, 4, 4, Offset(BilevelResult4X4, 0, 0, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 1, 0, 4, 4, Offset(BilevelResult4X4, 1, 0, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 0, 1, 4, 4, Offset(BilevelResult4X4, 0, 1, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 1, 1, 4, 4, Offset(BilevelResult4X4, 1, 1, 6, 6) }; - - yield return new object[] { BilevelBytes12X4, 1, 0, 0, 12, 4, BilevelResult12X4 }; - yield return new object[] { BilevelBytes12X4, 1, 0, 0, 12, 4, Offset(BilevelResult12X4, 0, 0, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 1, 0, 12, 4, Offset(BilevelResult12X4, 1, 0, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 0, 1, 12, 4, Offset(BilevelResult12X4, 0, 1, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 1, 1, 12, 4, Offset(BilevelResult12X4, 1, 1, 18, 6) }; + yield return [BilevelBytes4X4, 1, 0, 0, 4, 4, BilevelResult4X4]; + yield return [BilevelBytes4X4, 1, 0, 0, 4, 4, Offset(BilevelResult4X4, 0, 0, 6, 6)]; + yield return [BilevelBytes4X4, 1, 1, 0, 4, 4, Offset(BilevelResult4X4, 1, 0, 6, 6)]; + yield return [BilevelBytes4X4, 1, 0, 1, 4, 4, Offset(BilevelResult4X4, 0, 1, 6, 6)]; + yield return [BilevelBytes4X4, 1, 1, 1, 4, 4, Offset(BilevelResult4X4, 1, 1, 6, 6)]; + + yield return [BilevelBytes12X4, 1, 0, 0, 12, 4, BilevelResult12X4]; + yield return [BilevelBytes12X4, 1, 0, 0, 12, 4, Offset(BilevelResult12X4, 0, 0, 18, 6)]; + yield return [BilevelBytes12X4, 1, 1, 0, 12, 4, Offset(BilevelResult12X4, 1, 0, 18, 6)]; + yield return [BilevelBytes12X4, 1, 0, 1, 12, 4, Offset(BilevelResult12X4, 0, 1, 18, 6)]; + yield return [BilevelBytes12X4, 1, 1, 1, 12, 4, Offset(BilevelResult12X4, 1, 1, 18, 6)]; } } @@ -121,17 +121,17 @@ public static IEnumerable Grayscale4_Data { get { - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Grayscale4Result4X4 }; - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Offset(Grayscale4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 1, 0, 4, 4, Offset(Grayscale4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 1, 4, 4, Offset(Grayscale4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 1, 1, 4, 4, Offset(Grayscale4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Grayscale4Result3X4 }; - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Offset(Grayscale4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 1, 0, 3, 4, Offset(Grayscale4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 1, 3, 4, Offset(Grayscale4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 1, 1, 3, 4, Offset(Grayscale4Result3X4, 1, 1, 6, 6) }; + yield return [Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Grayscale4Result4X4]; + yield return [Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Offset(Grayscale4Result4X4, 0, 0, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 1, 0, 4, 4, Offset(Grayscale4Result4X4, 1, 0, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 0, 1, 4, 4, Offset(Grayscale4Result4X4, 0, 1, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 1, 1, 4, 4, Offset(Grayscale4Result4X4, 1, 1, 6, 6)]; + + yield return [Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Grayscale4Result3X4]; + yield return [Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Offset(Grayscale4Result3X4, 0, 0, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 1, 0, 3, 4, Offset(Grayscale4Result3X4, 1, 0, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 0, 1, 3, 4, Offset(Grayscale4Result3X4, 0, 1, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 1, 1, 3, 4, Offset(Grayscale4Result3X4, 1, 1, 6, 6)]; } } @@ -139,11 +139,11 @@ public static IEnumerable Grayscale8_Data { get { - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Grayscale8Result4X4 }; - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Offset(Grayscale8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 1, 0, 4, 4, Offset(Grayscale8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 1, 4, 4, Offset(Grayscale8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 1, 1, 4, 4, Offset(Grayscale8Result4X4, 1, 1, 6, 6) }; + yield return [Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Grayscale8Result4X4]; + yield return [Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Offset(Grayscale8Result4X4, 0, 0, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 1, 0, 4, 4, Offset(Grayscale8Result4X4, 1, 0, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 0, 1, 4, 4, Offset(Grayscale8Result4X4, 0, 1, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 1, 1, 4, 4, Offset(Grayscale8Result4X4, 1, 1, 6, 6)]; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index c809c6c7f9..dbfa00950c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -15,39 +15,43 @@ public class PaletteTiffColorTests : PhotometricInterpretationTestBase public static ushort[] Palette4ColorMap => GenerateColorMap(Palette4ColorPalette); private static readonly byte[] Palette4Bytes4X4 = - { + [ 0x01, 0x23, 0x4A, 0xD2, 0x12, 0x34, 0xAB, 0xEF - }; + ]; private static readonly Rgba32[][] Palette4Result4X4 = GenerateResult( Palette4ColorPalette, - new[] { new[] { 0x00, 0x01, 0x02, 0x03 }, new[] { 0x04, 0x0A, 0x0D, 0x02 }, new[] { 0x01, 0x02, 0x03, 0x04 }, new[] { 0x0A, 0x0B, 0x0E, 0x0F } }); + [ + [0x00, 0x01, 0x02, 0x03], [0x04, 0x0A, 0x0D, 0x02], [0x01, 0x02, 0x03, 0x04], [0x0A, 0x0B, 0x0E, 0x0F] + ]); private static readonly byte[] Palette4Bytes3X4 = - { + [ 0x01, 0x20, 0x4A, 0xD0, 0x12, 0x30, 0xAB, 0xE0 - }; + ]; - private static readonly Rgba32[][] Palette4Result3X4 = GenerateResult(Palette4ColorPalette, new[] { new[] { 0x00, 0x01, 0x02 }, new[] { 0x04, 0x0A, 0x0D }, new[] { 0x01, 0x02, 0x03 }, new[] { 0x0A, 0x0B, 0x0E } }); + private static readonly Rgba32[][] Palette4Result3X4 = GenerateResult(Palette4ColorPalette, [ + [0x00, 0x01, 0x02], [0x04, 0x0A, 0x0D], [0x01, 0x02, 0x03], [0x0A, 0x0B, 0x0E] + ]); public static IEnumerable Palette4Data { get { - yield return new object[] { Palette4Bytes4X4, 4, Palette4ColorMap, 0, 0, 4, 4, Palette4Result4X4 }; - yield return new object[] { Palette4Bytes4X4, 4, Palette4ColorMap, 0, 0, 4, 4, Offset(Palette4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Palette4Bytes4X4, 4, Palette4ColorMap, 1, 0, 4, 4, Offset(Palette4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Palette4Bytes4X4, 4, Palette4ColorMap, 0, 1, 4, 4, Offset(Palette4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Palette4Bytes4X4, 4, Palette4ColorMap, 1, 1, 4, 4, Offset(Palette4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Palette4Bytes3X4, 4, Palette4ColorMap, 0, 0, 3, 4, Palette4Result3X4 }; - yield return new object[] { Palette4Bytes3X4, 4, Palette4ColorMap, 0, 0, 3, 4, Offset(Palette4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Palette4Bytes3X4, 4, Palette4ColorMap, 1, 0, 3, 4, Offset(Palette4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Palette4Bytes3X4, 4, Palette4ColorMap, 0, 1, 3, 4, Offset(Palette4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Palette4Bytes3X4, 4, Palette4ColorMap, 1, 1, 3, 4, Offset(Palette4Result3X4, 1, 1, 6, 6) }; + yield return [Palette4Bytes4X4, 4, Palette4ColorMap, 0, 0, 4, 4, Palette4Result4X4]; + yield return [Palette4Bytes4X4, 4, Palette4ColorMap, 0, 0, 4, 4, Offset(Palette4Result4X4, 0, 0, 6, 6)]; + yield return [Palette4Bytes4X4, 4, Palette4ColorMap, 1, 0, 4, 4, Offset(Palette4Result4X4, 1, 0, 6, 6)]; + yield return [Palette4Bytes4X4, 4, Palette4ColorMap, 0, 1, 4, 4, Offset(Palette4Result4X4, 0, 1, 6, 6)]; + yield return [Palette4Bytes4X4, 4, Palette4ColorMap, 1, 1, 4, 4, Offset(Palette4Result4X4, 1, 1, 6, 6)]; + + yield return [Palette4Bytes3X4, 4, Palette4ColorMap, 0, 0, 3, 4, Palette4Result3X4]; + yield return [Palette4Bytes3X4, 4, Palette4ColorMap, 0, 0, 3, 4, Offset(Palette4Result3X4, 0, 0, 6, 6)]; + yield return [Palette4Bytes3X4, 4, Palette4ColorMap, 1, 0, 3, 4, Offset(Palette4Result3X4, 1, 0, 6, 6)]; + yield return [Palette4Bytes3X4, 4, Palette4ColorMap, 0, 1, 3, 4, Offset(Palette4Result3X4, 0, 1, 6, 6)]; + yield return [Palette4Bytes3X4, 4, Palette4ColorMap, 1, 1, 3, 4, Offset(Palette4Result3X4, 1, 1, 6, 6)]; } } @@ -56,24 +60,26 @@ public static IEnumerable Palette4Data public static ushort[] Palette8ColorMap => GenerateColorMap(Palette8ColorPalette); private static readonly byte[] Palette8Bytes4X4 = - { + [ 000, 001, 002, 003, 100, 110, 120, 130, 000, 255, 128, 255, 050, 100, 150, 200 - }; + ]; - private static readonly Rgba32[][] Palette8Result4X4 = GenerateResult(Palette8ColorPalette, new[] { new[] { 000, 001, 002, 003 }, new[] { 100, 110, 120, 130 }, new[] { 000, 255, 128, 255 }, new[] { 050, 100, 150, 200 } }); + private static readonly Rgba32[][] Palette8Result4X4 = GenerateResult(Palette8ColorPalette, [ + [000, 001, 002, 003], [100, 110, 120, 130], [000, 255, 128, 255], [050, 100, 150, 200] + ]); public static IEnumerable Palette8Data { get { - yield return new object[] { Palette8Bytes4X4, 8, Palette8ColorMap, 0, 0, 4, 4, Palette8Result4X4 }; - yield return new object[] { Palette8Bytes4X4, 8, Palette8ColorMap, 0, 0, 4, 4, Offset(Palette8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Palette8Bytes4X4, 8, Palette8ColorMap, 1, 0, 4, 4, Offset(Palette8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Palette8Bytes4X4, 8, Palette8ColorMap, 0, 1, 4, 4, Offset(Palette8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Palette8Bytes4X4, 8, Palette8ColorMap, 1, 1, 4, 4, Offset(Palette8Result4X4, 1, 1, 6, 6) }; + yield return [Palette8Bytes4X4, 8, Palette8ColorMap, 0, 0, 4, 4, Palette8Result4X4]; + yield return [Palette8Bytes4X4, 8, Palette8ColorMap, 0, 0, 4, 4, Offset(Palette8Result4X4, 0, 0, 6, 6)]; + yield return [Palette8Bytes4X4, 8, Palette8ColorMap, 1, 0, 4, 4, Offset(Palette8Result4X4, 1, 0, 6, 6)]; + yield return [Palette8Bytes4X4, 8, Palette8ColorMap, 0, 1, 4, 4, Offset(Palette8Result4X4, 0, 1, 6, 6)]; + yield return [Palette8Bytes4X4, 8, Palette8ColorMap, 1, 1, 4, 4, Offset(Palette8Result4X4, 1, 1, 6, 6)]; } } @@ -83,16 +89,20 @@ public static IEnumerable Palette8Data public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) => AssertDecode(expectedResult, pixels => { - new PaletteTiffColor(new TiffBitsPerSample(bitsPerSample, 0, 0), colorMap).Decode(inputData, pixels, left, top, width, height); + new PaletteTiffColor( + new TiffBitsPerSample(bitsPerSample, 0, 0), + colorMap, + TiffExtraSampleType.UnspecifiedData) + .Decode(inputData, pixels, left, top, width, height); }); private static uint[][] GeneratePalette(int count) { - var palette = new uint[count][]; + uint[][] palette = new uint[count][]; for (uint i = 0; i < count; i++) { - palette[i] = new[] { (i * 2u) % 65536u, (i * 2625u) % 65536u, (i * 29401u) % 65536u }; + palette[i] = [(i * 2u) % 65536u, (i * 2625u) % 65536u, (i * 29401u) % 65536u]; } return palette; @@ -101,7 +111,7 @@ private static uint[][] GeneratePalette(int count) private static ushort[] GenerateColorMap(uint[][] colorPalette) { int colorCount = colorPalette.Length; - var colorMap = new ushort[colorCount * 3]; + ushort[] colorMap = new ushort[colorCount * 3]; for (int i = 0; i < colorCount; i++) { @@ -115,7 +125,7 @@ private static ushort[] GenerateColorMap(uint[][] colorPalette) private static Rgba32[][] GenerateResult(uint[][] colorPalette, int[][] pixelLookup) { - var result = new Rgba32[pixelLookup.Length][]; + Rgba32[][] result = new Rgba32[pixelLookup.Length][]; for (int y = 0; y < pixelLookup.Length; y++) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs index b174403839..7040e167df 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PhotometricInterpretationTestBase.cs @@ -10,14 +10,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation; [Trait("Format", "Tiff")] public abstract class PhotometricInterpretationTestBase { - public static Rgba32 DefaultColor = new Rgba32(42, 96, 18, 128); + public static Rgba32 DefaultColor = new(42, 96, 18, 128); public static Rgba32[][] Offset(Rgba32[][] input, int xOffset, int yOffset, int width, int height) { int inputHeight = input.Length; int inputWidth = input[0].Length; - var output = new Rgba32[height][]; + Rgba32[][] output = new Rgba32[height][]; for (int y = 0; y < output.Length; y++) { @@ -45,9 +45,9 @@ internal static void AssertDecode(Rgba32[][] expectedResult, Action(resultWidth, resultHeight)) + using (Image image = new(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/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index d8249c3619..5df7c4d62f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -12,226 +12,230 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation; [Trait("Format", "Tiff")] public class RgbPlanarTiffColorTests : PhotometricInterpretationTestBase { - private static readonly Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); - private static readonly Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); - private static readonly Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); - private static readonly Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); - private static readonly Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); - private static readonly Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); - private static readonly Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); + private static readonly Rgba32 Rgb4_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb4_444 = new(68, 68, 68, 255); + private static readonly Rgba32 Rgb4_888 = new(136, 136, 136, 255); + private static readonly Rgba32 Rgb4_CCC = new(204, 204, 204, 255); + private static readonly Rgba32 Rgb4_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb4_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb4_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb4_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb4_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb4_400 = new(68, 0, 0, 255); + private static readonly Rgba32 Rgb4_800 = new(136, 0, 0, 255); + private static readonly Rgba32 Rgb4_C00 = new(204, 0, 0, 255); + private static readonly Rgba32 Rgb4_48C = new(68, 136, 204, 255); private static readonly byte[] Rgb4Bytes4X4R = - { + [ 0x0F, 0x0F, 0xF0, 0x0F, 0x48, 0xC4, 0x04, 0x8C - }; + ]; private static readonly byte[] Rgb4Bytes4X4G = - { + [ 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x08, 0x04, 0x8C - }; + ]; private static readonly byte[] Rgb4Bytes4X4B = - { + [ 0x0F, 0x0F, 0x00, 0xFF, 0x00, 0x0C, 0x04, 0x8C - }; + ]; - private static readonly byte[][] Rgb4Bytes4X4 = { Rgb4Bytes4X4R, Rgb4Bytes4X4G, Rgb4Bytes4X4B }; + private static readonly byte[][] Rgb4Bytes4X4 = [Rgb4Bytes4X4R, Rgb4Bytes4X4G, Rgb4Bytes4X4B]; private static readonly Rgba32[][] Rgb4Result4X4 = - { - new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC } - }; + [ + [Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF], + [Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F], + [Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C], + [Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC] + ]; private static readonly byte[] Rgb4Bytes3X4R = - { + [ 0x0F, 0x00, 0xF0, 0x00, 0x48, 0xC0, 0x04, 0x80 - }; + ]; private static readonly byte[] Rgb4Bytes3X4G = - { + [ 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x04, 0x80 - }; + ]; private static readonly byte[] Rgb4Bytes3X4B = - { + [ 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x04, 0x80 - }; + ]; - private static readonly byte[][] Rgb4Bytes3X4 = { Rgb4Bytes3X4R, Rgb4Bytes3X4G, Rgb4Bytes3X4B }; + private static readonly byte[][] Rgb4Bytes3X4 = [Rgb4Bytes3X4R, Rgb4Bytes3X4G, Rgb4Bytes3X4B]; private static readonly Rgba32[][] Rgb4Result3X4 = - { - new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888 } - }; + [ + [Rgb4_000, Rgb4_FFF, Rgb4_000], + [Rgb4_F00, Rgb4_0F0, Rgb4_00F], + [Rgb4_400, Rgb4_800, Rgb4_C00], + [Rgb4_000, Rgb4_444, Rgb4_888] + ]; public static IEnumerable Rgb4Data { get { - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4 }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4 }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6)]; + + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6)]; } } - private static readonly Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); - private static readonly Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); - private static readonly Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); - private static readonly Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); - private static readonly Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); - private static readonly Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); - private static readonly Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); + private static readonly Rgba32 Rgb8_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb8_444 = new(64, 64, 64, 255); + private static readonly Rgba32 Rgb8_888 = new(128, 128, 128, 255); + private static readonly Rgba32 Rgb8_CCC = new(192, 192, 192, 255); + private static readonly Rgba32 Rgb8_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb8_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb8_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb8_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb8_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb8_400 = new(64, 0, 0, 255); + private static readonly Rgba32 Rgb8_800 = new(128, 0, 0, 255); + private static readonly Rgba32 Rgb8_C00 = new(192, 0, 0, 255); + private static readonly Rgba32 Rgb8_48C = new(64, 128, 192, 255); private static readonly byte[] Rgb8Bytes4X4R = - { + [ 000, 255, 000, 255, 255, 000, 000, 255, 064, 128, 192, 064, 000, 064, 128, 192 - }; + ]; private static readonly byte[] Rgb8Bytes4X4G = - { + [ 000, 255, 000, 255, 000, 255, 000, 000, 000, 000, 000, 128, 000, 064, 128, 192 - }; + ]; private static readonly byte[] Rgb8Bytes4X4B = - { + [ 000, 255, 000, 255, 000, 000, 255, 255, 000, 000, 000, 192, 000, 064, 128, 192 - }; + ]; private static readonly byte[][] Rgb8Bytes4X4 = - { + [ Rgb8Bytes4X4R, Rgb8Bytes4X4G, Rgb8Bytes4X4B - }; + ]; private static readonly Rgba32[][] Rgb8Result4X4 = - { - new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, - new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, - new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, - new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC } - }; + [ + [Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF], + [Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F], + [Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C], + [Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC] + ]; public static IEnumerable Rgb8Data { get { - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4 }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6)]; } } - private static readonly Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); - private static readonly Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); - private static readonly Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); - private static readonly Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); - private static readonly Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); - private static readonly Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); - private static readonly Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); + private static readonly Rgba32 Rgb484_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb484_444 = new(68, 64, 68, 255); + private static readonly Rgba32 Rgb484_888 = new(136, 128, 136, 255); + private static readonly Rgba32 Rgb484_CCC = new(204, 192, 204, 255); + private static readonly Rgba32 Rgb484_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb484_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb484_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb484_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb484_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb484_400 = new(68, 0, 0, 255); + private static readonly Rgba32 Rgb484_800 = new(136, 0, 0, 255); + private static readonly Rgba32 Rgb484_C00 = new(204, 0, 0, 255); + private static readonly Rgba32 Rgb484_48C = new(68, 128, 204, 255); private static readonly byte[] Rgb484Bytes4X4R = - { + [ 0x0F, 0x0F, 0xF0, 0x0F, 0x48, 0xC4, 0x04, 0x8C - }; + ]; private static readonly byte[] Rgb484Bytes4X4G = - { + [ 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x80, 0xC0 - }; + ]; private static readonly byte[] Rgb484Bytes4X4B = - { + [ 0x0F, 0x0F, 0x00, 0xFF, 0x00, 0x0C, 0x04, 0x8C - }; + ]; private static readonly Rgba32[][] Rgb484Result4X4 = - { - new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, - new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, - new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, - new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC } - }; + [ + [Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF], + [Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F], + [Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C], + [Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC] + ]; - private static readonly byte[][] Rgb484Bytes4X4 = { Rgb484Bytes4X4R, Rgb484Bytes4X4G, Rgb484Bytes4X4B }; + private static readonly byte[][] Rgb484Bytes4X4 = [Rgb484Bytes4X4R, Rgb484Bytes4X4G, Rgb484Bytes4X4B]; public static IEnumerable Rgb484_Data { get { - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4 }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) + ]; } } @@ -251,7 +255,7 @@ public void Decode_WritesPixelData( expectedResult, pixels => { - var buffers = new IMemoryOwner[inputData.Length]; + IMemoryOwner[] buffers = new IMemoryOwner[inputData.Length]; for (int i = 0; i < buffers.Length; i++) { buffers[i] = Configuration.Default.MemoryAllocator.Allocate(inputData[i].Length); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index aeb135773c..6935d504ed 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -10,151 +10,155 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation; [Trait("Format", "Tiff")] public class RgbTiffColorTests : PhotometricInterpretationTestBase { - private static readonly Rgba32 Rgb4_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb4_444 = new Rgba32(68, 68, 68, 255); - private static readonly Rgba32 Rgb4_888 = new Rgba32(136, 136, 136, 255); - private static readonly Rgba32 Rgb4_CCC = new Rgba32(204, 204, 204, 255); - private static readonly Rgba32 Rgb4_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb4_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb4_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb4_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb4_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb4_400 = new Rgba32(68, 0, 0, 255); - private static readonly Rgba32 Rgb4_800 = new Rgba32(136, 0, 0, 255); - private static readonly Rgba32 Rgb4_C00 = new Rgba32(204, 0, 0, 255); - private static readonly Rgba32 Rgb4_48C = new Rgba32(68, 136, 204, 255); + private static readonly Rgba32 Rgb4_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb4_444 = new(68, 68, 68, 255); + private static readonly Rgba32 Rgb4_888 = new(136, 136, 136, 255); + private static readonly Rgba32 Rgb4_CCC = new(204, 204, 204, 255); + private static readonly Rgba32 Rgb4_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb4_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb4_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb4_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb4_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb4_400 = new(68, 0, 0, 255); + private static readonly Rgba32 Rgb4_800 = new(136, 0, 0, 255); + private static readonly Rgba32 Rgb4_C00 = new(204, 0, 0, 255); + private static readonly Rgba32 Rgb4_48C = new(68, 136, 204, 255); private static readonly byte[] Rgb4Bytes4X4 = - { + [ 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0x0F, 0x40, 0x08, 0x00, 0xC0, 0x04, 0x8C, 0x00, 0x04, 0x44, 0x88, 0x8C, 0xCC - }; + ]; private static readonly Rgba32[][] Rgb4Result4X4 = - { - new[] { Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC } - }; + [ + [Rgb4_000, Rgb4_FFF, Rgb4_000, Rgb4_FFF], + [Rgb4_F00, Rgb4_0F0, Rgb4_00F, Rgb4_F0F], + [Rgb4_400, Rgb4_800, Rgb4_C00, Rgb4_48C], + [Rgb4_000, Rgb4_444, Rgb4_888, Rgb4_CCC] + ]; private static readonly byte[] Rgb4Bytes3X4 = - { + [ 0x00, 0x0F, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x40, 0x08, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x44, 0x88, 0x80 - }; + ]; private static readonly Rgba32[][] Rgb4Result3X4 = - { - new[] { Rgb4_000, Rgb4_FFF, Rgb4_000 }, - new[] { Rgb4_F00, Rgb4_0F0, Rgb4_00F }, - new[] { Rgb4_400, Rgb4_800, Rgb4_C00 }, - new[] { Rgb4_000, Rgb4_444, Rgb4_888 } - }; + [ + [Rgb4_000, Rgb4_FFF, Rgb4_000], + [Rgb4_F00, Rgb4_0F0, Rgb4_00F], + [Rgb4_400, Rgb4_800, Rgb4_C00], + [Rgb4_000, Rgb4_444, Rgb4_888] + ]; public static IEnumerable Rgb4Data { get { - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4 }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4 }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6)]; + yield return [Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6)]; + + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6)]; + yield return [Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6)]; } } - private static readonly Rgba32 Rgb8_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb8_444 = new Rgba32(64, 64, 64, 255); - private static readonly Rgba32 Rgb8_888 = new Rgba32(128, 128, 128, 255); - private static readonly Rgba32 Rgb8_CCC = new Rgba32(192, 192, 192, 255); - private static readonly Rgba32 Rgb8_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb8_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb8_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb8_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb8_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb8_400 = new Rgba32(64, 0, 0, 255); - private static readonly Rgba32 Rgb8_800 = new Rgba32(128, 0, 0, 255); - private static readonly Rgba32 Rgb8_C00 = new Rgba32(192, 0, 0, 255); - private static readonly Rgba32 Rgb8_48C = new Rgba32(64, 128, 192, 255); + private static readonly Rgba32 Rgb8_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb8_444 = new(64, 64, 64, 255); + private static readonly Rgba32 Rgb8_888 = new(128, 128, 128, 255); + private static readonly Rgba32 Rgb8_CCC = new(192, 192, 192, 255); + private static readonly Rgba32 Rgb8_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb8_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb8_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb8_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb8_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb8_400 = new(64, 0, 0, 255); + private static readonly Rgba32 Rgb8_800 = new(128, 0, 0, 255); + private static readonly Rgba32 Rgb8_C00 = new(192, 0, 0, 255); + private static readonly Rgba32 Rgb8_48C = new(64, 128, 192, 255); private static readonly byte[] Rgb8Bytes4X4 = - { + [ 000, 000, 000, 255, 255, 255, 000, 000, 000, 255, 255, 255, 255, 000, 000, 000, 255, 000, 000, 000, 255, 255, 000, 255, 064, 000, 000, 128, 000, 000, 192, 000, 000, 064, 128, 192, 000, 000, 000, 064, 064, 064, 128, 128, 128, 192, 192, 192 - }; + ]; private static readonly Rgba32[][] Rgb8Result4X4 = - { - new[] { Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF }, - new[] { Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F }, - new[] { Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C }, - new[] { Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC } - }; + [ + [Rgb8_000, Rgb8_FFF, Rgb8_000, Rgb8_FFF], + [Rgb8_F00, Rgb8_0F0, Rgb8_00F, Rgb8_F0F], + [Rgb8_400, Rgb8_800, Rgb8_C00, Rgb8_48C], + [Rgb8_000, Rgb8_444, Rgb8_888, Rgb8_CCC] + ]; public static IEnumerable Rgb8Data { get { - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4 }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6)]; + yield return [Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6)]; } } - private static readonly Rgba32 Rgb484_000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Rgb484_444 = new Rgba32(68, 64, 68, 255); - private static readonly Rgba32 Rgb484_888 = new Rgba32(136, 128, 136, 255); - private static readonly Rgba32 Rgb484_CCC = new Rgba32(204, 192, 204, 255); - private static readonly Rgba32 Rgb484_FFF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Rgb484_F00 = new Rgba32(255, 0, 0, 255); - private static readonly Rgba32 Rgb484_0F0 = new Rgba32(0, 255, 0, 255); - private static readonly Rgba32 Rgb484_00F = new Rgba32(0, 0, 255, 255); - private static readonly Rgba32 Rgb484_F0F = new Rgba32(255, 0, 255, 255); - private static readonly Rgba32 Rgb484_400 = new Rgba32(68, 0, 0, 255); - private static readonly Rgba32 Rgb484_800 = new Rgba32(136, 0, 0, 255); - private static readonly Rgba32 Rgb484_C00 = new Rgba32(204, 0, 0, 255); - private static readonly Rgba32 Rgb484_48C = new Rgba32(68, 128, 204, 255); + private static readonly Rgba32 Rgb484_000 = new(0, 0, 0, 255); + private static readonly Rgba32 Rgb484_444 = new(68, 64, 68, 255); + private static readonly Rgba32 Rgb484_888 = new(136, 128, 136, 255); + private static readonly Rgba32 Rgb484_CCC = new(204, 192, 204, 255); + private static readonly Rgba32 Rgb484_FFF = new(255, 255, 255, 255); + private static readonly Rgba32 Rgb484_F00 = new(255, 0, 0, 255); + private static readonly Rgba32 Rgb484_0F0 = new(0, 255, 0, 255); + private static readonly Rgba32 Rgb484_00F = new(0, 0, 255, 255); + private static readonly Rgba32 Rgb484_F0F = new(255, 0, 255, 255); + private static readonly Rgba32 Rgb484_400 = new(68, 0, 0, 255); + private static readonly Rgba32 Rgb484_800 = new(136, 0, 0, 255); + private static readonly Rgba32 Rgb484_C00 = new(204, 0, 0, 255); + private static readonly Rgba32 Rgb484_48C = new(68, 128, 204, 255); private static readonly byte[] Rgb484Bytes4X4 = - { + [ 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0xF0, 0x00, 0x0F, 0xF0, 0x0F, 0x40, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x48, 0x0C, 0x00, 0x00, 0x44, 0x04, 0x88, 0x08, 0xCC, 0x0C - }; + ]; private static readonly Rgba32[][] Rgb484Result4X4 = - { - new[] { Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF }, - new[] { Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F }, - new[] { Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C }, - new[] { Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC } - }; + [ + [Rgb484_000, Rgb484_FFF, Rgb484_000, Rgb484_FFF], + [Rgb484_F00, Rgb484_0F0, Rgb484_00F, Rgb484_F0F], + [Rgb484_400, Rgb484_800, Rgb484_C00, Rgb484_48C], + [Rgb484_000, Rgb484_444, Rgb484_888, Rgb484_CCC] + ]; public static IEnumerable Rgb484Data { get { - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4 }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) + ]; + yield return [Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) + ]; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbaTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbaTiffColorTests.cs new file mode 100644 index 0000000000..6a0afcd859 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbaTiffColorTests.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation; + +[Trait("Format", "Tiff")] +public class RgbaTiffColorTests : PhotometricInterpretationTestBase +{ + [Fact] + public void DecodeUsesAlphaChannelBitDepth() + { + // The single pixel packs 4-bit RGB followed by 8-bit alpha. Using the blue-channel width for alpha would consume + // only the first alpha nibble and leave the decoded opacity at 8 instead of 128. + byte[] input = [0xF0, 0x88, 0x00]; + Rgba32[][] expected = [[new Rgba32(255, 0, 136, 128)]]; + + AssertDecode(expected, pixels => + { + RgbaTiffColor color = new(TiffExtraSampleType.UnassociatedAlphaData, new TiffBitsPerSample(4, 4, 4, 8)); + color.Decode(input, pixels, 0, 0, 1, 1); + }); + } + + [Fact] + public void DecodeAssociatedAlphaRestoresLogicalColor() + { + byte[] input = [64, 32, 16, 128]; + Rgba32[][] expected = [[new Rgba32(128, 64, 32, 128)]]; + + AssertDecode(expected, pixels => + { + RgbaTiffColor color = new(TiffExtraSampleType.AssociatedAlphaData, new TiffBitsPerSample(8, 8, 8, 8)); + color.Decode(input, pixels, 0, 0, 1, 1); + }); + } + + [Fact] + public void DecodeUnassociatedAlphaAssociatesDestinationStorage() + { + byte[] input = [128, 64, 32, 128]; + using Image image = new(1, 1); + + RgbaTiffColor color = new(TiffExtraSampleType.UnassociatedAlphaData, new TiffBitsPerSample(8, 8, 8, 8)); + color.Decode(input, image.Frames.RootFrame.PixelBuffer, 0, 0, 1, 1); + + // The decoder receives straight TIFF channels, while Rgba32P stores RGB multiplied by its quantized alpha byte. + Assert.Equal(new Rgba32P(64, 32, 16, 128), image[0, 0]); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 0b58e3891e..7a09bfae7f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -10,110 +10,110 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation; [Trait("Format", "Tiff")] public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static readonly Rgba32 Gray000 = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Gray128 = new Rgba32(127, 127, 127, 255); - private static readonly Rgba32 Gray255 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Gray0 = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Gray8 = new Rgba32(119, 119, 119, 255); - private static readonly Rgba32 GrayF = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Bit0 = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Bit1 = new Rgba32(0, 0, 0, 255); + private static readonly Rgba32 Gray000 = new(255, 255, 255, 255); + private static readonly Rgba32 Gray128 = new(127, 127, 127, 255); + private static readonly Rgba32 Gray255 = new(0, 0, 0, 255); + private static readonly Rgba32 Gray0 = new(255, 255, 255, 255); + private static readonly Rgba32 Gray8 = new(119, 119, 119, 255); + private static readonly Rgba32 GrayF = new(0, 0, 0, 255); + private static readonly Rgba32 Bit0 = new(255, 255, 255, 255); + private static readonly Rgba32 Bit1 = new(0, 0, 0, 255); private static readonly byte[] BilevelBytes4X4 = - { + [ 0b01010000, 0b11110000, 0b01110000, 0b10010000 - }; + ]; private static readonly Rgba32[][] BilevelResult4X4 = - { - new[] { Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit1 }, - new[] { Bit1, Bit0, Bit0, Bit1 } - }; + [ + [Bit0, Bit1, Bit0, Bit1], + [Bit1, Bit1, Bit1, Bit1], + [Bit0, Bit1, Bit1, Bit1], + [Bit1, Bit0, Bit0, Bit1] + ]; private static readonly byte[] BilevelBytes12X4 = - { + [ 0b01010101, 0b01010000, 0b11111111, 0b11111111, 0b01101001, 0b10100000, 0b10010000, 0b01100000 - }; + ]; private static readonly Rgba32[][] BilevelResult12X4 = - { - new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, - new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, - new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, - new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 } - }; + [ + [Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1], + [Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1], + [Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0], + [Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0] + ]; private static readonly byte[] Grayscale4Bytes4X4 = - { + [ 0x8F, 0x0F, 0xFF, 0xFF, 0x08, 0x8F, 0xF0, 0xF8 - }; + ]; private static readonly Rgba32[][] Grayscale4Result4X4 = - { - new[] { Gray8, GrayF, Gray0, GrayF }, - new[] { GrayF, GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8, GrayF }, - new[] { GrayF, Gray0, GrayF, Gray8 } - }; + [ + [Gray8, GrayF, Gray0, GrayF], + [GrayF, GrayF, GrayF, GrayF], + [Gray0, Gray8, Gray8, GrayF], + [GrayF, Gray0, GrayF, Gray8] + ]; private static readonly byte[] Grayscale4Bytes3X4 = - { + [ 0x8F, 0x00, 0xFF, 0xF0, 0x08, 0x80, 0xF0, 0xF0 - }; + ]; private static readonly Rgba32[][] Grayscale4Result3X4 = - { - new[] { Gray8, GrayF, Gray0 }, - new[] { GrayF, GrayF, GrayF }, - new[] { Gray0, Gray8, Gray8 }, - new[] { GrayF, Gray0, GrayF } - }; + [ + [Gray8, GrayF, Gray0], + [GrayF, GrayF, GrayF], + [Gray0, Gray8, Gray8], + [GrayF, Gray0, GrayF] + ]; private static readonly byte[] Grayscale8Bytes4X4 = - { + [ 128, 255, 000, 255, 255, 255, 255, 255, 000, 128, 128, 255, 255, 000, 255, 128 - }; + ]; private static readonly Rgba32[][] Grayscale8Result4X4 = - { - new[] { Gray128, Gray255, Gray000, Gray255 }, - new[] { Gray255, Gray255, Gray255, Gray255 }, - new[] { Gray000, Gray128, Gray128, Gray255 }, - new[] { Gray255, Gray000, Gray255, Gray128 } - }; + [ + [Gray128, Gray255, Gray000, Gray255], + [Gray255, Gray255, Gray255, Gray255], + [Gray000, Gray128, Gray128, Gray255], + [Gray255, Gray000, Gray255, Gray128] + ]; public static IEnumerable BilevelData { get { - yield return new object[] { BilevelBytes4X4, 1, 0, 0, 4, 4, BilevelResult4X4 }; - yield return new object[] { BilevelBytes4X4, 1, 0, 0, 4, 4, Offset(BilevelResult4X4, 0, 0, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 1, 0, 4, 4, Offset(BilevelResult4X4, 1, 0, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 0, 1, 4, 4, Offset(BilevelResult4X4, 0, 1, 6, 6) }; - yield return new object[] { BilevelBytes4X4, 1, 1, 1, 4, 4, Offset(BilevelResult4X4, 1, 1, 6, 6) }; - - yield return new object[] { BilevelBytes12X4, 1, 0, 0, 12, 4, BilevelResult12X4 }; - yield return new object[] { BilevelBytes12X4, 1, 0, 0, 12, 4, Offset(BilevelResult12X4, 0, 0, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 1, 0, 12, 4, Offset(BilevelResult12X4, 1, 0, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 0, 1, 12, 4, Offset(BilevelResult12X4, 0, 1, 18, 6) }; - yield return new object[] { BilevelBytes12X4, 1, 1, 1, 12, 4, Offset(BilevelResult12X4, 1, 1, 18, 6) }; + yield return [BilevelBytes4X4, 1, 0, 0, 4, 4, BilevelResult4X4]; + yield return [BilevelBytes4X4, 1, 0, 0, 4, 4, Offset(BilevelResult4X4, 0, 0, 6, 6)]; + yield return [BilevelBytes4X4, 1, 1, 0, 4, 4, Offset(BilevelResult4X4, 1, 0, 6, 6)]; + yield return [BilevelBytes4X4, 1, 0, 1, 4, 4, Offset(BilevelResult4X4, 0, 1, 6, 6)]; + yield return [BilevelBytes4X4, 1, 1, 1, 4, 4, Offset(BilevelResult4X4, 1, 1, 6, 6)]; + + yield return [BilevelBytes12X4, 1, 0, 0, 12, 4, BilevelResult12X4]; + yield return [BilevelBytes12X4, 1, 0, 0, 12, 4, Offset(BilevelResult12X4, 0, 0, 18, 6)]; + yield return [BilevelBytes12X4, 1, 1, 0, 12, 4, Offset(BilevelResult12X4, 1, 0, 18, 6)]; + yield return [BilevelBytes12X4, 1, 0, 1, 12, 4, Offset(BilevelResult12X4, 0, 1, 18, 6)]; + yield return [BilevelBytes12X4, 1, 1, 1, 12, 4, Offset(BilevelResult12X4, 1, 1, 18, 6)]; } } @@ -121,17 +121,17 @@ public static IEnumerable Grayscale4Data { get { - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Grayscale4Result4X4 }; - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Offset(Grayscale4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 1, 0, 4, 4, Offset(Grayscale4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 0, 1, 4, 4, Offset(Grayscale4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale4Bytes4X4, 4, 1, 1, 4, 4, Offset(Grayscale4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Grayscale4Result3X4 }; - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Offset(Grayscale4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 1, 0, 3, 4, Offset(Grayscale4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 0, 1, 3, 4, Offset(Grayscale4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale4Bytes3X4, 4, 1, 1, 3, 4, Offset(Grayscale4Result3X4, 1, 1, 6, 6) }; + yield return [Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Grayscale4Result4X4]; + yield return [Grayscale4Bytes4X4, 4, 0, 0, 4, 4, Offset(Grayscale4Result4X4, 0, 0, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 1, 0, 4, 4, Offset(Grayscale4Result4X4, 1, 0, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 0, 1, 4, 4, Offset(Grayscale4Result4X4, 0, 1, 6, 6)]; + yield return [Grayscale4Bytes4X4, 4, 1, 1, 4, 4, Offset(Grayscale4Result4X4, 1, 1, 6, 6)]; + + yield return [Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Grayscale4Result3X4]; + yield return [Grayscale4Bytes3X4, 4, 0, 0, 3, 4, Offset(Grayscale4Result3X4, 0, 0, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 1, 0, 3, 4, Offset(Grayscale4Result3X4, 1, 0, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 0, 1, 3, 4, Offset(Grayscale4Result3X4, 0, 1, 6, 6)]; + yield return [Grayscale4Bytes3X4, 4, 1, 1, 3, 4, Offset(Grayscale4Result3X4, 1, 1, 6, 6)]; } } @@ -139,11 +139,11 @@ public static IEnumerable Grayscale8Data { get { - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Grayscale8Result4X4 }; - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Offset(Grayscale8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 1, 0, 4, 4, Offset(Grayscale8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 0, 1, 4, 4, Offset(Grayscale8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Grayscale8Bytes4X4, 8, 1, 1, 4, 4, Offset(Grayscale8Result4X4, 1, 1, 6, 6) }; + yield return [Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Grayscale8Result4X4]; + yield return [Grayscale8Bytes4X4, 8, 0, 0, 4, 4, Offset(Grayscale8Result4X4, 0, 0, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 1, 0, 4, 4, Offset(Grayscale8Result4X4, 1, 0, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 0, 1, 4, 4, Offset(Grayscale8Result4X4, 0, 1, 6, 6)]; + yield return [Grayscale8Bytes4X4, 8, 1, 1, 4, 4, Offset(Grayscale8Result4X4, 1, 1, 6, 6)]; } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs index 4acdf3e509..b574c7e55c 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/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ab49805a35..e5ecb50f5c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -2,11 +2,15 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming +using System.Numerics; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Tiff; @@ -22,8 +26,39 @@ public class TiffDecoderTests : TiffDecoderBaseTester { public static readonly string[] MultiframeTestImages = Multiframes; + /// + /// Decoded floating-point components are normalized before they enter half-vector storage. + /// + /// The encoded floating-point TIFF. + /// The normalized intensity. + [Theory] + [InlineData("49492A00080000000A0000010400010000000800000001010400010000000100000002010300010000002000000003010300010000000100" + + "0000060103000100000001000000110104000100000086000000150103000100000001000000160104000100000001000000170104000100" + + "000020000000530103000100000003000000000000000000807F0000807F0000807F0000807F0000807F0000807F0000807F0000807F", 1F)] + [InlineData("49492A00080000000A0000010400010000000800000001010400010000000100000002010300010000002000000003010300010000000100" + + "0000060103000100000001000000110104000100000086000000150103000100000001000000160104000100000001000000170104000100" + + "000020000000530103000100000003000000000000000000C07F0000C07F0000C07F0000C07F0000C07F0000C07F0000C07F0000C07F", 0F)] + [InlineData("49492A00080000000A0000010400010000000800000001010400010000000100000002010300010000002000000003010300010000000100" + + "0000060103000100000001000000110104000100000086000000150103000100000001000000160104000100000001000000170104000100" + + "000020000000530103000100000003000000000000000000004000000040000000400000004000000040000000400000004000000040", 1F)] + [InlineData("49492A00080000000A0000010400010000000800000001010400010000000100000002010300010000002000000003010300010000000100" + + "0000060103000100000001000000110104000100000086000000150103000100000001000000160104000100000001000000170104000100" + + "000020000000530103000100000003000000000000000000003F0000003F0000003F0000003F0000003F0000003F0000003F0000003F", .5F)] + public void Decode_FloatingPointSamples_NormalizesHalfVector4(string hex, float intensity) + { + byte[] data = Convert.FromHexString(hex); + using Image image = Image.Load(data); + Assert.Equal(new Size(8, 1), image.Size); + + Vector4 expected = new(intensity, intensity, intensity, 1F); + + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(expected, image[x, 0].ToScaledVector4()); + } + } + [Theory] - [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] [WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) @@ -91,6 +126,40 @@ public void TiffDecoder_CanDecode_Planar(TestImageProvider provi public void TiffDecoder_CanDecode_Tiled(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(TiledRgbaDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgbDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGrayDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray32BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray32BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb48BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb48BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgba64BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgba64BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb96BitLittleEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb96BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Tiled_Deflate_Compressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(TiledRgbaLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgbLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGrayLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitLittleEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray16BitBigEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray32BitLittleEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledGray32BitBigEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb48BitLittleEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb48BitBigEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgba64BitLittleEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgba64BitBigEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb96BitLittleEndianLzwCompressedWithPredictor, PixelTypes.Rgba32)] + [WithFile(TiledRgb96BitBigEndianDeflateCompressedWithPredictor, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Tiled_Lzw_Compressed(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(Rgba8BitPlanarUnassociatedAlpha, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Planar_32Bit(TestImageProvider provider) @@ -133,6 +202,17 @@ public void TiffDecoder_CanDecode_Planar_128Bit(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + /// + /// Palette TIFFs with LZW compression and horizontal prediction preserve colors for both palette ranges. + /// + /// The pixel type. + /// The TIFF image provider. + [Theory] + [WithFile(Issue3182ColorMap16Bit, PixelTypes.Rgba64)] + [WithFile(Issue3182ColorMap8Bit, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_PaletteWithLzwAndPredictor(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)] [WithFile(Flower4BitPalette, PixelTypes.Rgba32)] @@ -308,16 +388,100 @@ public void TiffDecoder_CanDecode_CieLab(TestImageProvider provi [Theory] [WithFile(Cmyk, PixelTypes.Rgba32)] [WithFile(CmykLzwPredictor, PixelTypes.Rgba32)] + [WithFile(CmykJpeg, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Cmyk(TestImageProvider provider) where TPixel : unmanaged, IPixel { // Note: The image from MagickReferenceDecoder does not look right, maybe we are doing something wrong // converting the pixel data from Magick.NET to our format with CMYK? - using Image image = provider.GetImage(); + using Image image = provider.GetImage(TiffDecoder.Instance); + image.DebugSave(provider); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); + } + + [Theory] + [WithFile(Icc.PerceptualCmyk, PixelTypes.Rgba32)] + [WithFile(Icc.PerceptualCieLab, PixelTypes.Rgba32)] + [WithFile(Icc.PerceptualRgb8, PixelTypes.Rgba32)] + [WithFile(Icc.PerceptualRgb16, PixelTypes.Rgba32)] + public void Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance, new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert }); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + Assert.Null(image.Metadata.IccProfile); + } + + [Theory] + [WithFile(Icc.PerceptualRgb8, PixelTypes.Rgba32)] + [WithFile(Icc.PerceptualRgb16, PixelTypes.Rgba32)] + public void Decode_WhenColorProfileHandlingIsPreserve_PreservesIccProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() { ColorProfileHandling = ColorProfileHandling.Preserve }; + using Image image = provider.GetImage(TiffDecoder.Instance, options); + + Assert.NotNull(image.Metadata.IccProfile); + Assert.NotSame(image.Frames.RootFrame.Metadata.IccProfile, image.Metadata.IccProfile); + Assert.Equal( + image.Frames.RootFrame.Metadata.IccProfile.ToByteArray(), + image.Metadata.IccProfile.ToByteArray()); + } + + [Theory] + [WithFile(Issues2454_A, PixelTypes.Rgba32)] + [WithFile(Issues2454_B, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_YccK(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + image.DebugSave(provider); + + // ARM reports a 0.0000% difference, so we use a tolerant comparer here. + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.0001F), provider); + } + + [Theory] + [WithFile(Issues3031, PixelTypes.Rgba64)] + [WithFile(Rgba16BitAssociatedAlphaBigEndian, PixelTypes.Rgba64)] + [WithFile(Rgba16BitAssociatedAlphaLittleEndian, PixelTypes.Rgba64)] + public void TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + PngEncoder encoder = new() + { + BitDepth = PngBitDepth.Bit16, + ColorType = PngColorType.RgbWithAlpha + }; + + // This exact Rgba64 comparison requires a 16-bit reference. The Windows reference encoder uses a 32-bit + // System.Drawing bitmap and would otherwise quantize each channel to 8 bits while producing the PNG. + image.DebugSave(provider, testOutputDetails: null, extension: "png", encoder: encoder); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } + [Theory] + [WithFile(Issues2454_A, PixelTypes.Rgba32)] + [WithFile(Issues2454_B, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_YccK_ICC(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + DecoderOptions options = new() + { + ColorProfileHandling = ColorProfileHandling.Convert, + }; + + using Image image = provider.GetImage(TiffDecoder.Instance, options); + image.DebugSave(provider); + + // Linux reports a 0.0000% difference, so we use a tolerant comparer here. + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.0001F), provider); + } + [Theory] [WithFile(FlowerRgb101010Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb101010Planar, PixelTypes.Rgba32)] @@ -596,6 +760,16 @@ public void CanDecodeJustOneFrame(TestImageProvider provider) Assert.Equal(1, image.Frames.Count); } + [Theory] + [WithFile(MultiFrameMipMap, PixelTypes.Rgba32)] + public void CanDecode_MultiFrameMipMap(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + Assert.Equal(7, image.Frames.Count); + image.DebugSaveMultiFrame(provider); + } + [Theory] [WithFile(RgbJpegCompressed, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressed2, PixelTypes.Rgba32)] @@ -604,17 +778,17 @@ public void CanDecodeJustOneFrame(TestImageProvider provider) [WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)] [WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)] - [WithFile(Issues2123, PixelTypes.Rgba32)] - public void TiffDecoder_CanDecode_JpegCompressed(TestImageProvider provider) - where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, useExactComparer: false); + [WithFile(Issues2123, PixelTypes.Rgba32, 0.110f)] + public void TiffDecoder_CanDecode_JpegCompressed(TestImageProvider provider, float tolerance = 0.001f) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, useExactComparer: false, compareTolerance: tolerance); [Theory] [WithFile(RgbOldJpegCompressed, PixelTypes.Rgba32)] - [WithFile(RgbOldJpegCompressed2, PixelTypes.Rgba32)] + [WithFile(RgbOldJpegCompressed2, PixelTypes.Rgba32, 0.1003f)] [WithFile(RgbOldJpegCompressed3, PixelTypes.Rgba32)] [WithFile(RgbOldJpegCompressedGray, PixelTypes.Rgba32)] [WithFile(YCbCrOldJpegCompressed, PixelTypes.Rgba32)] - public void TiffDecoder_CanDecode_OldJpegCompressed(TestImageProvider provider) + public void TiffDecoder_CanDecode_OldJpegCompressed(TestImageProvider provider, float tolerance = 0.001f) where TPixel : unmanaged, IPixel { DecoderOptions decoderOptions = new() @@ -625,7 +799,7 @@ public void TiffDecoder_CanDecode_OldJpegCompressed(TestImageProvider(TestIm 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, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput( + ImageComparer.Exact, + provider, + appendPixelTypeToFileName: false); + } + [Theory] [WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)] public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets(TestImageProvider provider) @@ -693,7 +884,7 @@ public void TiffDecoder_CanDecode_TiledWithBadZlib(TestImageProvider(TestImageProvider provider { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(TiffDecoder.Instance, options); @@ -738,4 +929,50 @@ public void TiffDecoder_Decode_Resize(TestImageProvider provider testOutputDetails: details, appendPixelTypeToFileName: false); } + + [Theory] + [WithFile(ExtraSamplesUnspecified, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_ExtraSamplesUnspecified(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(Issue2983, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Issue2983(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Fact] + public void Identify_MalformedIccProfile_IgnoresNonCriticalErrorsByDefault() + { + ImageInfo info = Image.Identify(CreateTiffWithMalformedIccProfile()); + Assert.Equal(1, info.Width); + Assert.Equal(1, info.Height); + } + + [Fact] + public void Decode_MalformedIccProfile_IgnoresNonCriticalErrorsByDefault() + { + using Image image = Image.Load(CreateTiffWithMalformedIccProfile()); + Assert.Equal(1, image.Width); + Assert.Equal(1, image.Height); + } + + [Fact] + public void Identify_MalformedIccProfile_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => Image.Identify(options, CreateTiffWithMalformedIccProfile())); + } + + [Fact] + public void Decode_MalformedIccProfile_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + Assert.Throws(() => + { + using Image image = Image.Load(options, CreateTiffWithMalformedIccProfile()); + }); + } + + private static byte[] CreateTiffWithMalformedIccProfile() + => CorruptedMetadataImageFactory.CreateImageWithMalformedIccProfile(new TiffEncoder()); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderBaseTester.cs index 2a822e7054..158a0d4730 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, @@ -26,18 +26,18 @@ protected static void TestStripLength( where TPixel : unmanaged, IPixel { // arrange - var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; + TiffEncoder tiffEncoder = new() { PhotometricInterpretation = photometricInterpretation, Compression = compression }; using Image input = provider.GetImage(); - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata(); - TiffCompression inputCompression = inputMeta.Compression ?? TiffCompression.None; + TiffCompression inputCompression = inputMeta.Compression; // act input.Save(memStream, tiffEncoder); // assert memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile; TiffFrameMetadata outputMeta = output.Frames.RootFrame.Metadata.GetTiffMetadata(); ImageFrame rootFrame = output.Frames.RootFrame; @@ -48,7 +48,6 @@ protected static void TestStripLength( Number[] stripByteCounts = exifProfileOutput.GetValue(ExifTag.StripByteCounts)?.Value; Assert.NotNull(stripByteCounts); Assert.True(stripByteCounts.Length > 1); - Assert.NotNull(outputMeta.BitsPerPixel); foreach (Number sz in stripByteCounts) { @@ -90,7 +89,7 @@ protected static void TestTiffEncoderCore( where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - var encoder = new TiffEncoder + TiffEncoder encoder = new() { PhotometricInterpretation = photometricInterpretation, BitsPerPixel = bitsPerPixel, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 8724147301..282966ea85 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 void WriteHeader_WritesValidHeader() 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/TiffEncoderMultiframeTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs index b74093fcc1..9f6e74183f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs @@ -18,7 +18,6 @@ public void TiffEncoder_EncodeMultiframe_Works(TestImageProvider where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb); [Theory] - [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] public void TiffEncoder_EncodeMultiframe_NotSupport(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb)); @@ -47,7 +46,7 @@ public void TiffEncoder_EncodeMultiframe_RemoveFrames(TestImageProvider< image.Frames.RemoveFrame(0); TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Bit24; - var encoder = new TiffEncoder + TiffEncoder encoder = new() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb, BitsPerPixel = bitsPerPixel, @@ -70,35 +69,35 @@ public void TiffEncoder_EncodeMultiframe_AddFrames(TestImageProvider image = provider.GetImage(); Assert.Equal(1, image.Frames.Count); - using var image1 = new Image(image.Width, image.Height, Color.Green.ToRgba32()); + using Image image1 = new(image.Width, image.Height, Color.Green.ToPixel()); - using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToRgba32()); + using Image image2 = new(image.Width, image.Height, Color.Yellow.ToPixel()); image.Frames.AddFrame(image1.Frames.RootFrame); image.Frames.AddFrame(image2.Frames.RootFrame); TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Bit24; - var encoder = new TiffEncoder + TiffEncoder encoder = new() { PhotometricInterpretation = TiffPhotometricInterpretation.Rgb, BitsPerPixel = bitsPerPixel, Compression = TiffCompression.Deflate }; - using (var ms = new System.IO.MemoryStream()) + using (MemoryStream ms = new()) { image.Save(ms, encoder); ms.Position = 0; - using var output = Image.Load(ms); + using Image output = Image.Load(ms); Assert.Equal(3, output.Frames.Count); 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 +121,11 @@ public void TiffEncoder_EncodeMultiframe_Create(TestImageProvider image = provider.GetImage(); - using var image0 = new Image(image.Width, image.Height, Color.Red.ToRgba32()); + using Image image0 = new(image.Width, image.Height, Color.Red.ToPixel()); - using var image1 = new Image(image.Width, image.Height, Color.Green.ToRgba32()); + using Image image1 = new(image.Width, image.Height, Color.Green.ToPixel()); - using var image2 = new Image(image.Width, image.Height, Color.Yellow.ToRgba32()); + using Image image2 = new(image.Width, image.Height, Color.Yellow.ToPixel()); image.Frames.AddFrame(image0.Frames.RootFrame); image.Frames.AddFrame(image1.Frames.RootFrame); @@ -134,19 +133,19 @@ public void TiffEncoder_EncodeMultiframe_Create(TestImageProvider(ms); + using Image output = Image.Load(ms); Assert.Equal(3, output.Frames.Count); @@ -154,9 +153,9 @@ public void TiffEncoder_EncodeMultiframe_Create(TestImageProvider 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/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 1fafb4cd04..cc1b78c076 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff; @@ -31,7 +32,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 +58,7 @@ public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInt 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 +156,11 @@ public void EncoderOptions_SetPhotometricInterpretationAndCompression_Works( { // 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 +203,6 @@ public void TiffEncoder_PreservesBitsPerPixel(TestImageProvider 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 +226,11 @@ public void TiffEncoder_PreservesCompression(TestImageProvider p } [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 @@ -271,7 +256,7 @@ public void TiffEncoder_WritesIfdOffsetAtWordBoundary() TiffEncoder tiffEncoder = new(); using MemoryStream memStream = new(); using Image image = new(1, 1); - byte[] expectedIfdOffsetBytes = { 12, 0 }; + byte[] expectedIfdOffsetBytes = [12, 0]; // act image.Save(memStream, tiffEncoder); @@ -308,6 +293,82 @@ public void TiffEncoder_EncodesWithCorrectBiColorModeCompression(TestIma Assert.Equal(expectedCompression, frameMetaData.Compression); } + [Theory] + [WithFile(MultiFrameMipMap, PixelTypes.Rgba32)] + public void TiffEncoder_EncodesMultiFrameMipMap(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + Assert.Equal(7, image.Frames.Count); + + using MemoryStream memStream = new(); + image.SaveAsTiff(memStream); + + memStream.Position = 0; + using Image output = Image.Load(memStream); + + Assert.Equal(image.Size, output.Size); + Assert.Equal(image.Frames.Count, output.Frames.Count); + + for (int i = 0; i < image.Frames.Count; i++) + { + TiffFrameMetadata inputMetadata = image.Frames[i].Metadata.GetTiffMetadata(); + TiffFrameMetadata outputMetadata = output.Frames[i].Metadata.GetTiffMetadata(); + + Assert.Equal(inputMetadata.EncodingWidth, outputMetadata.EncodingWidth); + Assert.Equal(inputMetadata.EncodingHeight, outputMetadata.EncodingHeight); + } + } + + [Theory] + [WithFile(MultiFrameMipMap, PixelTypes.Rgba32)] + public void TiffEncoder_EncodesMultiFrameMipMap_WithScaling(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + Assert.Equal(7, image.Frames.Count); + + Size size = image.Size; + + List encodedDimensions = []; + foreach (ImageFrame frame in image.Frames) + { + TiffFrameMetadata metadata = frame.Metadata.GetTiffMetadata(); + encodedDimensions.Add(new Size(metadata.EncodingWidth, metadata.EncodingHeight)); + } + + const int scale = 2; + image.Mutate(x => x.Resize(image.Width / scale, image.Height / scale)); + + using MemoryStream memStream = new(); + image.SaveAsTiff(memStream); + + memStream.Position = 0; + using Image output = Image.Load(memStream); + + Assert.Equal(image.Size, output.Size); + Assert.Equal(image.Frames.Count, output.Frames.Count); + + // The encoded dimensions should automatically be scaled down by the + // horizontal and vertical scaling factors. + float ratioX = output.Width / (float)size.Width; + float ratioY = output.Height / (float)size.Height; + + for (int i = 0; i < image.Frames.Count; i++) + { + TiffFrameMetadata inputMetadata = image.Frames[i].Metadata.GetTiffMetadata(); + TiffFrameMetadata outputMetadata = output.Frames[i].Metadata.GetTiffMetadata(); + + int expectedWidth = (int)MathF.Ceiling(encodedDimensions[i].Width * ratioX); + int expectedHeight = (int)MathF.Ceiling(encodedDimensions[i].Height * ratioY); + + Assert.Equal(expectedWidth, inputMetadata.EncodingWidth); + Assert.Equal(expectedHeight, inputMetadata.EncodingHeight); + Assert.Equal(inputMetadata.EncodingWidth, outputMetadata.EncodingWidth); + Assert.Equal(inputMetadata.EncodingHeight, outputMetadata.EncodingHeight); + } + } + // This makes sure, that when decoding a planar tiff, the planar configuration is not carried over to the encoded image. [Theory] [WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)] @@ -493,6 +554,45 @@ public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_WhiteIsZero_ public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax); + /// + /// CCITT row framing must fit even when each row contains only one pixel. + /// + /// The image width. + [Theory] + [InlineData(1)] + [InlineData(64)] + public void TiffEncoder_EncodeNarrowCcittGroup3Fax_Works(int width) + { + using Image image = new(width, 2000); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0)); + } + } + + TiffFrameMetadata metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); + metadata.BitsPerPixel = TiffBitsPerPixel.Bit1; + metadata.Compression = TiffCompression.CcittGroup3Fax; + + using MemoryStream output = new(); + image.Save(output, new TiffEncoder()); + + output.Position = 0; + using Image decoded = Image.Load(output); + Assert.Equal(image.Size, decoded.Size); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(image[x, y], decoded[x, y]); + } + } + } + [Theory] [WithFile(Issues2255, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithCcittGroup3FaxCompression_WithoutSpecifyingBitPerPixel_Works(TestImageProvider provider) @@ -508,6 +608,26 @@ public void TiffEncoder_EncodeBiColor_WithCcittGroup4FaxCompression_WhiteIsZero_ public void TiffEncoder_EncodeBiColor_WithCcittGroup4FaxCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup4Fax); + /// + /// Re-encoding a one-pixel Group 4 image must retain its pixel and fit the end-of-block code. + /// + [Fact] + public void TiffEncoder_ReencodeNarrowCcittGroup4Fax_Works() + { + byte[] data = Convert.FromBase64String( + "SUkqAAgAAAAJAAABAwABAAAAAQAAAAEBAwABAAAAAQAAAAIBAwABAAAAAQAAAAMBAwABAAAABAAAAAYBAwABAAAAAAAAABEBBAABAAAA" + + "egAAABUBAwABAAAAAQAAABYBBAABAAAAAQAAABcBBAABAAAABAAAAAAAAACACACA"); + + using Image image = Image.Load(data); + using MemoryStream output = new(); + image.Save(output, new TiffEncoder()); + + output.Position = 0; + using Image decoded = Image.Load(output); + Assert.Equal(new Size(1, 1), decoded.Size); + Assert.Equal(image[0, 0], decoded[0, 0]); + } + [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_WhiteIsZero_Works(TestImageProvider provider) @@ -518,6 +638,16 @@ public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_WhiteIsZero public void TiffEncoder_EncodeBiColor_WithModifiedHuffmanCompression_BlackIsZero_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit1, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D); + [Theory] + [WithFile(Issue2909, PixelTypes.Rgba32)] + public void TiffEncoder_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, null, TiffCompression.Lzw, imageDecoder: TiffDecoder.Instance); + + [Theory] + [WithFile(Issue2909, PixelTypes.Rgba32)] + public void TiffEncoder_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, null, TiffCompression.Deflate, imageDecoder: TiffDecoder.Instance); + [Theory] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)] [WithFile(GrayscaleUncompressed16Bit, PixelTypes.L16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)] @@ -552,8 +682,7 @@ public void TiffEncode_WorksWithDiscontiguousBuffers(TestImageProvider image = provider.GetImage(); - TiffEncoder encoder = new() - { PhotometricInterpretation = photometricInterpretation }; + TiffEncoder encoder = new() { PhotometricInterpretation = photometricInterpretation }; image.DebugSave(provider, encoder); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index e31487cd23..9f0f0e9910 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestDataIcc; using SixLabors.ImageSharp.Tests.TestUtilities; using static SixLabors.ImageSharp.Tests.TestImages.Tiff; @@ -61,16 +62,15 @@ public void TiffFrameMetadata_CloneIsDeep(TestImageProvider prov clone.PhotometricInterpretation = TiffPhotometricInterpretation.CieLab; clone.Predictor = TiffPredictor.Horizontal; - Assert.False(meta.BitsPerPixel == clone.BitsPerPixel); - Assert.False(meta.Compression == clone.Compression); - Assert.False(meta.PhotometricInterpretation == clone.PhotometricInterpretation); - Assert.False(meta.Predictor == clone.Predictor); + Assert.NotEqual(meta.BitsPerPixel, clone.BitsPerPixel); + Assert.NotEqual(meta.Compression, clone.Compression); + Assert.NotEqual(meta.PhotometricInterpretation, clone.PhotometricInterpretation); + Assert.NotEqual(meta.Predictor, clone.Predictor); } private static void VerifyExpectedTiffFrameMetaDataIsPresent(TiffFrameMetadata frameMetaData) { Assert.NotNull(frameMetaData); - Assert.NotNull(frameMetaData.BitsPerPixel); Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); @@ -150,14 +150,23 @@ public void MetadataProfiles(TestImageProvider provider, bool ig Assert.NotNull(meta); if (ignoreMetadata) { + Assert.Null(image.Metadata.XmpProfile); + Assert.Null(image.Metadata.ExifProfile); Assert.Null(rootFrameMetaData.XmpProfile); Assert.Null(rootFrameMetaData.ExifProfile); } else { + Assert.NotNull(image.Metadata.XmpProfile); + Assert.NotNull(image.Metadata.ExifProfile); Assert.NotNull(rootFrameMetaData.XmpProfile); Assert.NotNull(rootFrameMetaData.ExifProfile); - Assert.Equal(2599, rootFrameMetaData.XmpProfile.Data.Length); + + Assert.NotSame(rootFrameMetaData.XmpProfile, image.Metadata.XmpProfile); + Assert.NotSame(rootFrameMetaData.ExifProfile, image.Metadata.ExifProfile); + Assert.Equal(rootFrameMetaData.XmpProfile.Data, image.Metadata.XmpProfile.Data); + Assert.Equal(rootFrameMetaData.ExifProfile.ToByteArray(), image.Metadata.ExifProfile.ToByteArray()); + Assert.Equal(2596, rootFrameMetaData.XmpProfile.Data.Length); // padding bytes are trimmed Assert.Equal(25, rootFrameMetaData.ExifProfile.Values.Count); } } @@ -171,6 +180,10 @@ public void CanDecodeImage_WithIptcDataAsLong(TestImageProvider IptcProfile iptcProfile = image.Frames.RootFrame.Metadata.IptcProfile; Assert.NotNull(iptcProfile); + Assert.NotNull(image.Metadata.IptcProfile); + Assert.NotSame(iptcProfile, image.Metadata.IptcProfile); + Assert.Equal(iptcProfile.Data, image.Metadata.IptcProfile.Data); + IptcValue byline = iptcProfile.Values.FirstOrDefault(data => data.Tag == IptcTag.Byline); Assert.NotNull(byline); Assert.Equal("Studio Mantyniemi", byline.Value); @@ -186,7 +199,7 @@ public void BaselineTags(TestImageProvider provider) Assert.Equal(32, rootFrame.Width); Assert.Equal(32, rootFrame.Height); Assert.NotNull(rootFrame.Metadata.XmpProfile); - Assert.Equal(2599, rootFrame.Metadata.XmpProfile.Data.Length); + Assert.Equal(2596, rootFrame.Metadata.XmpProfile.Data.Length); // padding bytes are trimmed ExifProfile exifProfile = rootFrame.Metadata.ExifProfile; TiffFrameMetadata tiffFrameMetadata = rootFrame.Metadata.GetTiffMetadata(); @@ -208,8 +221,8 @@ public void BaselineTags(TestImageProvider provider) Rational expectedResolution = new(10, 1, simplify: false); Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.XResolution).Value); Assert.Equal(expectedResolution, exifProfile.GetValue(ExifTag.YResolution).Value); - Assert.Equal(new Number[] { 8u }, exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); - Assert.Equal(new Number[] { 285u }, exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); + Assert.Equal([8u], exifProfile.GetValue(ExifTag.StripOffsets)?.Value, new NumberComparer()); + Assert.Equal([285u], exifProfile.GetValue(ExifTag.StripByteCounts)?.Value, new NumberComparer()); Assert.Null(exifProfile.GetValue(ExifTag.ExtraSamples, false)?.Value); Assert.Equal(32u, exifProfile.GetValue(ExifTag.RowsPerStrip).Value); Assert.Null(exifProfile.GetValue(ExifTag.SampleFormat, false)); @@ -336,8 +349,7 @@ public void Encode_PreservesMetadata_IptcAndIcc(TestImageProvider(TestImageProvider(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder.Instance); + ImageFrame frame = image.Frames.RootFrame; + TiffFrameMetadata tiffMeta = frame.Metadata.GetTiffMetadata(); + Assert.Equal(TiffPhotometricInterpretation.PaletteColor, tiffMeta.PhotometricInterpretation); + Assert.NotNull(tiffMeta.LocalColorTable); + } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffUtilitiesTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffUtilitiesTest.cs new file mode 100644 index 0000000000..104ab76a7d --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffUtilitiesTest.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Tiff.Utils; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils; + +[Trait("Format", "Tiff")] +public class TiffUtilitiesTest +{ + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(42, 84, 128, 0)] + [InlineData(65535, 65535, 65535, 0)] + public void ColorFromRgba64Premultiplied_WithZeroAlpha_ReturnsDefaultPixel(ushort r, ushort g, ushort b, ushort a) + { + Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied(r, g, b, a); + + Assert.Equal(default, actual); + } + + [Fact] + public void ColorScaleTo24BitPremultiplied_WithZeroAlpha_ReturnsDefaultPixel() + { + Rgba64 unassociated = TiffUtilities.ColorScaleTo24BitPremultiplied(0x123456, 0x654321, 0xABCDEF, 0); + Rgba32P associated = TiffUtilities.ColorScaleTo24BitPremultiplied(0x123456, 0x654321, 0xABCDEF, 0); + + Assert.Equal(default, unassociated); + Assert.Equal(default, associated); + } + + [Fact] + public void ColorScaleTo32BitPremultiplied_WithZeroAlpha_ReturnsDefaultPixel() + { + Rgba64 unassociated = TiffUtilities.ColorScaleTo32BitPremultiplied(0x12345678, 0x654321AB, 0xABCDEF12, 0); + Rgba32P associated = TiffUtilities.ColorScaleTo32BitPremultiplied(0x12345678, 0x654321AB, 0xABCDEF12, 0); + + Assert.Equal(default, unassociated); + Assert.Equal(default, associated); + } + + [Theory] + [InlineData(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535)] + [InlineData(32767, 0, 0, 65535, 32767, 0, 0, 65535)] + [InlineData(0, 32767, 0, 65535, 0, 32767, 0, 65535)] + [InlineData(0, 0, 32767, 65535, 0, 0, 32767, 65535)] + public void ColorFromRgba64Premultiplied_WithNoAlpha_ReturnExpectedValues(ushort r, ushort g, ushort b, ushort a, ushort expectedR, ushort expectedG, ushort expectedB, ushort expectedA) + { + Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied(r, g, b, a); + + Assert.Equal(new Rgba64(expectedR, expectedG, expectedB, expectedA), actual); + } + + [Theory] + [InlineData(32766, 0, 0, 32766, 65535, 0, 0, 32766)] // Red, 50% Alpha + [InlineData(0, 32766, 0, 32766, 0, 65535, 0, 32766)] // Green, 50% Alpha + [InlineData(0, 0, 32766, 32766, 0, 0, 65535, 32766)] // Blue, 50% Alpha + /* The exact 25% result lies below the 16-bit midpoint by less than one float ULP. The Vector4 conversion therefore + reaches the midpoint and applies the established round-to-even contract without requiring a comparison tolerance. */ + [InlineData(8191, 0, 0, 16383, 32766, 0, 0, 16383)] // Red, 25% Alpha + [InlineData(0, 8191, 0, 16383, 0, 32766, 0, 16383)] // Green, 25% Alpha + [InlineData(0, 0, 8191, 16383, 0, 0, 32766, 16383)] // Blue, 25% Alpha + [InlineData(8191, 0, 0, 0, 0, 0, 0, 0)] // Red, 0% Alpha + [InlineData(0, 8191, 0, 0, 0, 0, 0, 0)] // Green, 0% Alpha + [InlineData(0, 0, 8191, 0, 0, 0, 0, 0)] // Blue, 0% Alpha + public void ColorFromRgba64Premultiplied_WithAlpha_ReturnExpectedValues(ushort r, ushort g, ushort b, ushort a, ushort expectedR, ushort expectedG, ushort expectedB, ushort expectedA) + { + Rgba64 actual = TiffUtilities.ColorFromRgba64Premultiplied(r, g, b, a); + + Assert.Equal(new Rgba64(expectedR, expectedG, expectedB, expectedA), actual); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index 9b26ab2702..939baeeb68 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -11,8 +11,8 @@ public class TiffWriterTests [Fact] public void IsLittleEndian_IsTrueOnWindows() { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); Assert.True(TiffStreamWriter.IsLittleEndian); } @@ -22,8 +22,8 @@ public void IsLittleEndian_IsTrueOnWindows() [InlineData(new byte[] { 1, 2, 3, 4, 5 }, 5)] public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.Write(data); Assert.Equal(writer.Position, expectedResult); } @@ -31,8 +31,8 @@ public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) [Fact] public void Write_WritesByte() { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.Write(42); Assert.Equal(new byte[] { 42 }, stream.ToArray()); @@ -41,8 +41,8 @@ public void Write_WritesByte() [Fact] public void Write_WritesByteArray() { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.Write(new byte[] { 2, 4, 6, 8 }); Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray()); @@ -51,8 +51,8 @@ public void Write_WritesByteArray() [Fact] public void Write_WritesUInt16() { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.Write(1234, stackalloc byte[2]); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); @@ -61,8 +61,8 @@ public void Write_WritesUInt16() [Fact] public void Write_WritesUInt32() { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.Write(12345678U, stackalloc byte[4]); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); @@ -78,8 +78,8 @@ public void Write_WritesUInt32() public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) { - using var stream = new MemoryStream(); - using var writer = new TiffStreamWriter(stream); + using MemoryStream stream = new(); + using TiffStreamWriter writer = new(stream); writer.WritePadded(bytes); Assert.Equal(expectedResult, stream.ToArray()); @@ -88,10 +88,10 @@ public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) [Fact] public void WriteMarker_WritesToPlacedPosition() { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); Span buffer = stackalloc byte[4]; - using (var writer = new TiffStreamWriter(stream)) + using (TiffStreamWriter writer = new(stream)) { writer.Write(0x11111111, buffer); long marker = writer.PlaceMarker(buffer); diff --git a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs index c5e8c975f1..9657859e06 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs @@ -12,13 +12,13 @@ public class ColorSpaceTransformUtilsTests private static void RunCollectColorBlueTransformsTest() { uint[] pixelData = - { + [ 3074, 256, 256, 256, 0, 65280, 65280, 65280, 256, 256, 0, 256, 0, 65280, 0, 65280, 16711680, 256, 256, 0, 65024, 0, 256, 256, 0, 65280, 0, 65280, 0, 256, 0, 256 - }; + ]; int[] expectedOutput = - { + [ 31, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -27,7 +27,7 @@ private static void RunCollectColorBlueTransformsTest() 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; int[] histo = new int[256]; ColorSpaceTransformUtils.CollectColorBlueTransforms(pixelData, 0, 32, 1, 0, 0, histo); @@ -38,13 +38,13 @@ private static void RunCollectColorBlueTransformsTest() private static void RunCollectColorRedTransformsTest() { uint[] pixelData = - { + [ 3074, 256, 256, 256, 0, 65280, 65280, 65280, 256, 256, 0, 256, 0, 65280, 0, 65280, 16711680, 256, 256, 0, 65024, 0, 256, 256, 0, 65280, 0, 65280, 0, 256, 0, 256 - }; + ]; int[] expectedOutput = - { + [ 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -53,7 +53,7 @@ private static void RunCollectColorRedTransformsTest() 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; + ]; int[] histo = new int[256]; ColorSpaceTransformUtils.CollectColorRedTransforms(pixelData, 0, 32, 1, 0, histo); @@ -71,17 +71,17 @@ private static void RunCollectColorRedTransformsTest() public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorBlueTransforms_WithoutSSE41_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableSSE41); + public void CollectColorBlueTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void CollectColorBlueTransforms_WithoutAvx2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableAVX2); + public void CollectColorBlueTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableAVX2); [Fact] public void CollectColorRedTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorRedTransforms_WithoutSSE41_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableSSE41); + public void CollectColorRedTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void CollectColorRedTransforms_WithoutAvx2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2); + public void CollectColorRedTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index 4551e3e23e..4dcccb1a3d 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs @@ -11,8 +11,10 @@ public class LosslessUtilsTests { private static void RunCombinedShannonEntropyTest() { - int[] x = { 3, 5, 2, 5, 3, 1, 2, 2, 3, 3, 1, 2, 1, 2, 1, 1, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 1, 0, 0, 2, 1, 1, 0, 3, 1, 2, 3, 2, 3 }; - int[] y = { 11, 12, 8, 3, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 1, 1, 2, 4, 6, 4 }; + int[] x = [3, 5, 2, 5, 3, 1, 2, 2, 3, 3, 1, 2, 1, 2, 1, 1, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 1, 0, 0, 2, 1, 1, 0, 3, 1, 2, 3, 2, 3 + ]; + int[] y = [11, 12, 8, 3, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 1, 1, 2, 4, 6, 4 + ]; const float expected = 884.7585f; float actual = LosslessUtils.CombinedShannonEntropy(x, y); @@ -23,7 +25,7 @@ private static void RunCombinedShannonEntropyTest() private static void RunSubtractGreenTest() { uint[] pixelData = - { + [ 4293035898, 4293101432, 4292903793, 4292838511, 4292837995, 4292771950, 4292903791, 4293299316, 4293563769, 4293629303, 4293363312, 4291913575, 4289282905, 4288692313, 4289349210, 4289809240, 4289743703, 4289874775, 4289940567, 4289743701, 4290137943, 4290860378, 4291058267, 4291386715, @@ -31,10 +33,10 @@ private static void RunSubtractGreenTest() 4291913571, 4292044130, 4291978850, 4291847521, 4291847524, 4291847779, 4291913571, 4291848293, 4291651689, 4291585895, 4291519584, 4291715936, 4291520355, 4291650658, 4291847263, 4291913313, 4291847777, 4291781731, 4291783015 - }; + ]; uint[] expectedOutput = - { + [ 4284188659, 4284254193, 4284318702, 4284187883, 4284318441, 4284383470, 4284318700, 4284124392, 4283799012, 4283864546, 4284581610, 4285163264, 4284891926, 4284497945, 4284761620, 4284893965, 4284828428, 4284959500, 4284959755, 4284828426, 4284960520, 4285289733, 4285159937, 4285292030, @@ -42,7 +44,7 @@ private static void RunSubtractGreenTest() 4285163516, 4285425149, 4285294332, 4285228540, 4285228543, 4285163261, 4285163516, 4285032701, 4284835841, 4284835584, 4284966140, 4285228029, 4284770300, 4285097214, 4285293819, 4285228795, 4285163259, 4285228287, 4284901886 - }; + ]; LosslessUtils.SubtractGreenFromBlueAndRed(pixelData); @@ -52,7 +54,7 @@ private static void RunSubtractGreenTest() private static void RunAddGreenToBlueAndRedTest() { uint[] pixelData = - { + [ 4284188659, 4284254193, 4284318702, 4284187883, 4284318441, 4284383470, 4284318700, 4284124392, 4283799012, 4283864546, 4284581610, 4285163264, 4284891926, 4284497945, 4284761620, 4284893965, 4284828428, 4284959500, 4284959755, 4284828426, 4284960520, 4285289733, 4285159937, 4285292030, @@ -60,10 +62,10 @@ private static void RunAddGreenToBlueAndRedTest() 4285163516, 4285425149, 4285294332, 4285228540, 4285228543, 4285163261, 4285163516, 4285032701, 4284835841, 4284835584, 4284966140, 4285228029, 4284770300, 4285097214, 4285293819, 4285228795, 4285163259, 4285228287, 4284901886 - }; + ]; uint[] expectedOutput = - { + [ 4293035898, 4293101432, 4292903793, 4292838511, 4292837995, 4292771950, 4292903791, 4293299316, 4293563769, 4293629303, 4293363312, 4291913575, 4289282905, 4288692313, 4289349210, 4289809240, 4289743703, 4289874775, 4289940567, 4289743701, 4290137943, 4290860378, 4291058267, 4291386715, @@ -71,7 +73,7 @@ private static void RunAddGreenToBlueAndRedTest() 4291913571, 4292044130, 4291978850, 4291847521, 4291847524, 4291847779, 4291913571, 4291848293, 4291651689, 4291585895, 4291519584, 4291715936, 4291520355, 4291650658, 4291847263, 4291913313, 4291847777, 4291781731, 4291783015 - }; + ]; LosslessUtils.AddGreenToBlueAndRed(pixelData); @@ -81,15 +83,15 @@ private static void RunAddGreenToBlueAndRedTest() private static void RunTransformColorTest() { uint[] pixelData = - { + [ 5998579, 65790, 130301, 16646653, 196350, 130565, 16712702, 16583164, 16452092, 65790, 782600, 647446, 16571414, 16448771, 263931, 132601, 16711935, 131072, 511, 16711679, 132350, 329469, 16647676, 132093, 66303, 16647169, 16515584, 196607, 196096, 16646655, 514, 131326, 16712192, 327169, 16646655, 16776960, 3, 16712190, 511, 16646401, 16580612, 65535, 196092, 327425, 16319743, 392450, 196861, 16712192, 16711680, 130564, 16451071 - }; + ]; - var m = new Vp8LMultipliers() + Vp8LMultipliers m = new() { GreenToBlue = 240, GreenToRed = 232, @@ -97,13 +99,13 @@ private static void RunTransformColorTest() }; uint[] expectedOutput = - { + [ 100279, 65790, 16710907, 16712190, 130813, 65028, 131840, 264449, 133377, 65790, 61697, 15917319, 14801924, 16317698, 591614, 394748, 16711935, 131072, 65792, 16711679, 328704, 656896, 132607, 328703, 197120, 66563, 16646657, 196607, 130815, 16711936, 131587, 131326, 66049, 261632, 16711936, 16776960, 3, 511, 65792, 16711938, 16580612, 65535, 65019, 327425, 16516097, 261377, 196861, 66049, 16711680, 65027, 16712962 - }; + ]; LosslessUtils.TransformColor(m, pixelData, pixelData.Length); @@ -113,15 +115,15 @@ private static void RunTransformColorTest() private static void RunTransformColorInverseTest() { uint[] pixelData = - { + [ 100279, 65790, 16710907, 16712190, 130813, 65028, 131840, 264449, 133377, 65790, 61697, 15917319, 14801924, 16317698, 591614, 394748, 16711935, 131072, 65792, 16711679, 328704, 656896, 132607, 328703, 197120, 66563, 16646657, 196607, 130815, 16711936, 131587, 131326, 66049, 261632, 16711936, 16776960, 3, 511, 65792, 16711938, 16580612, 65535, 65019, 327425, 16516097, 261377, 196861, 66049, 16711680, 65027, 16712962 - }; + ]; - var m = new Vp8LMultipliers() + Vp8LMultipliers m = new() { GreenToBlue = 240, GreenToRed = 232, @@ -129,13 +131,13 @@ private static void RunTransformColorInverseTest() }; uint[] expectedOutput = - { + [ 5998579, 65790, 130301, 16646653, 196350, 130565, 16712702, 16583164, 16452092, 65790, 782600, 647446, 16571414, 16448771, 263931, 132601, 16711935, 131072, 511, 16711679, 132350, 329469, 16647676, 132093, 66303, 16647169, 16515584, 196607, 196096, 16646655, 514, 131326, 16712192, 327169, 16646655, 16776960, 3, 16712190, 511, 16646401, 16580612, 65535, 196092, 327425, 16319743, 392450, 196861, 16712192, 16711680, 130564, 16451071 - }; + ]; LosslessUtils.TransformColorInverse(m, pixelData); @@ -145,7 +147,7 @@ private static void RunTransformColorInverseTest() private static void RunPredictor11Test() { // arrange - uint[] topData = { 4278258949, 4278258949 }; + uint[] topData = [4278258949, 4278258949]; const uint left = 4294839812; short[] scratch = new short[8]; const uint expectedResult = 4294839812; @@ -166,7 +168,7 @@ private static void RunPredictor11Test() private static void RunPredictor12Test() { // arrange - uint[] topData = { 4294844413, 4294779388 }; + uint[] topData = [4294844413, 4294779388]; const uint left = 4294844413; const uint expectedResult = 4294779388; @@ -186,10 +188,10 @@ private static void RunPredictor12Test() private static void RunPredictor13Test() { // arrange - uint[] topData0 = { 4278193922, 4278193666 }; + uint[] topData0 = [4278193922, 4278193666]; const uint left0 = 4278193410; const uint expectedResult0 = 4278193154; - uint[] topData1 = { 4294933015, 4278219803 }; + uint[] topData1 = [4294933015, 4278219803]; const uint left1 = 4278236686; const uint expectedResult1 = 4278231571; uint actual0 = 0; @@ -218,17 +220,18 @@ private static void RunPredictor13Test() public void BundleColorMap_WithXbitsZero_Works() { // arrange - byte[] row = { 238, 238, 238, 238, 238, 238, 240, 237, 240, 235, 223, 223, 218, 220, 226, 219, 220, 204, 218, 211, 218, 221, 254, 255 }; + byte[] row = [238, 238, 238, 238, 238, 238, 240, 237, 240, 235, 223, 223, 218, 220, 226, 219, 220, 204, 218, 211, 218, 221, 254, 255 + ]; int xBits = 0; uint[] actual = new uint[row.Length]; uint[] expected = - { + [ 4278251008, 4278251008, 4278251008, 4278251008, 4278251008, 4278251008, 4278251520, 4278250752, 4278251520, 4278250240, 4278247168, 4278247168, 4278245888, 4278246400, 4278247936, 4278246144, 4278246400, 4278242304, 4278245888, 4278244096, 4278245888, 4278246656, 4278255104, 4278255360 - }; + ]; // act LosslessUtils.BundleColorMap(row, actual.Length, xBits, actual); @@ -242,7 +245,7 @@ public void BundleColorMap_WithXbitsNoneZero_Works() { // arrange byte[] row = - { + [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -251,17 +254,17 @@ public void BundleColorMap_WithXbitsNoneZero_Works() 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 - }; + ]; int xBits = 2; uint[] actual = new uint[row.Length]; uint[] expected = - { + [ 4278233600, 4278233600, 4278233600, 4278233600, 4278255360, 4278255360, 4278255360, 4278255360, 4278233600, 4278233600, 4278233600, 4278233600, 4278255360, 4278255360, 4278255360, 4278255360, 4278211840, 4278211840, 4278211840, 4278211840, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278206208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; // act LosslessUtils.BundleColorMap(row, actual.Length, xBits, actual); @@ -301,19 +304,19 @@ public void BundleColorMap_WithXbitsNoneZero_Works() public void Predictor11_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableSSE2); + public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void Predictor12_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableSSE2); + public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void Predictor13_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE2); + public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void SubtractGreen_Works() => RunSubtractGreenTest(); @@ -328,7 +331,7 @@ public void BundleColorMap_WithXbitsNoneZero_Works() public void SubtractGreen_Scalar_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSSE3); + public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); [Fact] public void AddGreenToBlueAndRed_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.AllowAll); @@ -337,13 +340,13 @@ public void BundleColorMap_WithXbitsNoneZero_Works() public void AddGreenToBlueAndRed_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2); [Fact] - public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableSSSE3); + public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColor_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableSSE2); + public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColor_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableAVX2); @@ -352,7 +355,7 @@ public void BundleColorMap_WithXbitsNoneZero_Works() public void TransformColorInverse_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableSSE2); + public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColorInverse_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs index 73e7044f5b..f4189933fa 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs @@ -13,9 +13,10 @@ public class LossyUtilsTests private static void RunTransformTwoTest() { // arrange - short[] src = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 23, 0, 0, -23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + short[] src = [19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 23, 0, 0, -23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]; byte[] dst = - { + [ 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, @@ -23,16 +24,16 @@ private static void RunTransformTwoTest() 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; byte[] expected = - { + [ 105, 105, 105, 105, 105, 103, 100, 98, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 108, 105, 102, 100, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 111, 109, 106, 103, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 103, 103, 103, 103, 105, 105, 105, 105, 113, 111, 108, 106, 0, 0, 0, 0, 169, 169, 169, 169, 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; int[] scratch = new int[16]; // act @@ -45,9 +46,9 @@ private static void RunTransformTwoTest() private static void RunTransformOneTest() { // arrange - short[] src = { -176, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + short[] src = [-176, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; byte[] dst = - { + [ 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, @@ -55,9 +56,9 @@ private static void RunTransformOneTest() 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129 - }; + ]; byte[] expected = - { + [ 111, 111, 111, 111, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 108, 108, 108, 108, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, @@ -65,7 +66,7 @@ private static void RunTransformOneTest() 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 101, 101, 101, 101, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 129 - }; + ]; int[] scratch = new int[16]; // act @@ -86,7 +87,7 @@ private static void RunVp8Sse16X16Test() a[i] = (byte)rand.Next(byte.MaxValue); b[i] = (byte)rand.Next(byte.MaxValue); } - int[] expected = { 2533110, 2818581, 2984663, 2891188, 2855134, 2634604, 2466504, 3061747, 2626010, 2640965 }; + int[] expected = [2533110, 2818581, 2984663, 2891188, 2855134, 2634604, 2466504, 3061747, 2626010, 2640965]; // act + assert int offset = 0; @@ -110,7 +111,7 @@ private static void RunVp8Sse16X8Test() a[i] = (byte)rand.Next(byte.MaxValue); b[i] = (byte)rand.Next(byte.MaxValue); } - int[] expected = { 1298274, 1234836, 1325264, 1493317, 1551995, 1432668, 1407891, 1483297, 1537930, 1317204 }; + int[] expected = [1298274, 1234836, 1325264, 1493317, 1551995, 1432668, 1407891, 1483297, 1537930, 1317204]; // act + assert int offset = 0; @@ -134,7 +135,7 @@ private static void RunVp8Sse4X4Test() a[i] = (byte)rand.Next(byte.MaxValue); b[i] = (byte)rand.Next(byte.MaxValue); } - int[] expected = { 194133, 125861, 165966, 195688, 106491, 173015, 266960, 200272, 311224, 122545 }; + int[] expected = [194133, 125861, 165966, 195688, 106491, 173015, 266960, 200272, 311224, 122545]; // act + assert int offset = 0; @@ -151,7 +152,7 @@ private static void RunMean16x4Test() { // arrange byte[] input = - { + [ 154, 145, 102, 115, 127, 129, 126, 125, 126, 120, 133, 152, 157, 153, 119, 94, 104, 116, 111, 113, 113, 109, 105, 124, 173, 175, 177, 170, 175, 172, 166, 164, 151, 141, 99, 114, 125, 126, 135, 150, 133, 115, 127, 149, 141, 168, 100, 54, 110, 117, 115, 116, 119, 115, 117, 130, 174, 174, 174, 157, @@ -159,9 +160,9 @@ private static void RunMean16x4Test() 109, 115, 113, 120, 120, 117, 128, 115, 174, 173, 173, 161, 152, 148, 153, 162, 105, 140, 96, 114, 115, 122, 141, 173, 190, 190, 142, 106, 151, 78, 66, 141, 110, 117, 123, 136, 118, 124, 127, 114, 173, 175, 166, 155, 155, 159, 159, 158 - }; + ]; uint[] dc = new uint[4]; - uint[] expectedDc = { 1940, 2139, 2252, 1813 }; + uint[] expectedDc = [1940, 2139, 2252, 1813]; // act LossyUtils.Mean16x4(input, dc); @@ -174,24 +175,24 @@ private static void RunHadamardTransformTest() { // arrange byte[] a = - { + [ 27, 27, 28, 29, 29, 28, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 26, 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 28, 27, 27, 26, 26, 27, 27, 28, 27, 28, 28, 29, 29, 28, 28, 27 - }; + ]; byte[] b = - { + [ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 - }; + ]; - ushort[] w = { 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 }; + ushort[] w = [38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2]; int expected = 2; // act diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index f0961de6bb..16990c3577 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -24,7 +24,7 @@ public void ColorSpaceTransform_WithPeakImage_WithHardwareIntrinsics_Works() [Fact] public void ColorSpaceTransform_WithPeakImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableSSE41); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableHWIntrinsic); [Fact] public void ColorSpaceTransform_WithBikeImage_WithHardwareIntrinsics_Works() @@ -32,7 +32,7 @@ public void ColorSpaceTransform_WithBikeImage_WithHardwareIntrinsics_Works() [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableSSE41); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableHWIntrinsic); [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works() @@ -43,7 +43,7 @@ private static void RunColorSpaceTransformTestWithPeakImage() { // arrange uint[] expectedData = - { + [ 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4294577152, 4294707200, 4294707200, 4294707200, 4294707200, 4294837248, 4294837248, 4293926912, 4294316544, 4278191104, 4278191104, 4294837248, 4294837248, 4280287232, 4280350720, 4294447104, 4294707200, @@ -76,11 +76,11 @@ private static void RunColorSpaceTransformTestWithPeakImage() 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4279503872, 4279503872, 4280288256, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232 - }; + ]; // 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; @@ -100,17 +100,17 @@ private static void RunColorSpaceTransformTestWithBikeImage() { // arrange uint[] expectedData = - { + [ 4278714368, 4278192876, 4278198304, 4278198304, 4278190304, 4278190080, 4278190080, 4278198272, 4278197760, 4278198816, 4278197794, 4278197774, 4278190080, 4278190080, 4278198816, 4278197281, 4278197280, 4278197792, 4278200353, 4278191343, 4278190304, 4294713873, 4278198784, 4294844416, 4278201578, 4278200044, 4278191343, 4278190288, 4294705200, 4294717139, 4278203628, 4278201064, 4278201586, 4278197792, 4279240909 - }; + ]; // 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 @@ private static uint[] ToBgra(Image image) 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/Formats/WebP/QuantEncTests.cs b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs index 59691204be..3808ba6d03 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs @@ -12,13 +12,14 @@ public class QuantEncTests private static unsafe void RunQuantizeBlockTest() { // arrange - short[] input = { 378, 777, -851, 888, 259, 148, 0, -111, -185, -185, -74, -37, 148, 74, 111, 74 }; + short[] input = [378, 777, -851, 888, 259, 148, 0, -111, -185, -185, -74, -37, 148, 74, 111, 74]; short[] output = new short[16]; - ushort[] q = { 42, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37 }; - ushort[] iq = { 3120, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542 }; - uint[] bias = { 49152, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296 }; - uint[] zthresh = { 26, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21 }; - short[] expectedOutput = { 9, 21, 7, -5, 4, -23, 24, 0, -5, 4, 2, -2, -3, -1, 3, 2 }; + ushort[] q = [42, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37]; + ushort[] iq = [3120, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542, 3542]; + uint[] bias = [49152, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296, 55296 + ]; + uint[] zthresh = [26, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21]; + short[] expectedOutput = [9, 21, 7, -5, 4, -23, 24, 0, -5, 4, 2, -2, -3, -1, 3, 2]; int expectedResult = 1; Vp8Matrix vp8Matrix = default; for (int i = 0; i < 16; i++) @@ -44,7 +45,7 @@ private static unsafe void RunQuantizeBlockTest() public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll); [Fact] - public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableSSE2); + public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void QuantizeBlock_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs index dab0716927..c0b14cdd7e 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs @@ -12,12 +12,14 @@ public class Vp8EncodingTests private static void RunFTransform2Test() { // arrange - byte[] src = { 154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175 }; - byte[] reference = { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 }; + byte[] src = [154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175 + ]; + byte[] reference = [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 + ]; short[] actualOutput1 = new short[16]; short[] actualOutput2 = new short[16]; - short[] expectedOutput1 = { 182, 4, 1, 1, 6, 7, -1, -4, 5, 0, -2, 1, 2, 1, 1, 1 }; - short[] expectedOutput2 = { 192, -34, 10, 1, -11, 8, 10, -7, 6, 3, -8, 4, 5, -3, -2, 6 }; + short[] expectedOutput1 = [182, 4, 1, 1, 6, 7, -1, -4, 5, 0, -2, 1, 2, 1, 1, 1]; + short[] expectedOutput2 = [192, -34, 10, 1, -11, 8, 10, -7, 6, 3, -8, 4, 5, -3, -2, 6]; // act Vp8Encoding.FTransform2(src, reference, actualOutput1, actualOutput2, new int[16]); @@ -31,7 +33,7 @@ private static void RunFTransformTest() { // arrange byte[] src = - { + [ 154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, @@ -39,9 +41,9 @@ private static void RunFTransformTest() 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175 - }; + ]; byte[] reference = - { + [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, @@ -49,9 +51,9 @@ private static void RunFTransformTest() 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 - }; + ]; short[] actualOutput = new short[16]; - short[] expectedOutput = { 182, 4, 1, 1, 6, 7, -1, -4, 5, 0, -2, 1, 2, 1, 1, 1 }; + short[] expectedOutput = [182, 4, 1, 1, 6, 7, -1, -4, 5, 0, -2, 1, 2, 1, 1, 1]; // act Vp8Encoding.FTransform(src, reference, actualOutput, new int[16]); @@ -64,7 +66,7 @@ private static void RunOneInverseTransformTest() { // arrange byte[] reference = - { + [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, @@ -72,17 +74,18 @@ private static void RunOneInverseTransformTest() 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 - }; - short[] input = { 1, 216, -48, 0, 96, -24, -48, 24, 0, -24, 24, 0, 0, 0, 0, 0, 38, -240, -72, -24, 0, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ]; + short[] input = [1, 216, -48, 0, 96, -24, -48, 24, 0, -24, 24, 0, 0, 0, 0, 0, 38, -240, -72, -24, 0, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]; byte[] dst = new byte[128]; byte[] expected = - { + [ 161, 160, 149, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 160, 133, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 147, 109, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 128, 87, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; int[] scratch = new int[16]; // act @@ -96,7 +99,7 @@ private static void RunTwoInverseTransformTest() { // arrange byte[] reference = - { + [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, @@ -104,17 +107,18 @@ private static void RunTwoInverseTransformTest() 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 - }; - short[] input = { 1, 216, -48, 0, 96, -24, -48, 24, 0, -24, 24, 0, 0, 0, 0, 0, 38, -240, -72, -24, 0, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ]; + short[] input = [1, 216, -48, 0, 96, -24, -48, 24, 0, -24, 24, 0, 0, 0, 0, 0, 38, -240, -72, -24, 0, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]; byte[] dst = new byte[128]; byte[] expected = - { + [ 161, 160, 149, 105, 78, 127, 156, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 160, 133, 85, 81, 129, 155, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 147, 109, 76, 85, 130, 153, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 128, 87, 83, 88, 132, 152, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; int[] scratch = new int[16]; // act diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs index a18eff73ce..990c583856 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs @@ -13,7 +13,7 @@ public static IEnumerable Data { get { - var result = new List(); + List result = new(); result.Add(new object[] { new byte[] @@ -69,7 +69,7 @@ public static IEnumerable Data private static void RunCollectHistogramTest() { // arrange - var histogram = new Vp8Histogram(); + Vp8Histogram histogram = new(); byte[] reference = { @@ -172,7 +172,7 @@ private static void RunCollectHistogramTest() public void GetAlpha_WithEmptyHistogram_Works() { // arrange - var histogram = new Vp8Histogram(); + Vp8Histogram histogram = new(); // act int alpha = histogram.GetAlpha(); @@ -186,7 +186,7 @@ public void GetAlpha_WithEmptyHistogram_Works() public void GetAlpha_Works(byte[] reference, byte[] pred) { // arrange - var histogram = new Vp8Histogram(); + Vp8Histogram histogram = new(); histogram.CollectHistogram(reference, pred, 0, 1); // act @@ -201,9 +201,9 @@ public void GetAlpha_Works(byte[] reference, byte[] pred) public void Merge_Works(byte[] reference, byte[] pred) { // arrange - var histogram1 = new Vp8Histogram(); + Vp8Histogram histogram1 = new(); histogram1.CollectHistogram(reference, pred, 0, 1); - var histogram2 = new Vp8Histogram(); + Vp8Histogram histogram2 = new(); histogram1.Merge(histogram2); // act diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs index cfe79e49e6..8d8bcb57c3 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs @@ -14,7 +14,7 @@ private static void RunAddVectorTest() { // arrange uint[] pixelData = - { + [ 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4294577152, 4294707200, 4294707200, 4294707200, 4294707200, 4294837248, 4294837248, 4293926912, 4294316544, 4278191104, 4278191104, 4294837248, 4294837248, 4280287232, 4280350720, 4294447104, 4294707200, @@ -47,10 +47,10 @@ private static void RunAddVectorTest() 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4279503872, 4279503872, 4280288256, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232 - }; + ]; uint[] literals = - { + [ 198, 0, 14, 0, 46, 0, 22, 0, 36, 0, 24, 0, 12, 0, 10, 0, 10, 0, 2, 0, 2, 0, 0, 0, 0, 0, 6, 0, 0, 0, 10, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -59,25 +59,21 @@ private static void RunAddVectorTest() 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 2, 0, 2, 0, 0, 0, 8, 0, 2, 0, 38, 0, 4 - }; + ]; uint[] expectedLiterals = new uint[1305]; // All remaining values are expected to be zero. literals.AsSpan().CopyTo(expectedLiterals); - Vp8LBackwardRefs backwardRefs = new(pixelData.Length); + MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator; + + using Vp8LBackwardRefs backwardRefs = new(memoryAllocator, pixelData.Length); for (int i = 0; i < pixelData.Length; i++) { - backwardRefs.Add(new PixOrCopy() - { - BgraOrDistance = pixelData[i], - Len = 1, - Mode = PixOrCopyMode.Literal - }); + backwardRefs.Add(PixOrCopy.CreateLiteral(pixelData[i])); } - MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator; using OwnedVp8LHistogram histogram0 = OwnedVp8LHistogram.Create(memoryAllocator, backwardRefs, 3); using OwnedVp8LHistogram histogram1 = OwnedVp8LHistogram.Create(memoryAllocator, backwardRefs, 3); for (int i = 0; i < 5; i++) diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ModeScoreTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ModeScoreTests.cs index 0b85ececb9..ded637967a 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8ModeScoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ModeScoreTests.cs @@ -11,7 +11,7 @@ public class Vp8ModeScoreTests [Fact] public void InitScore_Works() { - var score = new Vp8ModeScore(); + Vp8ModeScore score = new(); score.InitScore(); Assert.Equal(0, score.D); Assert.Equal(0, score.SD); @@ -25,7 +25,7 @@ public void InitScore_Works() public void CopyScore_Works() { // arrange - var score1 = new Vp8ModeScore + Vp8ModeScore score1 = new() { Score = 123, Nz = 1, @@ -36,7 +36,7 @@ public void CopyScore_Works() R = 6, SD = 7 }; - var score2 = new Vp8ModeScore(); + Vp8ModeScore score2 = new(); score2.InitScore(); // act @@ -55,7 +55,7 @@ public void CopyScore_Works() public void AddScore_Works() { // arrange - var score1 = new Vp8ModeScore + Vp8ModeScore score1 = new() { Score = 123, Nz = 1, @@ -66,7 +66,7 @@ public void AddScore_Works() R = 6, SD = 7 }; - var score2 = new Vp8ModeScore + Vp8ModeScore score2 = new() { Score = 123, Nz = 1, diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs index 4982929c2c..2a0821108a 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs @@ -239,7 +239,7 @@ private static void RunSetCoeffsTest() { // arrange Vp8Residual residual = new(); - short[] coeffs = { 110, 0, -2, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }; + short[] coeffs = [110, 0, -2, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0]; // act residual.SetCoeffs(coeffs); @@ -252,5 +252,5 @@ private static void RunSetCoeffsTest() public void SetCoeffsTest_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); [Fact] - public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableSSE2); + public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs index a3fe028db5..3962d4f37d 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs @@ -23,7 +23,7 @@ public void CheckNonOpaque_WithOpaquePixels_WithHardwareIntrinsics_Works() [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableSSE2); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutAvx2_Works() @@ -35,7 +35,7 @@ public void CheckNonOpaque_WithNoneOpaquePixels_WithHardwareIntrinsics_Works() [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableSSE2); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works() @@ -45,7 +45,7 @@ private static void RunCheckNoneOpaqueWithNoneOpaquePixelsTest() { // arrange byte[] rowBytes = - { + [ 122, 120, 101, 255, 171, 165, 151, 255, 209, 208, 210, 255, @@ -104,9 +104,9 @@ private static void RunCheckNoneOpaqueWithNoneOpaquePixelsTest() 171, 165, 151, 0, 209, 208, 210, 100, 174, 183, 189, 255, - 148, 158, 158, 255, - }; - Span row = MemoryMarshal.Cast(rowBytes); + 148, 158, 158, 255 + ]; + ReadOnlySpan row = MemoryMarshal.Cast(rowBytes); bool noneOpaque; for (int length = 8; length < row.Length; length += 8) @@ -127,7 +127,7 @@ private static void RunCheckNoneOpaqueWithOpaquePixelsTest() { // arrange byte[] rowBytes = - { + [ 122, 120, 101, 255, 171, 165, 151, 255, 209, 208, 210, 255, @@ -186,9 +186,9 @@ private static void RunCheckNoneOpaqueWithOpaquePixelsTest() 171, 165, 151, 255, 209, 208, 210, 255, 174, 183, 189, 255, - 148, 158, 158, 255, - }; - Span row = MemoryMarshal.Cast(rowBytes); + 148, 158, 158, 255 + ]; + ReadOnlySpan row = MemoryMarshal.Cast(rowBytes); bool noneOpaque; for (int length = 8; length < row.Length; length += 8) diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 0dda304b64..a3e3b81cf0 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -18,7 +19,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); @@ -30,8 +31,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)] @@ -313,6 +314,21 @@ public void Decode_AnimatedLossless_VerifyAllFrames(TestImageProvider(TestImageProvider provider) @@ -330,6 +346,21 @@ public void Decode_AnimatedLossy_VerifyAllFrames(TestImageProvider(TestImageProvider provider) @@ -348,7 +379,7 @@ public void Decode_AnimatedLossy_IgnoreBackgroundColor_Works(TestImagePr WebpDecoderOptions options = new() { BackgroundColorHandling = BackgroundColorHandling.Ignore, - GeneralOptions = new DecoderOptions() + GeneralOptions = new DecoderOptions { MaxFrames = 1 } @@ -387,7 +418,7 @@ public void WebpDecoder_Decode_Resize(TestImageProvider provider { DecoderOptions options = new() { - TargetSize = new() { Width = 150, Height = 150 } + TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(WebpDecoder.Instance, options); @@ -450,6 +481,22 @@ public void WebpDecoder_CanDecode_Issue2670(TestImageProvider pr image.CompareToOriginal(provider, ReferenceDecoder); } + // https://github.com/SixLabors/ImageSharp/issues/2866 + [Theory] + [WithFile(Lossy.Issue2866, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2866(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // Web + using Image image = provider.GetImage( + WebpDecoder.Instance, + new WebpDecoderOptions { BackgroundColorHandling = BackgroundColorHandling.Ignore }); + + // We can't use the reference decoder here. + // It creates frames of different size without blending the frames. + image.DebugSave(provider, extension: "webp", encoder: new WebpEncoder()); + } + [Theory] [WithFile(Lossless.LossLessCorruptImage3, PixelTypes.Rgba32)] public void WebpDecoder_ThrowImageFormatException_OnInvalidImages(TestImageProvider provider) @@ -533,4 +580,45 @@ public void Identify_VerifyRatio(string imagePath) Assert.Equal(37.8, meta.VerticalResolution); Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, meta.ResolutionUnits); } + + [Theory] + [WithFile(Lossy.Issue2925, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2925(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(WebpDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); + } + + [Theory] + [WithFile(Lossy.Issue2906, PixelTypes.Rgba32)] + public void WebpDecoder_CanDecode_Issue2906(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(WebpDecoder.Instance); + + ExifProfile exifProfile = image.Metadata.ExifProfile; + IExifValue comment = exifProfile.GetValue(ExifTag.UserComment); + + Assert.NotNull(comment); + Assert.Equal(EncodedString.CharacterCode.Unicode, comment.Value.Code); + Assert.StartsWith("1girl, pariya, ", comment.Value.Text); + + image.DebugSave(provider); + image.CompareToOriginal(provider, ReferenceDecoder); + } + + [Theory] + [WithFile(Icc.Perceptual, PixelTypes.Rgba32)] + [WithFile(Icc.PerceptualcLUTOnly, PixelTypes.Rgba32)] + public void Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(WebpDecoder.Instance, new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert }); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + Assert.Null(image.Metadata.IccProfile); + } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index c6751e2a66..73bb1457da 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -2,11 +2,16 @@ // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; 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; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -19,6 +24,70 @@ public class WebpEncoderTests { private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.NoFilter06); + /// + /// Selected EXIF parts are respected whether the lazy profile is installed before or after synchronization. + /// + /// Whether the stream installs the profile after metadata synchronization. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Encode_LazyExifProfile_AppliesSelectedParts(bool reentrant) + { + ExifProfile source = new(); + source.SetValue(ExifTag.Make, "POC"); + source.SetValue(ExifTag.GPSLatitudeRef, "N"); + ExifProfile filteredLazy = new(source.ToByteArray()) + { + Parts = ExifParts.IfdTags | ExifParts.ExifTags + }; + + using Image image = new(1, 1); + using MemoryStream output = reentrant + ? new SwapOnCanSeekStream(() => image.Metadata.ExifProfile = filteredLazy) + : new MemoryStream(); + + if (!reentrant) + { + image.Metadata.ExifProfile = filteredLazy; + } + + image.SaveAsWebp(output); + output.Position = 0; + using Image decoded = Image.Load(output); + + Assert.NotNull(decoded.Metadata.ExifProfile); + Assert.True(decoded.Metadata.ExifProfile.TryGetValue(ExifTag.Make, out IExifValue make)); + Assert.Equal("POC", make.Value); + Assert.False(decoded.Metadata.ExifProfile.TryGetValue(ExifTag.GPSLatitudeRef, out _)); + } + + /// + /// Replaces metadata at the stream capability check, after encoder synchronization has completed. + /// + private sealed class SwapOnCanSeekStream : MemoryStream + { + private Action callback; + + /// + /// Initializes a stream that invokes the callback on its first capability check. + /// + /// The metadata replacement callback. + public SwapOnCanSeekStream(Action callback) => this.callback = callback; + + /// + public override bool CanSeek + { + get + { + Action action = this.callback; + this.callback = null; + action?.Invoke(); + + return base.CanSeek; + } + } + } + [Theory] [WithFile(Lossless.Animated, PixelTypes.Rgba32)] public void Encode_AnimatedLossless(TestImageProvider provider) @@ -94,21 +163,78 @@ public void Encode_AnimatedFormatTransform_FromGif(TestImageProvider(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + WebpEncoder encoder = new() + { + FileFormat = WebpFileFormatType.Lossless + }; + + QuantizerOptions options = new() + { + TransparencyThreshold = 128 / 255F + }; + + // First save as gif to gif using different quantizers with default options. + // Alpha thresholding is 64/255F. + GifEncoder gifEncoder = new() + { + Quantizer = new HexadecatreeQuantizer(options) + }; + provider.Utility.SaveTestOutputFile(image, "gif", gifEncoder, "hexadecatree"); + + gifEncoder = new GifEncoder + { + Quantizer = new WuQuantizer(options) + }; + provider.Utility.SaveTestOutputFile(image, "gif", gifEncoder, "wu"); + + // Now clone and quantize the image using the same quantizers without alpha thresholding and save as webp. + options = new QuantizerOptions + { + TransparencyThreshold = 0 + }; + + using Image cloned1 = image.Clone(); + cloned1.Mutate(c => c.Quantize(new HexadecatreeQuantizer(options))); + provider.Utility.SaveTestOutputFile(cloned1, "webp", encoder, "hexadecatree"); + + using Image cloned2 = image.Clone(); + cloned2.Mutate(c => c.Quantize(new WuQuantizer(options))); + provider.Utility.SaveTestOutputFile(cloned2, "webp", encoder, "wu"); + + // Now blend the images with a blue background and save as webp. + using Image background1 = new(image.Width, image.Height, Color.White.ToPixel()); + background1.Mutate(c => c.DrawImage(cloned1, 1)); + provider.Utility.SaveTestOutputFile(background1, "webp", encoder, "hexadecatree-blended"); + + using Image background2 = new(image.Width, image.Height, Color.White.ToPixel()); + background2.Mutate(c => c.DrawImage(cloned2, 1)); + provider.Utility.SaveTestOutputFile(background2, "webp", encoder, "wu-blended"); + } + [Theory] [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] public void Encode_AnimatedFormatTransform_FromPng(TestImageProvider provider) @@ -143,35 +269,36 @@ public void Encode_AnimatedFormatTransform_FromPng(TestImageProvider(TestImageProvider provider, WebpFileFormatType expectedFormat) + public void Encode_PreserveEncodingType(TestImageProvider provider, WebpFileFormatType expectedFormat) where TPixel : unmanaged, IPixel { WebpEncoder options = new(); @@ -219,7 +346,7 @@ public void Encode_Lossless_WithDifferentQuality_Works(TestImageProvider }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_q", quality); + string testOutputDetails = $"lossless_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -249,7 +376,7 @@ public void Encode_Lossless_WithDifferentMethodAndQuality_Works(TestImag }; 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); } @@ -289,7 +416,7 @@ public void Encode_Lossless_WithNearLosslessFlag_Works(TestImageProvider }; 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)); } @@ -309,11 +436,11 @@ public void Encode_Lossless_WithPreserveTransparentColor_Works(TestImage { FileFormat = WebpFileFormatType.Lossless, Method = method, - TransparentColorMode = WebpTransparentColorMode.Preserve + TransparentColorMode = TransparentColorMode.Preserve }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_m", method); + string testOutputDetails = $"lossless_m{method}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -343,7 +470,7 @@ public void Encode_Lossy_WithDifferentQuality_Works(TestImageProvider image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_q", quality); + string testOutputDetails = $"lossy_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); } @@ -363,7 +490,7 @@ public void Encode_Lossy_WithDifferentFilterStrength_Works(TestImageProv }; 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)); } @@ -383,7 +510,7 @@ public void Encode_Lossy_WithDifferentSpatialNoiseShapingStrength_Works( }; 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)); } @@ -413,7 +540,7 @@ public void Encode_Lossy_WithDifferentMethodsAndQuality_Works(TestImageP }; 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)); } @@ -439,7 +566,7 @@ public void Encode_Lossy_WithAlpha_Works(TestImageProvider provi "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}"); @@ -467,7 +594,7 @@ public void Encode_Lossy_WithAlphaUsingCompression_Works(TestImageProvid "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/WebpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs index ab8fef60f7..324070e4a5 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs @@ -1,9 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers.Binary; +using System.Text; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -13,6 +16,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [Trait("Format", "Webp")] public class WebpMetaDataTests { + public static IEnumerable IccMetadataOptions() + { + foreach (SegmentIntegrityHandling integrity in new[] { SegmentIntegrityHandling.Strict, SegmentIntegrityHandling.IgnoreAncillary, SegmentIntegrityHandling.IgnoreImageData }) + { + foreach (bool skipMetadata in new[] { false, true }) + { + foreach (bool animated in new[] { false, true }) + { + yield return new object[] { integrity, skipMetadata, animated }; + } + } + } + } + + public static IEnumerable TruncatedMetadataOptions() + { + foreach (string chunkType in new[] { "EXIF", "XMP " }) + { + foreach (uint length in new[] { 0x40000000U, 0xFFFFFFFEU, uint.MaxValue }) + { + foreach (SegmentIntegrityHandling integrity in new[] { SegmentIntegrityHandling.Strict, SegmentIntegrityHandling.IgnoreAncillary, SegmentIntegrityHandling.IgnoreImageData }) + { + yield return new object[] { chunkType, length, integrity, false }; + yield return new object[] { chunkType, length, integrity, true }; + } + } + } + } + [Theory] [WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, false)] [WithFile(TestImages.Webp.Lossy.BikeWithExif, PixelTypes.Rgba32, true)] @@ -103,9 +135,9 @@ public async Task IgnoreMetadata_ControlsWhetherXmpIsParsed(TestImagePro public void Encode_WritesExifWithPadding(WebpFileFormatType fileFormatType) { // arrange - using var input = new Image(25, 25); - using var memoryStream = new MemoryStream(); - var expectedExif = new ExifProfile(); + using Image input = new(25, 25); + using MemoryStream memoryStream = new(); + ExifProfile expectedExif = new(); string expectedSoftware = "ImageSharp"; expectedExif.SetValue(ExifTag.Software, expectedSoftware); input.Metadata.ExifProfile = expectedExif; @@ -115,7 +147,7 @@ public void Encode_WritesExifWithPadding(WebpFileFormatType fileFormatType) memoryStream.Position = 0; // assert - using var image = Image.Load(memoryStream); + using Image image = Image.Load(memoryStream); ExifProfile actualExif = image.Metadata.ExifProfile; Assert.NotNull(actualExif); Assert.Equal(expectedExif.Values.Count, actualExif.Values.Count); @@ -129,7 +161,7 @@ public void EncodeLossyWebp_PreservesExif(TestImageProvider prov { // arrange using Image input = provider.GetImage(WebpDecoder.Instance); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); ExifProfile expectedExif = input.Metadata.ExifProfile; // act @@ -137,7 +169,7 @@ public void EncodeLossyWebp_PreservesExif(TestImageProvider prov memoryStream.Position = 0; // assert - using var image = Image.Load(memoryStream); + using Image image = Image.Load(memoryStream); ExifProfile actualExif = image.Metadata.ExifProfile; Assert.NotNull(actualExif); Assert.Equal(expectedExif.Values.Count, actualExif.Values.Count); @@ -150,7 +182,7 @@ public void EncodeLosslessWebp_PreservesExif(TestImageProvider p { // arrange using Image input = provider.GetImage(WebpDecoder.Instance); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); ExifProfile expectedExif = input.Metadata.ExifProfile; // act @@ -158,7 +190,7 @@ public void EncodeLosslessWebp_PreservesExif(TestImageProvider p memoryStream.Position = 0; // assert - using var image = Image.Load(memoryStream); + using Image image = Image.Load(memoryStream); ExifProfile actualExif = image.Metadata.ExifProfile; Assert.NotNull(actualExif); Assert.Equal(expectedExif.Values.Count, actualExif.Values.Count); @@ -174,14 +206,14 @@ public void Encode_PreservesColorProfile(TestImageProvider provi ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile; byte[] expectedProfileBytes = expectedProfile.ToByteArray(); - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); input.Save(memStream, new WebpEncoder() { FileFormat = fileFormat }); memStream.Position = 0; - using var output = Image.Load(memStream); + using Image output = Image.Load(memStream); ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile; byte[] actualProfileBytes = actualProfile.ToByteArray(); @@ -200,4 +232,180 @@ public void WebpDecoder_IgnoresInvalidExifChunk(TestImageProvider 0); + Assert.True(info.Height > 0); + } + + [Fact] + public void Identify_InvalidExifChunk_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + using MemoryStream stream = new(TestFile.Create(TestImages.Webp.Lossy.WithExifNotEnoughData).Bytes, false); + Assert.Throws(() => Image.Identify(options, stream)); + } + + [Fact] + public void Decode_InvalidExifChunk_ThrowsWithStrict() + { + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + using MemoryStream stream = new(TestFile.Create(TestImages.Webp.Lossy.WithExifNotEnoughData).Bytes, false); + Assert.Throws(() => + { + using Image image = Image.Load(options, stream); + }); + } + + [Theory] + [InlineData("ICCP", 0xFFFFFFFEU)] + [InlineData("EXIF", 0xFFFFFFFEU)] + [InlineData("XMP ", 0xFFFFFFFEU)] + [InlineData("ICCP", uint.MaxValue)] + [InlineData("EXIF", uint.MaxValue)] + [InlineData("XMP ", uint.MaxValue)] + public void Decode_WithOversizedMetadataChunk_ThrowsInvalidImageContentException(string chunkType, uint length) + { + byte[] payload = Convert.FromHexString( + "524946462200000057454250565038580A0000002000000000000000000049434350FEFFFFFF01020304"); + Encoding.ASCII.GetBytes(chunkType, payload.AsSpan(30, 4)); + BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(34), length); + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + + [Theory] + [InlineData("ICCP")] + [InlineData("EXIF")] + [InlineData("XMP ")] + public void Decode_WithMetadataChunkLargerThanRemainingData_ThrowsInStrictMode(string chunkType) + { + byte[] payload = Convert.FromHexString( + "524946460000000057454250565038580A00000020000000010000010000494343500000004000000000"); + Encoding.ASCII.GetBytes(chunkType, payload.AsSpan(30, 4)); + DecoderOptions options = new() { SegmentIntegrityHandling = SegmentIntegrityHandling.Strict }; + + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + + [Theory] + [MemberData(nameof(TruncatedMetadataOptions))] + public void Decode_TruncatedTrailingMetadata_RespectsOptions(string chunkType, uint length, SegmentIntegrityHandling integrity, bool skipMetadata) + { + byte[] payload = CreateWebpWithMetadata(chunkType, length, false, false); + DecoderOptions options = new() { SegmentIntegrityHandling = integrity, SkipMetadata = skipMetadata }; + + if (integrity is SegmentIntegrityHandling.Strict && !skipMetadata) + { + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + else + { + using Image image = Image.Load(options, payload); + Assert.Equal(new Size(2, 2), image.Size); + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + Assert.Equal(new Rgba32(17, 34, 51), image[x, y]); + } + } + + Assert.Null(image.Metadata.ExifProfile); + Assert.Null(image.Metadata.XmpProfile); + + ImageInfo info = Image.Identify(options, payload); + Assert.Equal(image.Size, info.Size); + Assert.Null(info.Metadata.ExifProfile); + Assert.Null(info.Metadata.XmpProfile); + } + } + + [Theory] + [MemberData(nameof(IccMetadataOptions))] + public void Decode_InvalidIccPayload_RespectsOptionsAndReadsFollowingImage(SegmentIntegrityHandling integrity, bool skipMetadata, bool animated) + { + byte[] payload = CreateWebpWithMetadata("ICCP", 4, true, animated); + DecoderOptions options = new() { SegmentIntegrityHandling = integrity, SkipMetadata = skipMetadata }; + + if (integrity is SegmentIntegrityHandling.Strict && !skipMetadata) + { + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + else + { + using Image image = Image.Load(options, payload); + Assert.Equal(new Size(2, 2), image.Size); + Assert.Equal(animated ? 2 : 1, image.Frames.Count); + Assert.Equal(new Rgba32(17, 34, 51), image[0, 0]); + Assert.Null(image.Metadata.IccProfile); + + ImageInfo info = Image.Identify(options, payload); + Assert.Equal(image.Size, info.Size); + Assert.Null(info.Metadata.IccProfile); + } + } + + [Theory] + [MemberData(nameof(IccMetadataOptions))] + public void Decode_TruncatedIccFraming_RemainsFatal(SegmentIntegrityHandling integrity, bool skipMetadata, bool animated) + { + byte[] payload = CreateWebpWithMetadata("ICCP", 0x40000000, true, animated); + DecoderOptions options = new() { SegmentIntegrityHandling = integrity, SkipMetadata = skipMetadata }; + + Assert.Throws(() => Image.Load(options, payload)); + Assert.Throws(() => Image.Identify(options, payload)); + } + + /// + /// Places a metadata declaration around a complete lossless image to test recovery independently of pixel truncation. + /// + private static byte[] CreateWebpWithMetadata(string chunkType, uint length, bool beforeImage, bool animated) + { + byte[] header = Convert.FromHexString( + "524946460000000057454250565038580A00000020000000010000010000494343500000004000000000"); + header[20] = chunkType switch { "ICCP" => 0x20, "EXIF" => 0x08, _ => 0x04 }; + Encoding.ASCII.GetBytes(chunkType, header.AsSpan(30, 4)); + BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(34), length); + + using Image source = new(2, 2, new Rgba32(17, 34, 51)); + if (animated) + { + header[20] |= 0x02; + using Image secondFrame = new(2, 2, new Rgba32(51, 34, 17)); + source.Frames.AddFrame(secondFrame.Frames.RootFrame); + } + + using MemoryStream encoded = new(); + source.Save(encoded, new WebpEncoder { FileFormat = WebpFileFormatType.Lossless }); + byte[] imageData = encoded.ToArray(); + using MemoryStream combined = new(); + combined.Write(header.AsSpan(0, 30)); + if (beforeImage) + { + combined.Write(header.AsSpan(30)); + } + + // Replace the encoder's extended header when present, keeping its complete + // image or animation chunks and the deliberately chosen metadata declaration. + int imageChunkOffset = imageData.AsSpan(12, 4).SequenceEqual("VP8X"u8) ? 30 : 12; + combined.Write(imageData.AsSpan(imageChunkOffset)); + if (!beforeImage) + { + combined.Write(header.AsSpan(30)); + } + + byte[] payload = combined.ToArray(); + BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(4), (uint)payload.Length - 8); + return payload; + } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpVp8XTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpVp8XTests.cs new file mode 100644 index 0000000000..78be0bf9fd --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpVp8XTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Webp.Chunks; + +namespace SixLabors.ImageSharp.Tests.Formats.WebP; + +[Trait("Format", "Webp")] +public class WebpVp8XTests +{ + [Fact] + public void WebpVp8X_WriteTo_Writes_Reserved_Bytes() + { + // arrange + WebpVp8X header = new(false, false, false, false, false, 10, 40); + MemoryStream ms = new(); + byte[] expected = [86, 80, 56, 88, 10, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 39, 0, 0]; + + // act + header.WriteTo(ms); + + // assert + byte[] actual = ms.ToArray(); + Assert.Equal(expected, actual); + } +} diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs index 3ae6601b18..482c41b251 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs @@ -13,13 +13,13 @@ 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); public static void RunUpSampleYuvToRgbTest() { - var provider = TestImageProvider.File(TestImageLossyFullPath); + TestImageProvider provider = TestImageProvider.File(TestImageLossyFullPath); using Image image = provider.GetImage(WebpDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder); @@ -32,7 +32,7 @@ public static void RunUpSampleYuvToRgbTest() public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll); [Fact] - public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE2); + public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableHWIntrinsic); [Theory] [WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)] @@ -52,7 +52,7 @@ public void ConvertRgbToYuv_Works(TestImageProvider provider) Span u = uBuffer.GetSpan(); Span v = vBuffer.GetSpan(); byte[] expectedY = - { + [ 82, 82, 82, 82, 128, 135, 134, 129, 167, 179, 176, 172, 192, 201, 200, 204, 188, 172, 175, 177, 168, 151, 154, 154, 153, 152, 151, 151, 152, 160, 160, 160, 160, 82, 82, 82, 82, 140, 137, 135, 116, 174, 183, 176, 162, 196, 199, 199, 210, 188, 166, 176, 181, 170, 145, 155, 154, 153, 154, 151, 151, 150, @@ -106,9 +106,9 @@ public void ConvertRgbToYuv_Works(TestImageProvider provider) 86, 75, 72, 77, 106, 96, 97, 96, 97, 100, 100, 100, 160, 160, 160, 159, 169, 170, 168, 170, 129, 115, 117, 123, 90, 71, 76, 79, 62, 51, 51, 47, 59, 79, 75, 74, 81, 100, 97, 98, 98, 100, 100, 100, 100 - }; + ]; byte[] expectedU = - { + [ 90, 90, 59, 63, 36, 38, 23, 20, 34, 35, 47, 48, 70, 82, 104, 121, 121, 90, 90, 61, 69, 37, 42, 22, 18, 33, 32, 47, 47, 67, 75, 97, 113, 120, 59, 61, 30, 37, 22, 20, 38, 36, 50, 50, 78, 83, 113, 122, 142, 166, 164, 63, 69, 37, 43, 20, 18, 34, 32, 48, 47, 70, 73, 102, 110, 136, 166, 166, 36, 37, 22, @@ -123,9 +123,9 @@ public void ConvertRgbToYuv_Works(TestImageProvider provider) 184, 212, 208, 227, 228, 227, 229, 218, 214, 196, 185, 188, 121, 113, 166, 166, 197, 191, 220, 217, 232, 237, 223, 222, 211, 208, 185, 173, 172, 121, 120, 164, 166, 193, 194, 217, 219, 232, 235, 224, 220, 212, 207, 188, 172, 172 - }; + ]; byte[] expectedV = - { + [ 240, 240, 201, 206, 172, 174, 136, 136, 92, 90, 55, 50, 37, 30, 26, 23, 23, 240, 240, 204, 213, 173, 179, 141, 141, 96, 98, 56, 54, 38, 31, 27, 25, 23, 201, 204, 164, 172, 129, 135, 82, 87, 46, 47, 33, 29, 25, 23, 20, 16, 16, 206, 213, 172, 180, 137, 141, 93, 99, 54, 54, 36, 31, 26, 25, 21, 17, 16, @@ -139,7 +139,7 @@ public void ConvertRgbToYuv_Works(TestImageProvider provider) 118, 151, 164, 188, 205, 206, 26, 27, 20, 21, 43, 39, 74, 69, 103, 102, 138, 143, 174, 188, 204, 216, 218, 23, 25, 16, 17, 55, 48, 85, 81, 117, 118, 159, 164, 195, 205, 216, 227, 227, 23, 23, 16, 16, 51, 52, 81, 83, 116, 122, 157, 168, 194, 206, 218, 227, 227 - }; + ]; // act YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame.PixelBuffer.GetRegion(), config, memoryAllocator, y, u, v); @@ -169,7 +169,7 @@ public void ConvertRgbToYuv_WithAlpha_Works(TestImageProvider pr Span u = uBuffer.GetSpan(); Span v = vBuffer.GetSpan(); byte[] expectedY = - { + [ 16, 16, 16, 16, 16, 235, 235, 235, 235, 235, 16, 16, 16, 16, 16, 152, 41, 41, 152, 152, 41, 41, 152, 152, 41, 41, 152, 152, 41, 41, 152, 16, 16, 16, 16, 16, 235, 235, 235, 235, 235, 16, 16, 16, 16, 16, 152, 41, 41, 152, 152, 41, 41, 152, 152, 41, 41, 152, 152, 41, 41, 152, 16, 16, 16, 16, 16, 235, @@ -213,9 +213,9 @@ public void ConvertRgbToYuv_WithAlpha_Works(TestImageProvider pr 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 29, 106, 183, 66, 143, 130, 78, 90, 168, 116, 103, 180, 63, 140, 152, 75, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 93, 171, 53, 131, 118, 66, 78, 155, 103, 90, 167, 50, 128, 140, 63, 75 - }; + ]; byte[] expectedU = - { + [ 128, 128, 128, 128, 128, 128, 128, 139, 240, 139, 240, 139, 240, 139, 240, 139, 128, 128, 128, 128, 128, 128, 128, 139, 240, 139, 240, 139, 240, 139, 240, 139, 128, 128, 128, 128, 128, 128, 128, 139, 240, 139, 240, 139, 240, 139, 240, 139, 128, 128, 128, 128, 128, 128, 128, 139, 240, 139, 240, 139, @@ -229,9 +229,9 @@ public void ConvertRgbToYuv_WithAlpha_Works(TestImageProvider pr 159, 159, 159, 159, 159, 159, 159, 161, 112, 113, 138, 87, 143, 112, 88, 161, 240, 240, 240, 240, 240, 240, 240, 137, 110, 162, 110, 140, 158, 104, 159, 137, 240, 240, 240, 240, 240, 240, 240, 109, 150, 108, 140, 161, 80, 157, 162, 128 - }; + ]; byte[] expectedV = - { + [ 128, 128, 128, 128, 128, 128, 128, 189, 110, 189, 110, 189, 110, 189, 110, 189, 128, 128, 128, 128, 128, 128, 128, 189, 110, 189, 110, 189, 110, 189, 110, 189, 128, 128, 128, 128, 128, 128, 128, 189, 110, 189, 110, 189, 110, 189, 110, 189, 128, 128, 128, 128, 128, 128, 128, 189, 110, 189, 110, 189, @@ -245,7 +245,7 @@ public void ConvertRgbToYuv_WithAlpha_Works(TestImageProvider pr 141, 165, 126, 147, 173, 101, 101, 101, 101, 101, 101, 101, 135, 109, 129, 122, 124, 107, 108, 128, 138, 110, 110, 110, 110, 110, 110, 110, 117, 137, 151, 127, 114, 131, 139, 142, 120, 110, 110, 110, 110, 110, 110, 110, 142, 156, 119, 137, 167, 141, 151, 66, 85 - }; + ]; // act YuvConversion.ConvertRgbToYuv(image.Frames.RootFrame.PixelBuffer.GetRegion(), config, memoryAllocator, y, u, v); diff --git a/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs b/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs index d663a803b3..682f5373db 100644 --- a/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs +++ b/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs @@ -12,9 +12,9 @@ public class GraphicOptionsDefaultsExtensionsTests [Fact] public void SetDefaultOptionsOnProcessingContext() { - var option = new GraphicsOptions(); - var config = new Configuration(); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + GraphicsOptions option = new(); + Configuration config = new(); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); context.SetGraphicsOptions(option); @@ -26,12 +26,12 @@ public void SetDefaultOptionsOnProcessingContext() [Fact] public void UpdateDefaultOptionsOnProcessingContext_AlwaysNewInstance() { - var option = new GraphicsOptions() + GraphicsOptions option = new() { BlendPercentage = 0.9f }; - var config = new Configuration(); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + Configuration config = new(); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); context.SetGraphicsOptions(option); context.SetGraphicsOptions(o => @@ -40,7 +40,7 @@ public void UpdateDefaultOptionsOnProcessingContext_AlwaysNewInstance() o.BlendPercentage = 0.4f; }); - var returnedOption = context.GetGraphicsOptions(); + GraphicsOptions returnedOption = context.GetGraphicsOptions(); Assert.Equal(0.4f, returnedOption.BlendPercentage); Assert.Equal(0.9f, option.BlendPercentage); // hasn't been mutated } @@ -48,8 +48,8 @@ public void UpdateDefaultOptionsOnProcessingContext_AlwaysNewInstance() [Fact] public void SetDefaultOptionsOnConfiguration() { - var option = new GraphicsOptions(); - var config = new Configuration(); + GraphicsOptions option = new(); + Configuration config = new(); config.SetGraphicsOptions(option); @@ -59,11 +59,11 @@ public void SetDefaultOptionsOnConfiguration() [Fact] public void UpdateDefaultOptionsOnConfiguration_AlwaysNewInstance() { - var option = new GraphicsOptions() + GraphicsOptions option = new() { BlendPercentage = 0.9f }; - var config = new Configuration(); + Configuration config = new(); config.SetGraphicsOptions(option); config.SetGraphicsOptions(o => @@ -72,7 +72,7 @@ public void UpdateDefaultOptionsOnConfiguration_AlwaysNewInstance() o.BlendPercentage = 0.4f; }); - var returnedOption = config.GetGraphicsOptions(); + GraphicsOptions returnedOption = config.GetGraphicsOptions(); Assert.Equal(0.4f, returnedOption.BlendPercentage); Assert.Equal(0.9f, option.BlendPercentage); // hasn't been mutated } @@ -80,13 +80,13 @@ public void UpdateDefaultOptionsOnConfiguration_AlwaysNewInstance() [Fact] public void GetDefaultOptionsFromConfiguration_SettingNullThenReturnsNewInstance() { - var config = new Configuration(); + Configuration config = new(); - var options = config.GetGraphicsOptions(); + GraphicsOptions options = config.GetGraphicsOptions(); Assert.NotNull(options); config.SetGraphicsOptions((GraphicsOptions)null); - var options2 = config.GetGraphicsOptions(); + GraphicsOptions options2 = config.GetGraphicsOptions(); Assert.NotNull(options2); // we set it to null should now be a new instance @@ -96,10 +96,10 @@ public void GetDefaultOptionsFromConfiguration_SettingNullThenReturnsNewInstance [Fact] public void GetDefaultOptionsFromConfiguration_IgnoreIncorectlyTypesDictionEntry() { - var config = new Configuration(); + Configuration config = new(); config.Properties[typeof(GraphicsOptions)] = "wronge type"; - var options = config.GetGraphicsOptions(); + GraphicsOptions options = config.GetGraphicsOptions(); Assert.NotNull(options); Assert.IsType(options); } @@ -107,63 +107,63 @@ public void GetDefaultOptionsFromConfiguration_IgnoreIncorectlyTypesDictionEntry [Fact] public void GetDefaultOptionsFromConfiguration_AlwaysReturnsInstance() { - var config = new Configuration(); + Configuration config = new(); Assert.DoesNotContain(typeof(GraphicsOptions), config.Properties.Keys); - var options = config.GetGraphicsOptions(); + GraphicsOptions options = config.GetGraphicsOptions(); Assert.NotNull(options); } [Fact] public void GetDefaultOptionsFromConfiguration_AlwaysReturnsSameValue() { - var config = new Configuration(); + Configuration config = new(); - var options = config.GetGraphicsOptions(); - var options2 = config.GetGraphicsOptions(); + GraphicsOptions options = config.GetGraphicsOptions(); + GraphicsOptions options2 = config.GetGraphicsOptions(); Assert.Equal(options, options2); } [Fact] public void GetDefaultOptionsFromProcessingContext_AlwaysReturnsInstance() { - var config = new Configuration(); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + Configuration config = new(); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); - var ctxOptions = context.GetGraphicsOptions(); + GraphicsOptions ctxOptions = context.GetGraphicsOptions(); Assert.NotNull(ctxOptions); } [Fact] public void GetDefaultOptionsFromProcessingContext_AlwaysReturnsInstanceEvenIfSetToNull() { - var config = new Configuration(); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + Configuration config = new(); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); context.SetGraphicsOptions((GraphicsOptions)null); - var ctxOptions = context.GetGraphicsOptions(); + GraphicsOptions ctxOptions = context.GetGraphicsOptions(); Assert.NotNull(ctxOptions); } [Fact] public void GetDefaultOptionsFromProcessingContext_FallbackToConfigsInstance() { - var option = new GraphicsOptions(); - var config = new Configuration(); + GraphicsOptions option = new(); + Configuration config = new(); config.SetGraphicsOptions(option); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); - var ctxOptions = context.GetGraphicsOptions(); + GraphicsOptions ctxOptions = context.GetGraphicsOptions(); Assert.Equal(option, ctxOptions); } [Fact] public void GetDefaultOptionsFromProcessingContext_IgnoreIncorectlyTypesDictionEntry() { - var config = new Configuration(); - var context = new FakeImageOperationsProvider.FakeImageOperations(config, null, true); + Configuration config = new(); + FakeImageOperationsProvider.FakeImageOperations context = new(config, null, true); context.Properties[typeof(GraphicsOptions)] = "wronge type"; - var options = context.GetGraphicsOptions(); + GraphicsOptions options = context.GetGraphicsOptions(); Assert.NotNull(options); Assert.IsType(options); } diff --git a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs index 3531599866..e9d45a1fde 100644 --- a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs @@ -8,12 +8,12 @@ namespace SixLabors.ImageSharp.Tests; public class GraphicsOptionsTests { - private static readonly GraphicsOptionsComparer GraphicsOptionsComparer = new GraphicsOptionsComparer(); - private readonly GraphicsOptions newGraphicsOptions = new GraphicsOptions(); + private static readonly GraphicsOptionsComparer GraphicsOptionsComparer = new(); + private readonly GraphicsOptions newGraphicsOptions = new(); private readonly GraphicsOptions cloneGraphicsOptions = new GraphicsOptions().DeepClone(); [Fact] - public void CloneGraphicsOptionsIsNotNull() => Assert.True(this.cloneGraphicsOptions != null); + public void CloneGraphicsOptionsIsNotNull() => Assert.NotNull(this.cloneGraphicsOptions); [Fact] public void DefaultGraphicsOptionsAntialias() @@ -22,46 +22,37 @@ public void DefaultGraphicsOptionsAntialias() Assert.True(this.cloneGraphicsOptions.Antialias); } - [Fact] - public void DefaultGraphicsOptionsAntialiasSuppixelDepth() - { - const int Expected = 16; - Assert.Equal(Expected, this.newGraphicsOptions.AntialiasSubpixelDepth); - Assert.Equal(Expected, this.cloneGraphicsOptions.AntialiasSubpixelDepth); - } - [Fact] public void DefaultGraphicsOptionsBlendPercentage() { - const float Expected = 1F; - Assert.Equal(Expected, this.newGraphicsOptions.BlendPercentage); - Assert.Equal(Expected, this.cloneGraphicsOptions.BlendPercentage); + const float expected = 1F; + Assert.Equal(expected, this.newGraphicsOptions.BlendPercentage); + Assert.Equal(expected, this.cloneGraphicsOptions.BlendPercentage); } [Fact] public void DefaultGraphicsOptionsColorBlendingMode() { - const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal; - Assert.Equal(Expected, this.newGraphicsOptions.ColorBlendingMode); - Assert.Equal(Expected, this.cloneGraphicsOptions.ColorBlendingMode); + const PixelColorBlendingMode expected = PixelColorBlendingMode.Normal; + Assert.Equal(expected, this.newGraphicsOptions.ColorBlendingMode); + Assert.Equal(expected, this.cloneGraphicsOptions.ColorBlendingMode); } [Fact] public void DefaultGraphicsOptionsAlphaCompositionMode() { - const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver; - Assert.Equal(Expected, this.newGraphicsOptions.AlphaCompositionMode); - Assert.Equal(Expected, this.cloneGraphicsOptions.AlphaCompositionMode); + const PixelAlphaCompositionMode expected = PixelAlphaCompositionMode.SrcOver; + Assert.Equal(expected, this.newGraphicsOptions.AlphaCompositionMode); + Assert.Equal(expected, this.cloneGraphicsOptions.AlphaCompositionMode); } [Fact] public void NonDefaultClone() { - var expected = new GraphicsOptions + GraphicsOptions expected = new() { AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop, Antialias = false, - AntialiasSubpixelDepth = 23, BlendPercentage = .25F, ColorBlendingMode = PixelColorBlendingMode.HardLight, }; @@ -74,12 +65,11 @@ public void NonDefaultClone() [Fact] public void CloneIsDeep() { - var expected = new GraphicsOptions(); + GraphicsOptions expected = new(); GraphicsOptions actual = expected.DeepClone(); actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop; actual.Antialias = false; - actual.AntialiasSubpixelDepth = 23; actual.BlendPercentage = .25F; actual.ColorBlendingMode = PixelColorBlendingMode.HardLight; diff --git a/tests/ImageSharp.Tests/Helpers/ColorNumericsTests.cs b/tests/ImageSharp.Tests/Helpers/ColorNumericsTests.cs index 4c06d0cd55..903e997aad 100644 --- a/tests/ImageSharp.Tests/Helpers/ColorNumericsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ColorNumericsTests.cs @@ -15,14 +15,68 @@ public class ColorNumericsTests public void GetBT709Luminance_WithVector4(float x, float y, float z, int luminanceLevels, int expected) { // arrange - var vector = new Vector4(x, y, z, 0.0f); + Vector4 vector = new(x, y, z, 0.0f); // act - int actual = ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels); + int actual = ColorNumerics.GetBT709Luminance(vector, luminanceLevels); // assert Assert.Equal(expected, actual); } - // TODO: We need to test all ColorNumerics methods! + [Theory] + [InlineData((ushort)0, (byte)0)] + [InlineData((ushort)128, (byte)0)] + [InlineData((ushort)129, (byte)1)] + [InlineData((ushort)257, (byte)1)] + [InlineData((ushort)32896, (byte)128)] + [InlineData(ushort.MaxValue, byte.MaxValue)] + public void From16BitTo8Bit_ReturnsExpectedValue(ushort component, byte expected) + { + byte actual = ColorNumerics.From16BitTo8Bit(component); + + Assert.Equal(expected, actual); + } + + [Fact] + public void From16BitTo8Bit_RoundTripsAllExpanded8BitValues() + { + for (int i = 0; i <= byte.MaxValue; i++) + { + byte expected = (byte)i; + ushort component = ColorNumerics.From8BitTo16Bit(expected); + + byte actual = ColorNumerics.From16BitTo8Bit(component); + + Assert.Equal(expected, actual); + } + } + + [Theory] + [InlineData(0U, (byte)0)] + [InlineData(8421504U, (byte)0)] + [InlineData(8421505U, (byte)1)] + [InlineData(16843009U, (byte)1)] + [InlineData(2155905152U, (byte)128)] + [InlineData(uint.MaxValue, byte.MaxValue)] + public void From32BitTo8Bit_ReturnsExpectedValue(uint component, byte expected) + { + byte actual = ColorNumerics.From32BitTo8Bit(component); + + Assert.Equal(expected, actual); + } + + [Fact] + public void From32BitTo8Bit_RoundTripsAllExpanded8BitValues() + { + for (int i = 0; i <= byte.MaxValue; i++) + { + byte expected = (byte)i; + uint component = ColorNumerics.From8BitTo32Bit(expected); + + byte actual = ColorNumerics.From32BitTo8Bit(component); + + Assert.Equal(expected, actual); + } + } } diff --git a/tests/ImageSharp.Tests/Helpers/NumericsTests.cs b/tests/ImageSharp.Tests/Helpers/NumericsTests.cs index 75f988a4cb..77ecc447ee 100644 --- a/tests/ImageSharp.Tests/Helpers/NumericsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/NumericsTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Tests.Helpers; @@ -9,7 +10,12 @@ public class NumericsTests { private delegate void SpanAction(Span span, TArg arg, TArg1 arg1); - private readonly ApproximateFloatComparer approximateFloatComparer = new ApproximateFloatComparer(1e-6f); + private readonly ApproximateFloatComparer approximateFloatComparer = new(1e-6f); + + /// + /// Gets lengths that straddle the Vector4 block boundaries used by the 128-, 256-, and 512-bit kernels. + /// + public static TheoryData AssociationSpanLengths => new() { 0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63 }; [Theory] [InlineData(0)] @@ -156,14 +162,23 @@ public void LeastCommonMultiple(int a, int b, int expected) } [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - [InlineData(63)] + [MemberData(nameof(AssociationSpanLengths))] public void PremultiplyVectorSpan(int length) { - var rnd = new Random(42); + Random rnd = new(42); Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + + for (int i = 0; i < source.Length; i++) + { + // Exact zero and one exercise the special alpha boundaries alongside the random fractional values. + source[i].W = i % 5 switch + { + 0 => 0F, + 1 => 1F, + _ => source[i].W + }; + } + Vector4[] expected = source.Select(v => { Numerics.Premultiply(ref v); @@ -173,17 +188,32 @@ public void PremultiplyVectorSpan(int length) Numerics.Premultiply(source); Assert.Equal(expected, source, this.approximateFloatComparer); + + for (int i = 0; i < source.Length; i++) + { + // Alpha is storage metadata here and must survive each SIMD width without any floating-point transformation. + Assert.Equal(BitConverter.SingleToInt32Bits(expected[i].W), BitConverter.SingleToInt32Bits(source[i].W)); + } } [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - [InlineData(63)] + [MemberData(nameof(AssociationSpanLengths))] public void UnPremultiplyVectorSpan(int length) { - var rnd = new Random(42); + Random rnd = new(42); Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + + for (int i = 0; i < source.Length; i++) + { + // A zero alpha preserves the complete source vector by contract; one also verifies the identity case. + source[i].W = i % 5 switch + { + 0 => 0F, + 1 => 1F, + _ => source[i].W + }; + } + Vector4[] expected = source.Select(v => { Numerics.UnPremultiply(ref v); @@ -193,6 +223,12 @@ public void UnPremultiplyVectorSpan(int length) Numerics.UnPremultiply(source); Assert.Equal(expected, source, this.approximateFloatComparer); + + for (int i = 0; i < source.Length; i++) + { + // Alpha is the divisor and must still survive each SIMD width without any floating-point transformation. + Assert.Equal(BitConverter.SingleToInt32Bits(expected[i].W), BitConverter.SingleToInt32Bits(source[i].W)); + } } [Theory] @@ -270,6 +306,114 @@ public void ClampDouble(int length, double min, double max) (v, m1, m2) => Numerics.Clamp(v, m1, m2)); } + /// + /// Scalar, SIMD, and span clamps map nonfinite values to the requested bounds. + /// + /// The lower bound. + /// The upper bound. + [Theory] + [InlineData(0F, 1F)] + [InlineData(-2F, 3F)] + [InlineData(.25F, .75F)] + public void ClampSingle_NormalizesNonfiniteValues(float min, float max) + { + float midpoint = (min + max) / 2; + float[] inputs = [float.NaN, float.PositiveInfinity, float.NegativeInfinity, midpoint]; + float[] normalized = [min, max, min, midpoint]; + float[] values = new float[65]; + float[] expected = new float[values.Length]; + + // The length includes complete registers and remainders for every supported SIMD width. + for (int i = 0; i < values.Length; i++) + { + values[i] = inputs[i % inputs.Length]; + expected[i] = normalized[i % normalized.Length]; + Assert.Equal(expected[i], Numerics.Clamp(values[i], min, max)); + } + + Vector4 input = new(inputs[0], inputs[1], inputs[2], inputs[3]); + Vector4 result = new(normalized[0], normalized[1], normalized[2], normalized[3]); + Assert.Equal(result, Numerics.Clamp(input, new Vector4(min), new Vector4(max))); + Assert.Equal(new Vector2(min, max), Numerics.Clamp(new Vector2(float.NaN, float.PositiveInfinity), new Vector2(min), new Vector2(max))); + + Vector128 vector128 = Vector128.LoadUnsafe(ref values[0]); + Assert.Equal(Vector128.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector128, Vector128.Create(min), Vector128.Create(max))); + + Vector256 vector256 = Vector256.LoadUnsafe(ref values[0]); + Assert.Equal(Vector256.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector256, Vector256.Create(min), Vector256.Create(max))); + + Vector512 vector512 = Vector512.LoadUnsafe(ref values[0]); + Assert.Equal(Vector512.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector512, Vector512.Create(min), Vector512.Create(max))); + + Numerics.Clamp(values, min, max); + Assert.Equal(expected, values); + } + + /// + /// Scalar, SIMD, and span clamps map nonfinite values to the requested bounds. + /// + /// The lower bound. + /// The upper bound. + [Theory] + [InlineData(0D, 1D)] + [InlineData(-2D, 3D)] + [InlineData(.25D, .75D)] + public void ClampDouble_NormalizesNonfiniteValues(double min, double max) + { + double midpoint = (min + max) / 2; + double[] inputs = [double.NaN, double.PositiveInfinity, double.NegativeInfinity, midpoint]; + double[] normalized = [min, max, min, midpoint]; + double[] values = new double[65]; + double[] expected = new double[values.Length]; + + // The length includes complete registers and remainders for every supported SIMD width. + for (int i = 0; i < values.Length; i++) + { + values[i] = inputs[i % inputs.Length]; + expected[i] = normalized[i % normalized.Length]; + Assert.Equal(expected[i], Numerics.Clamp(values[i], min, max)); + } + + Vector128 vector128 = Vector128.LoadUnsafe(ref values[0]); + Assert.Equal(Vector128.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector128, Vector128.Create(min), Vector128.Create(max))); + + Vector256 vector256 = Vector256.LoadUnsafe(ref values[0]); + Assert.Equal(Vector256.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector256, Vector256.Create(min), Vector256.Create(max))); + + Vector512 vector512 = Vector512.LoadUnsafe(ref values[0]); + Assert.Equal(Vector512.LoadUnsafe(ref expected[0]), Numerics.Clamp(vector512, Vector512.Create(min), Vector512.Create(max))); + + Numerics.Clamp(values, min, max); + Assert.Equal(expected, values); + } + + /// + /// Clamping an in-range zero preserves its sign in scalar and bulk conversions. + /// + [Fact] + public void Clamp_PreservesInRangeSignedZero() + { + float[] singles = new float[65]; + double[] doubles = new double[65]; + + for (int i = 0; i < singles.Length; i++) + { + singles[i] = i % 2 == 0 ? -0F : 0F; + doubles[i] = i % 2 == 0 ? -0D : 0D; + Assert.Equal(BitConverter.SingleToInt32Bits(singles[i]), BitConverter.SingleToInt32Bits(Numerics.Clamp(singles[i], 0F, 1F))); + Assert.Equal(BitConverter.DoubleToInt64Bits(doubles[i]), BitConverter.DoubleToInt64Bits(Numerics.Clamp(doubles[i], 0D, 1D))); + } + + Numerics.Clamp(singles, 0F, 1F); + Numerics.Clamp(doubles, 0D, 1D); + + for (int i = 0; i < singles.Length; i++) + { + Assert.Equal(BitConverter.SingleToInt32Bits(i % 2 == 0 ? -0F : 0F), BitConverter.SingleToInt32Bits(singles[i])); + Assert.Equal(BitConverter.DoubleToInt64Bits(i % 2 == 0 ? -0D : 0D), BitConverter.DoubleToInt64Bits(doubles[i])); + } + } + private static void TestClampSpan( int length, T min, @@ -280,7 +424,7 @@ private static void TestClampSpan( { Span actual = new T[length]; - var r = new Random(); + Random r = new(); for (int i = 0; i < length; i++) { actual[i] = (T)Convert.ChangeType(r.Next(byte.MinValue, byte.MaxValue), typeof(T)); diff --git a/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs index c13a30052c..983c3cc2b9 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs @@ -28,7 +28,7 @@ public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(in } else { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, Configuration.Default.MemoryAllocator); Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism); diff --git a/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs index d393850d6b..017926fc5d 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelRowIteratorTests.cs @@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers; public class ParallelRowIteratorTests { + public delegate void BufferedRowAction(int y, Span span); public delegate void RowIntervalAction(RowInterval rows, Span span); private readonly ITestOutputHelper output; @@ -26,7 +27,7 @@ public ParallelRowIteratorTests(ITestOutputHelper output) /// maxDegreeOfParallelism, minY, maxY, expectedStepLength, expectedLastStepLength /// public static TheoryData IterateRows_OverMinimumPixelsLimit_Data = - new TheoryData + new() { { 1, 0, 100, -1, 100, 1 }, { 2, 0, 9, 5, 4, 2 }, @@ -52,12 +53,12 @@ public void IterateRows_OverMinimumPixelsLimit_IntervalsAreCorrect( int expectedLastStepLength, int expectedNumberOfSteps) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, 1, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, minY, 10, maxY - minY); + Rectangle rectangle = new(0, minY, 10, maxY - minY); int actualNumberOfSteps = 0; @@ -73,7 +74,7 @@ void RowAction(RowInterval rows) Assert.Equal(expected, step); } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals( rectangle, @@ -93,15 +94,15 @@ public void IterateRows_OverMinimumPixelsLimit_ShouldVisitAllRows( int expectedLastStepLength, int expectedNumberOfSteps) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, 1, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, minY, 10, maxY - minY); + Rectangle rectangle = new(0, minY, 10, maxY - minY); int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray(); - var actualData = new int[maxY]; + int[] actualData = new int[maxY]; void RowAction(RowInterval rows) { @@ -111,7 +112,7 @@ void RowAction(RowInterval rows) } } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals( rectangle, @@ -131,12 +132,12 @@ public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit( int expectedLastStepLength, int expectedNumberOfSteps) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, 1, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, minY, 10, maxY - minY); + Rectangle rectangle = new(0, minY, 10, maxY - minY); int actualNumberOfSteps = 0; @@ -152,7 +153,7 @@ void RowAction(RowInterval rows, Span buffer) Assert.Equal(expected, step); } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, @@ -172,15 +173,15 @@ public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit_ShouldVisitAllRows( int expectedLastStepLength, int expectedNumberOfSteps) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, 1, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, minY, 10, maxY - minY); + Rectangle rectangle = new(0, minY, 10, maxY - minY); int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray(); - var actualData = new int[maxY]; + int[] actualData = new int[maxY]; void RowAction(RowInterval rows, Span buffer) { @@ -190,7 +191,7 @@ void RowAction(RowInterval rows, Span buffer) } } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, @@ -200,8 +201,31 @@ void RowAction(RowInterval rows, Span buffer) Assert.Equal(expectedData, actualData); } + [Fact] + public void IterateRows_MaxDegreeOfParallelismMinusOne_ShouldVisitAllRows() + { + ParallelExecutionSettings parallelSettings = new( + -1, + 10, + Configuration.Default.MemoryAllocator); + + Rectangle rectangle = new(0, 0, 10, 10); + int[] actualData = new int[rectangle.Height]; + + void RowAction(int y) => actualData[y]++; + + TestRowActionOperation operation = new(RowAction); + + ParallelRowIterator.IterateRows( + rectangle, + in parallelSettings, + in operation); + + Assert.Equal(Enumerable.Repeat(1, rectangle.Height), actualData); + } + public static TheoryData IterateRows_WithEffectiveMinimumPixelsLimit_Data = - new TheoryData + new() { { 2, 200, 50, 2, 1, -1, 2 }, { 2, 200, 200, 1, 1, -1, 1 }, @@ -223,12 +247,12 @@ public void IterateRows_WithEffectiveMinimumPixelsLimit( int expectedStepLength, int expectedLastStepLength) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, minimumPixelsProcessedPerTask, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, 0, width, height); + Rectangle rectangle = new(0, 0, width, height); int actualNumberOfSteps = 0; @@ -244,7 +268,7 @@ void RowAction(RowInterval rows) Assert.Equal(expected, step); } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals( rectangle, @@ -265,12 +289,12 @@ public void IterateRowsWithTempBuffer_WithEffectiveMinimumPixelsLimit( int expectedStepLength, int expectedLastStepLength) { - var parallelSettings = new ParallelExecutionSettings( + ParallelExecutionSettings parallelSettings = new( maxDegreeOfParallelism, minimumPixelsProcessedPerTask, Configuration.Default.MemoryAllocator); - var rectangle = new Rectangle(0, 0, width, height); + Rectangle rectangle = new(0, 0, width, height); int actualNumberOfSteps = 0; @@ -286,7 +310,7 @@ void RowAction(RowInterval rows, Span buffer) Assert.Equal(expected, step); } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals, Vector4>( rectangle, @@ -296,8 +320,37 @@ void RowAction(RowInterval rows, Span buffer) Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps); } + [Fact] + public void IterateRowIntervalsWithTempBuffer_MaxDegreeOfParallelismMinusOne_ShouldVisitAllRows() + { + ParallelExecutionSettings parallelSettings = new( + -1, + 10, + Configuration.Default.MemoryAllocator); + + Rectangle rectangle = new(0, 0, 10, 10); + int[] actualData = new int[rectangle.Height]; + + void RowAction(RowInterval rows, Span buffer) + { + for (int y = rows.Min; y < rows.Max; y++) + { + actualData[y]++; + } + } + + TestRowIntervalOperation operation = new(RowAction); + + ParallelRowIterator.IterateRowIntervals, Vector4>( + rectangle, + in parallelSettings, + in operation); + + Assert.Equal(Enumerable.Repeat(1, rectangle.Height), actualData); + } + public static readonly TheoryData IterateRectangularBuffer_Data = - new TheoryData + new() { { 8, 582, 453, 10, 10, 291, 226 }, // boundary data from DetectEdgesTest.DetectEdges_InBox { 2, 582, 453, 10, 10, 291, 226 }, @@ -322,7 +375,7 @@ public void IterateRectangularBuffer( using (Buffer2D expected = memoryAllocator.Allocate2D(bufferWidth, bufferHeight, AllocationOptions.Clean)) using (Buffer2D actual = memoryAllocator.Allocate2D(bufferWidth, bufferHeight, AllocationOptions.Clean)) { - var rect = new Rectangle(rectX, rectY, rectWidth, rectHeight); + Rectangle rect = new(rectX, rectY, rectWidth, rectHeight); void FillRow(int y, Buffer2D buffer) { @@ -339,7 +392,7 @@ void FillRow(int y, Buffer2D buffer) } // Fill actual data using IterateRows: - var settings = new ParallelExecutionSettings(maxDegreeOfParallelism, memoryAllocator); + ParallelExecutionSettings settings = new(maxDegreeOfParallelism, memoryAllocator); void RowAction(RowInterval rows) { @@ -350,7 +403,7 @@ void RowAction(RowInterval rows) } } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ParallelRowIterator.IterateRowIntervals( rect, @@ -369,15 +422,15 @@ void RowAction(RowInterval rows) [InlineData(10, -10)] public void IterateRowsRequiresValidRectangle(int width, int height) { - var parallelSettings = default(ParallelExecutionSettings); + ParallelExecutionSettings parallelSettings = default(ParallelExecutionSettings); - var rect = new Rectangle(0, 0, width, height); + Rectangle rect = new(0, 0, width, height); void RowAction(RowInterval rows) { } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ArgumentOutOfRangeException ex = Assert.Throws( () => ParallelRowIterator.IterateRowIntervals(rect, in parallelSettings, in operation)); @@ -392,15 +445,15 @@ void RowAction(RowInterval rows) [InlineData(10, -10)] public void IterateRowsWithTempBufferRequiresValidRectangle(int width, int height) { - var parallelSettings = default(ParallelExecutionSettings); + ParallelExecutionSettings parallelSettings = default(ParallelExecutionSettings); - var rect = new Rectangle(0, 0, width, height); + Rectangle rect = new(0, 0, width, height); void RowAction(RowInterval rows, Span memory) { } - var operation = new TestRowIntervalOperation(RowAction); + TestRowIntervalOperation operation = new(RowAction); ArgumentOutOfRangeException ex = Assert.Throws( () => ParallelRowIterator.IterateRowIntervals, Rgba32>(rect, in parallelSettings, in operation)); @@ -434,7 +487,7 @@ public TestRowOperation() { } - public StrongBox MaxY { get; } = new StrongBox(); + public StrongBox MaxY { get; } = new(); public void Invoke(int y) { @@ -445,6 +498,32 @@ public void Invoke(int y) } } + private readonly struct TestRowActionOperation : IRowOperation + { + private readonly Action action; + + public TestRowActionOperation(Action action) + => this.action = action; + + public void Invoke(int y) + => this.action(y); + } + + private readonly struct TestRowOperation : IRowOperation + where TBuffer : unmanaged + { + private readonly BufferedRowAction action; + + public TestRowOperation(BufferedRowAction action) + => this.action = action; + + public int GetRequiredBufferLength(Rectangle bounds) + => bounds.Width; + + public void Invoke(int y, Span span) + => this.action(y, span); + } + private readonly struct TestRowIntervalOperation : IRowIntervalOperation { private readonly Action action; diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 95f1d4e289..215cd58bcf 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -10,7 +10,7 @@ public class RowIntervalTests [Fact] public void Slice1() { - var rowInterval = new RowInterval(10, 20); + RowInterval rowInterval = new(10, 20); RowInterval sliced = rowInterval.Slice(5); Assert.Equal(15, sliced.Min); @@ -20,7 +20,7 @@ public void Slice1() [Fact] public void Slice2() { - var rowInterval = new RowInterval(10, 20); + RowInterval rowInterval = new(10, 20); RowInterval sliced = rowInterval.Slice(3, 5); Assert.Equal(13, sliced.Min); @@ -30,8 +30,8 @@ public void Slice2() [Fact] public void Equality_WhenTrue() { - var a = new RowInterval(42, 123); - var b = new RowInterval(42, 123); + RowInterval a = new(42, 123); + RowInterval b = new(42, 123); Assert.True(a.Equals(b)); Assert.True(a.Equals((object)b)); @@ -42,9 +42,9 @@ public void Equality_WhenTrue() [Fact] public void Equality_WhenFalse() { - var a = new RowInterval(42, 123); - var b = new RowInterval(42, 125); - var c = new RowInterval(40, 123); + RowInterval a = new(42, 123); + RowInterval b = new(42, 125); + RowInterval c = new(40, 123); Assert.False(a.Equals(b)); Assert.False(c.Equals(a)); diff --git a/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs b/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs index 64f79840ca..0cacbdc87a 100644 --- a/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs +++ b/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers; public class TolerantMathTests { - private readonly TolerantMath tolerantMath = new TolerantMath(0.1); + private readonly TolerantMath tolerantMath = new(0.1); [Theory] [InlineData(0)] diff --git a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs index 390170cfef..63c1f7fa31 100644 --- a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs +++ b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs @@ -47,14 +47,14 @@ public void MemoryStream_ReadTest_Negative() ChunkedMemoryStream ms2 = new(this.allocator); Assert.Throws(() => ms2.Read(null, 0, 0)); - Assert.Throws(() => ms2.Read(new byte[] { 1 }, -1, 0)); - Assert.Throws(() => ms2.Read(new byte[] { 1 }, 0, -1)); - Assert.Throws(() => ms2.Read(new byte[] { 1 }, 2, 0)); - Assert.Throws(() => ms2.Read(new byte[] { 1 }, 0, 2)); + Assert.Throws(() => ms2.Read([1], -1, 0)); + Assert.Throws(() => ms2.Read([1], 0, -1)); + Assert.Throws(() => ms2.Read([1], 2, 0)); + Assert.Throws(() => ms2.Read([1], 0, 2)); ms2.Dispose(); - Assert.Throws(() => ms2.Read(new byte[] { 1 }, 0, 1)); + Assert.Throws(() => ms2.Read([1], 0, 1)); } [Theory] @@ -197,7 +197,7 @@ public void MemoryStream_WriteToSpanTests(int length) readonlyStream.Position = 0; bytArrRet = new byte[(int)readonlyStream.Length]; - readonlyStream.Read(bytArrRet, 0, (int)readonlyStream.Length); + readonlyStream.Read(bytArrRet); for (int i = 0; i < bytArr.Length; i++) { Assert.Equal(bytArr[i], bytArrRet[i]); @@ -216,7 +216,7 @@ public void MemoryStream_WriteToSpanTests(int length) ms2.WriteTo(ms3); ms3.Position = 0; bytArrRet = new byte[(int)ms3.Length]; - ms3.Read(bytArrRet, 0, (int)ms3.Length); + ms3.Read(bytArrRet); for (int i = 0; i < bytArr.Length; i++) { Assert.Equal(bytArr[i], bytArrRet[i]); @@ -229,7 +229,7 @@ public void MemoryStream_WriteByteTests() { using ChunkedMemoryStream ms2 = new(this.allocator); byte[] bytArrRet; - byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 }; + byte[] bytArr = [byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250]; for (int i = 0; i < bytArr.Length; i++) { @@ -254,7 +254,7 @@ public void MemoryStream_WriteToTests_Negative() using ChunkedMemoryStream ms2 = new(this.allocator); Assert.Throws(() => ms2.WriteTo(null)); - ms2.Write(new byte[] { 1 }, 0, 1); + ms2.Write([1], 0, 1); MemoryStream readonlyStream = new(new byte[1028], false); Assert.Throws(() => ms2.WriteTo(readonlyStream)); @@ -313,7 +313,7 @@ public static IEnumerable GetAllTestImages() IEnumerable allImageFiles = Directory.EnumerateFiles(TestEnvironment.InputImagesDirectoryFullPath, "*.*", SearchOption.AllDirectories) .Where(s => !s.EndsWith("txt", StringComparison.OrdinalIgnoreCase)); - List result = new(); + List result = []; foreach (string path in allImageFiles) { result.Add(path[TestEnvironment.InputImagesDirectoryFullPath.Length..]); @@ -396,22 +396,22 @@ public void EncoderIntegrationTest(TestImageProvider provider) public static IEnumerable CopyToData() { // Stream is positioned @ beginning of data - byte[] data1 = new byte[] { 1, 2, 3 }; + byte[] data1 = [1, 2, 3]; MemoryStream stream1 = new(data1); - yield return new object[] { stream1, data1 }; + yield return [stream1, data1]; // Stream is positioned in the middle of data - byte[] data2 = new byte[] { 0xff, 0xf3, 0xf0 }; + byte[] data2 = [0xff, 0xf3, 0xf0]; MemoryStream stream2 = new(data2) { Position = 1 }; - yield return new object[] { stream2, new byte[] { 0xf3, 0xf0 } }; + yield return [stream2, new byte[] { 0xf3, 0xf0 }]; // Stream is positioned after end of data byte[] data3 = data2; MemoryStream stream3 = new(data3) { Position = data3.Length + 1 }; - yield return new object[] { stream3, Array.Empty() }; + yield return [stream3, Array.Empty()]; } private byte[] CreateTestBuffer(int length) diff --git a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs index 25674e6a8d..0680c9a823 100644 --- a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs @@ -10,7 +10,7 @@ public class ImageCloneTests [Fact] public void CloneAs_WhenDisposed_Throws() { - var image = new Image(5, 5); + Image image = new(5, 5); image.Dispose(); Assert.Throws(() => image.CloneAs()); @@ -19,7 +19,7 @@ public void CloneAs_WhenDisposed_Throws() [Fact] public void Clone_WhenDisposed_Throws() { - var image = new Image(5, 5); + Image image = new(5, 5); image.Dispose(); Assert.Throws(() => image.Clone()); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index 62d1ef4db9..417ee18fbe 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -64,7 +64,7 @@ public void AddNewFrame_PixelBuffer_BufferIncorrectSize() using ImageFrame addedFrame = this.Collection.AddFrame(Array.Empty()); }); - Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message); + Assert.StartsWith($"Parameter \"data\" ({typeof(long)}) must be greater than or equal to {100}, was {0}", ex.Message); } [Fact] @@ -102,7 +102,7 @@ public void Constructor_FramesMustHaveSameSize() using ImageFrame imageFrame2 = new(Configuration.Default, 1, 1); new ImageFrameCollection( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -114,7 +114,7 @@ public void RemoveAtFrame_ThrowIfRemovingLastFrame() using ImageFrame imageFrame = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame }); + [imageFrame]); InvalidOperationException ex = Assert.Throws( () => collection.RemoveFrame(0)); @@ -128,7 +128,7 @@ public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); collection.RemoveFrame(0); Assert.Equal(1, collection.Count); @@ -141,7 +141,7 @@ public void RootFrameIsFrameAtIndexZero() using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); Assert.Equal(collection.RootFrame, collection[0]); } @@ -153,7 +153,7 @@ public void ConstructorPopulatesFrames() using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); Assert.Equal(2, collection.Count); } @@ -165,7 +165,7 @@ public void DisposeClearsCollection() using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); collection.Dispose(); @@ -179,7 +179,7 @@ public void Dispose_DisposesAllInnerFrames() using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, - new[] { imageFrame1, imageFrame2 }); + [imageFrame1, imageFrame2]); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); @@ -237,7 +237,7 @@ public void CreateFrame_CustomFillColor() 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 f9ff6ba69e..cd1213c230 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; @@ -23,13 +23,13 @@ public void AddFrame_OfDifferentPixelType() 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 +44,13 @@ public void InsertFrame_OfDifferentPixelType() 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 +177,7 @@ public void CreateFrame_CustomFillColor() ImageFrame frame = (ImageFrame)this.Image.Frames[1]; - frame.ComparePixelBufferTo(Color.HotPink); + frame.ComparePixelBufferTo(Color.HotPink.ToPixel()); } [Fact] @@ -247,7 +247,7 @@ public void PublicMethods_ThrowIfDisposed() { Image image = new(Configuration.Default, 10, 10); ImageFrameCollection frameCollection = image.Frames; - Rgba32[] rgba32Array = Array.Empty(); + Rgba32[] rgba32Array = []; image.Dispose(); // this should invalidate underlying collection as well @@ -279,7 +279,7 @@ public void ConstructGif_FromDifferentPixelTypes(TestImageProvider 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); @@ -313,10 +313,10 @@ private static void CompareGifMetadata(ImageFrame a, ImageFrame b) 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 == 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/Image/ImageFrameCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs index 14c38d1f70..887a67dd30 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Globalization; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; @@ -14,7 +15,7 @@ public abstract partial class ImageFrameCollectionTests : IDisposable public ImageFrameCollectionTests() { // Needed to get English exception messages, which are checked in several tests. - System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); + System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); this.Image = new Image(10, 10); this.Collection = new ImageFrameCollection(this.Image, 10, 10, default(Rgba32)); diff --git a/tests/ImageSharp.Tests/Image/ImageFrameTests.cs b/tests/ImageSharp.Tests/Image/ImageFrameTests.cs index 3b9779ea42..f8146363c7 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameTests.cs @@ -16,7 +16,7 @@ public class Indexer private void LimitBufferCapacity(int bufferCapacityInBytes) { - var allocator = new TestMemoryAllocator(); + TestMemoryAllocator allocator = new(); allocator.BufferCapacityInBytes = bufferCapacityInBytes; this.configuration.MemoryAllocator = allocator; } @@ -31,16 +31,16 @@ public void GetSet(bool enforceDisco) this.LimitBufferCapacity(100); } - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); 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() + public static TheoryData OutOfRangeData = new() { { false, -1 }, { false, 10 }, @@ -57,7 +57,7 @@ public void Get_OutOfRangeX(bool enforceDisco, int x) this.LimitBufferCapacity(100); } - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); ImageFrame frame = image.Frames.RootFrame; ArgumentOutOfRangeException ex = Assert.Throws(() => _ = frame[x, 3]); Assert.Equal("x", ex.ParamName); @@ -72,7 +72,7 @@ public void Set_OutOfRangeX(bool enforceDisco, int x) this.LimitBufferCapacity(100); } - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); ImageFrame frame = image.Frames.RootFrame; ArgumentOutOfRangeException ex = Assert.Throws(() => frame[x, 3] = default); Assert.Equal("x", ex.ParamName); @@ -87,7 +87,7 @@ public void Set_OutOfRangeY(bool enforceDisco, int y) this.LimitBufferCapacity(100); } - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); ImageFrame frame = image.Frames.RootFrame; ArgumentOutOfRangeException ex = Assert.Throws(() => frame[3, y] = default); Assert.Equal("y", ex.ParamName); @@ -105,14 +105,14 @@ public void CopyPixelDataTo_Success(bool disco, bool byteSpan) this.LimitBufferCapacity(20); } - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); if (disco) { Assert.True(image.GetPixelMemoryGroup().Count > 1); } byte[] expected = TestUtils.FillImageWithRandomBytes(image); - byte[] actual = new byte[expected.Length]; + Span actual = new byte[expected.Length]; if (byteSpan) { image.Frames.RootFrame.CopyPixelDataTo(actual); @@ -131,7 +131,7 @@ public void CopyPixelDataTo_Success(bool disco, bool byteSpan) [InlineData(true)] public void CopyPixelDataTo_DestinationTooShort_Throws(bool byteSpan) { - using var image = new Image(this.configuration, 10, 10); + using Image image = new(this.configuration, 10, 10); Assert.ThrowsAny(() => { @@ -173,7 +173,7 @@ protected override void ProcessPixelRowsImpl( [Fact] public void NullReference_Throws() { - using var img = new Image(1, 1); + using Image img = new(1, 1); ImageFrame frame = img.Frames.RootFrame; Assert.Throws(() => frame.ProcessPixelRows(null)); diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index f9c01ab564..e2f3c6337b 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -32,7 +32,7 @@ public ImageSaveTests() this.encoderNotInFormat = new Mock(); this.fileSystem = new Mock(); - var config = new Configuration + Configuration config = new() { FileSystem = this.fileSystem.Object }; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs index d8d9f4fe2a..0656f9e0bc 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Decode_Cancellation.cs @@ -12,8 +12,8 @@ public class Decode_Cancellation : ImageLoadTestBase { public Decode_Cancellation() => this.TopLevelConfiguration.StreamProcessingBufferSize = 128; - public static readonly string[] TestFileForEachCodec = new[] - { + public static readonly string[] TestFileForEachCodec = + [ TestImages.Jpeg.Baseline.Snake, // TODO: Figure out Unix cancellation failures, and validate cancellation for each decoder. @@ -24,7 +24,7 @@ public class Decode_Cancellation : ImageLoadTestBase //TestImages.Tga.Bit32BottomRight, //TestImages.Webp.Lossless.WithExif, //TestImages.Pbm.GrayscaleBinaryWide - }; + ]; public static object[][] IdentifyData { get; } = TestFileForEachCodec.Select(f => new object[] { f }).ToArray(); @@ -32,16 +32,16 @@ public class Decode_Cancellation : ImageLoadTestBase [MemberData(nameof(IdentifyData))] public async Task IdentifyAsync_PreCancelled(string file) { - using FileStream fs = File.OpenRead(TestFile.GetInputFileFullPath(file)); + await using FileStream fs = File.OpenRead(TestFile.GetInputFileFullPath(file)); CancellationToken preCancelled = new(canceled: true); await Assert.ThrowsAnyAsync(async () => await Image.IdentifyAsync(fs, preCancelled)); } private static TheoryData CreateLoadData() { - double[] percentages = new[] { 0, 0.3, 0.7 }; + double[] percentages = [0, 0.3, 0.7]; - TheoryData data = new(); + TheoryData data = []; foreach (string file in TestFileForEachCodec) { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs index f9a68c8bf6..178f5375f1 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -50,6 +50,10 @@ public void FromBytes_CustomConfiguration() Assert.Equal(this.LocalImageFormat, format); } + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.Throws(() => Image.DetectFormat([])); + [Fact] public void FromFileSystemPath_GlobalConfiguration() { @@ -114,7 +118,7 @@ public void WhenNoMatchingFormatFound_Throws_UnknownImageFormatException() { DecoderOptions options = new() { - Configuration = new() + Configuration = new Configuration() }; Assert.Throws(() => Image.DetectFormat(options, this.DataStream)); @@ -145,7 +149,7 @@ public Task WhenNoMatchingFormatFoundAsync_Throws_UnknownImageFormatException() { DecoderOptions options = new() { - Configuration = new() + Configuration = new Configuration() }; return Assert.ThrowsAsync(async () => await Image.DetectFormatAsync(options, new AsyncStreamWrapper(this.DataStream, () => false))); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.EncodeCancellation.cs b/tests/ImageSharp.Tests/Image/ImageTests.EncodeCancellation.cs new file mode 100644 index 0000000000..f3b1a01c94 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.EncodeCancellation.cs @@ -0,0 +1,131 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests; + +public partial class ImageTests +{ + [ValidateDisposedMemoryAllocations] + public class Encode_Cancellation + { + [Fact] + public async Task Encode_PreCancellation_Bmp() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsBmpAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Cur() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsCurAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Gif() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsGifAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Animated_Gif() + { + using Image image = new(10, 10); + image.Frames.CreateFrame(); + + await Assert.ThrowsAsync( + async () => await image.SaveAsGifAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Ico() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsIcoAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Jpeg() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsJpegAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Pbm() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsPbmAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Png() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsPngAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Animated_Png() + { + using Image image = new(10, 10); + image.Frames.CreateFrame(); + + await Assert.ThrowsAsync( + async () => await image.SaveAsPngAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Qoi() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsQoiAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Tga() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsTgaAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Tiff() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsTiffAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Webp() + { + using Image image = new(10, 10); + await Assert.ThrowsAsync( + async () => await image.SaveAsWebpAsync(Stream.Null, new CancellationToken(canceled: true))); + } + + [Fact] + public async Task Encode_PreCancellation_Animated_Webp() + { + using Image image = new(10, 10); + image.Frames.CreateFrame(); + + await Assert.ThrowsAsync( + async () => await image.SaveAsWebpAsync(Stream.Null, new CancellationToken(canceled: true))); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs index 7f6651d909..433a1e1018 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -38,6 +38,10 @@ public void FromBytes_GlobalConfiguration() Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat); } + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.Throws(() => Image.Identify([])); + [Fact] public void FromBytes_CustomConfiguration() { @@ -131,7 +135,7 @@ public void FromStream_CustomConfiguration_NoFormat() [Fact] public void WhenNoMatchingFormatFound_Throws_UnknownImageFormatException() { - DecoderOptions options = new() { Configuration = new() }; + DecoderOptions options = new() { Configuration = new Configuration() }; Assert.Throws(() => Image.Identify(options, this.DataStream)); } @@ -141,9 +145,8 @@ public void FromStream_ZeroLength_Throws_UnknownImageFormatException() { // https://github.com/SixLabors/ImageSharp/issues/1903 using ZipArchive zipFile = new(new MemoryStream( - new byte[] - { - 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, + [ + 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6D, 0x79, 0x73, 0x74, 0x65, 0x72, 0x79, 0x50, 0x4B, 0x01, 0x02, 0x3F, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -153,7 +156,7 @@ public void FromStream_ZeroLength_Throws_UnknownImageFormatException() 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x55, 0xA1, 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x59, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00 - })); + ])); using Stream stream = zipFile.Entries[0].Open(); Assert.Throws(() => Image.Identify(stream)); @@ -210,9 +213,8 @@ public async Task FromStreamAsync_ZeroLength_Throws_UnknownImageFormatException( { // https://github.com/SixLabors/ImageSharp/issues/1903 using ZipArchive zipFile = new(new MemoryStream( - new byte[] - { - 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, + [ + 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6D, 0x79, 0x73, 0x74, 0x65, 0x72, 0x79, 0x50, 0x4B, 0x01, 0x02, 0x3F, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -222,7 +224,7 @@ public async Task FromStreamAsync_ZeroLength_Throws_UnknownImageFormatException( 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x55, 0xA1, 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x59, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00 - })); + ])); using Stream stream = zipFile.Entries[0].Open(); await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(stream)); @@ -279,7 +281,7 @@ public async Task FromStreamAsync_CustomConfiguration() [Fact] public Task WhenNoMatchingFormatFoundAsync_Throws_UnknownImageFormatException() { - DecoderOptions options = new() { Configuration = new() }; + DecoderOptions options = new() { Configuration = new Configuration() }; AsyncStreamWrapper asyncStream = new(this.DataStream, () => false); return Assert.ThrowsAsync(async () => await Image.IdentifyAsync(options, asyncStream)); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index 996310d8c3..b0875a1e63 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; @@ -34,7 +35,7 @@ public abstract class ImageLoadTestBase : IDisposable public Configuration LocalConfiguration { get; } - public TestFormat TestFormat { get; } = new TestFormat(); + public TestFormat TestFormat { get; } = new(); /// /// Gets the top-level configuration in the context of this test case. @@ -57,7 +58,7 @@ protected ImageLoadTestBase() // 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 ImageInfo(new Size(1, 1), new ImageMetadata { DecodedImageFormat = PngFormat.Instance }); this.localImageFormatMock = new Mock(); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs index 238096be49..171aa6d4c5 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs @@ -15,19 +15,18 @@ public class LoadPixelData [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 +35,96 @@ public void FromPixels(bool useSpan) 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]); - - Assert.Equal(Color.White, (Color)img[1, 0]); - Assert.Equal(Color.Black, (Color)img[1, 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.FromPixel(img[1, 0])); + Assert.Equal(Color.Black, Color.FromPixel(img[1, 1])); + } + + [Fact] + public void FromPixels_WithRowStride() + { + Rgba32[] data = + [ + Color.Black.ToPixel(), + Color.White.ToPixel(), + Color.Red.ToPixel(), // padding + Color.White.ToPixel(), + Color.Black.ToPixel(), + Color.Red.ToPixel() // padding + ]; + + using Image img = Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStride: 3); + 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.FromPixel(img[1, 0])); + Assert.Equal(Color.Black, Color.FromPixel(img[1, 1])); + } + + [Fact] + public void FromBytes_WithRowStrideInBytes() + { + byte[] data = + [ + 0, 0, 0, 255, // 0,0 + 255, 255, 255, 255, // 0,1 + 255, 0, 0, 255, // padding + 255, 255, 255, 255, // 1,0 + 0, 0, 0, 255, // 1,1 + 255, 0, 0, 255 // padding + ]; + + using Image img = Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStrideInBytes: 12); + 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.FromPixel(img[1, 0])); + Assert.Equal(Color.Black, Color.FromPixel(img[1, 1])); + } + + [Fact] + public void FromPixels_WithRowStride_InvalidStride_Throws() + { + Rgba32[] data = new Rgba32[6]; + Assert.ThrowsAny( + () => Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStride: 1)); + } + + [Fact] + public void FromPixels_WithRowStride_InvalidLength_Throws() + { + Rgba32[] data = new Rgba32[4]; + Assert.ThrowsAny( + () => Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStride: 3)); + } + + [Fact] + public void FromBytes_WithRowStrideInBytes_InvalidStride_Throws() + { + byte[] data = new byte[24]; + Assert.ThrowsAny( + () => Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStrideInBytes: 10)); + } + + [Fact] + public void FromBytes_WithRowStrideInBytes_InvalidLength_Throws() + { + byte[] data = new byte[19]; + Assert.ThrowsAny( + () => Image.LoadPixelData(data.AsSpan(), width: 2, height: 2, rowStrideInBytes: 12)); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs index 3a47a0ea73..600bed0102 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs @@ -79,5 +79,16 @@ public void Configuration_Bytes_OutFormat_Agnostic() this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); } + + [Fact] + public void FromBytes_EmptySpan_Throws() + { + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + Assert.Throws(() => Image.Load(options, [])); + } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index 00ec985ac2..a0ce1e5d86 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -45,5 +45,9 @@ public void Bytes_OutFormat_Agnostic() VerifyDecodedImage(img); Assert.IsType(img.Metadata.DecodedImageFormat); } + + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.ThrowsAny(() => Image.Load([])); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs index 9b9f968bb4..c2609545d3 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs @@ -10,9 +10,9 @@ public partial class ImageTests { public class Load_FromStream_Throws : IDisposable { - private static readonly byte[] Data = new byte[] { 0x01 }; + private static readonly byte[] Data = [0x01]; - private MemoryStream Stream { get; } = new MemoryStream(Data); + private MemoryStream Stream { get; } = new(Data); [Fact] public void Image_Load_Throws_UnknownImageFormatException() @@ -32,6 +32,17 @@ public void Image_Load_T_Throws_UnknownImageFormatException() } }); - public void Dispose() => this.Stream?.Dispose(); + [Fact] + public void FromStream_Empty_Throws() + { + using MemoryStream ms = new(); + Assert.Throws(() => Image.Load(DecoderOptions.Default, ms)); + } + + public void Dispose() + { + this.Stream?.Dispose(); + GC.SuppressFinalize(this); + } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 46ae146942..65eeb124f6 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -39,7 +39,7 @@ public BitmapMemoryManager(Bitmap bitmap) } this.bitmap = bitmap; - var rectangle = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height); + System.Drawing.Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); this.bmpData = bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, bitmap.PixelFormat); this.length = bitmap.Width * bitmap.Height; } @@ -124,13 +124,13 @@ public override void Unpin() [Fact] public void WrapMemory_CreatedImageIsCorrect() { - var cfg = Configuration.CreateDefaultInstance(); - var metaData = new ImageMetadata(); + Configuration cfg = Configuration.CreateDefaultInstance(); + ImageMetadata metaData = new(); - var array = new Rgba32[25]; - var memory = new Memory(array); + Rgba32[] array = new Rgba32[25]; + Memory memory = new(array); - using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) + using (Image image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) { Assert.True(image.DangerousTryGetSinglePixelMemory(out Memory imageMem)); ref Rgba32 pixel0 = ref imageMem.Span[0]; @@ -141,6 +141,103 @@ public void WrapMemory_CreatedImageIsCorrect() } } + [Fact] + public void WrapMemory_MemoryOfT_Strided_CreatedImageIsCorrect() + { + Rgba32[] source = + [ + new Rgba32(1, 1, 1, 255), + new Rgba32(2, 2, 2, 255), + new Rgba32(3, 3, 3, 255), + new Rgba32(90, 90, 90, 255), + new Rgba32(4, 4, 4, 255), + new Rgba32(5, 5, 5, 255), + new Rgba32(6, 6, 6, 255), + new Rgba32(91, 91, 91, 255) + ]; + + using Image image = Image.WrapMemory(source.AsMemory(), width: 3, height: 2, rowStride: 4); + + Assert.Equal(4, image.Frames.RootFrame.PixelBuffer.RowStride); + Assert.False(image.DangerousTryGetSinglePixelMemory(out Memory _)); + Assert.Equal(source[0], image[0, 0]); + Assert.Equal(source[2], image[2, 0]); + Assert.Equal(source[4], image[0, 1]); + Assert.Equal(source[6], image[2, 1]); + } + + [Fact] + public void WrapMemory_MemoryOfT_Strided_CopyPixelDataTo_UsesRowStrideLayout() + { + Rgba32[] source = + [ + new Rgba32(1, 1, 1, 255), + new Rgba32(2, 2, 2, 255), + new Rgba32(3, 3, 3, 255), + new Rgba32(90, 90, 90, 255), + new Rgba32(4, 4, 4, 255), + new Rgba32(5, 5, 5, 255), + new Rgba32(6, 6, 6, 255), + new Rgba32(91, 91, 91, 255) + ]; + + using Image image = Image.WrapMemory(source.AsMemory(), width: 3, height: 2, rowStride: 4); + + Rgba32 sentinel = new(250, 1, 1, 255); + Rgba32[] destination = [sentinel, sentinel, sentinel, sentinel, sentinel, sentinel, sentinel]; + image.CopyPixelDataTo(destination); + + Assert.Equal(source[0], destination[0]); + Assert.Equal(source[1], destination[1]); + Assert.Equal(source[2], destination[2]); + Assert.Equal(sentinel, destination[3]); + Assert.Equal(source[4], destination[4]); + Assert.Equal(source[5], destination[5]); + Assert.Equal(source[6], destination[6]); + Assert.ThrowsAny(() => image.CopyPixelDataTo(new Rgba32[6])); + } + + [Fact] + public void WrapMemory_MemoryOfByte_Strided_CreatedImageIsCorrect() + { + int pixelSize = Unsafe.SizeOf(); + byte[] sourceBytes = new byte[8 * pixelSize]; + Span source = MemoryMarshal.Cast(sourceBytes.AsSpan()); + + source[0] = new Rgba32(1, 1, 1, 255); + source[1] = new Rgba32(2, 2, 2, 255); + source[2] = new Rgba32(3, 3, 3, 255); + source[4] = new Rgba32(4, 4, 4, 255); + source[5] = new Rgba32(5, 5, 5, 255); + source[6] = new Rgba32(6, 6, 6, 255); + + using Image image = Image.WrapMemory( + sourceBytes.AsMemory(), + width: 3, + height: 2, + rowStrideInBytes: 4 * pixelSize); + + Assert.Equal(4, image.Frames.RootFrame.PixelBuffer.RowStride); + Assert.False(image.DangerousTryGetSinglePixelMemory(out Memory _)); + Assert.Equal(source[0], image[0, 0]); + Assert.Equal(source[2], image[2, 0]); + Assert.Equal(source[4], image[0, 1]); + Assert.Equal(source[6], image[2, 1]); + } + + [Fact] + public void WrapMemory_Strided_InvalidStride_Throws() + { + Rgba32[] pixelSource = new Rgba32[8]; + byte[] byteSource = new byte[8 * Unsafe.SizeOf()]; + + Assert.ThrowsAny(() => Image.WrapMemory(pixelSource.AsMemory(), width: 3, height: 2, rowStride: 2)); + Assert.ThrowsAny(() => Image.WrapMemory(pixelSource.AsMemory(0, 6), width: 3, height: 2, rowStride: 4)); + + Assert.ThrowsAny(() => Image.WrapMemory(byteSource.AsMemory(), width: 3, height: 2, rowStrideInBytes: (4 * Unsafe.SizeOf()) - 1)); + Assert.ThrowsAny(() => Image.WrapMemory(byteSource.AsMemory(0, 6 * Unsafe.SizeOf()), width: 3, height: 2, rowStrideInBytes: 4 * Unsafe.SizeOf())); + } + [Fact] public void WrapSystemDrawingBitmap_WhenObserved() { @@ -149,22 +246,22 @@ public void WrapSystemDrawingBitmap_WhenObserved() return; } - using (var bmp = new Bitmap(51, 23)) + using (Bitmap bmp = new(51, 23)) { - using (var memoryManager = new BitmapMemoryManager(bmp)) + using (BitmapMemoryManager memoryManager = new(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)) + using (Image image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { Assert.Equal(memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory()); image.GetPixelMemoryGroup().Fill(bg); image.ProcessPixelRows(accessor => { - for (var i = 10; i < 20; i++) + for (int i = 10; i < 20; i++) { accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); } @@ -195,19 +292,19 @@ public void WrapSystemDrawingBitmap_WhenOwned() return; } - using (var bmp = new Bitmap(51, 23)) + using (Bitmap bmp = new(51, 23)) { - var memoryManager = new BitmapMemoryManager(bmp); - Bgra32 bg = Color.Red; - Bgra32 fg = Color.Green; + BitmapMemoryManager memoryManager = new(bmp); + Bgra32 bg = Color.Red.ToPixel(); + Bgra32 fg = Color.Green.ToPixel(); - using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) + using (Image image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) { Assert.Equal(memoryManager.Memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory()); image.GetPixelMemoryGroup().Fill(bg); image.ProcessPixelRows(accessor => { - for (var i = 10; i < 20; i++) + for (int i = 10; i < 20; i++) { accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); } @@ -227,13 +324,13 @@ public void WrapSystemDrawingBitmap_WhenOwned() [Fact] public void WrapMemory_FromBytes_CreatedImageIsCorrect() { - var cfg = Configuration.CreateDefaultInstance(); - var metaData = new ImageMetadata(); + Configuration cfg = Configuration.CreateDefaultInstance(); + ImageMetadata metaData = new(); - var array = new byte[25 * Unsafe.SizeOf()]; - var memory = new Memory(array); + byte[] array = new byte[25 * Unsafe.SizeOf()]; + Memory memory = new(array); - using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) + using (Image image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) { Assert.True(image.DangerousTryGetSinglePixelMemory(out Memory imageMem)); ref Rgba32 pixel0 = ref imageMem.Span[0]; @@ -252,16 +349,16 @@ public void WrapSystemDrawingBitmap_FromBytes_WhenObserved() return; } - using (var bmp = new Bitmap(51, 23)) + using (Bitmap bmp = new(51, 23)) { - using (var memoryManager = new BitmapMemoryManager(bmp)) + using (BitmapMemoryManager memoryManager = new(bmp)) { 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)) + using (Image image = Image.WrapMemory(byteMemory, bmp.Width, bmp.Height)) { Span pixelSpan = pixelMemory.Span; Span imageSpan = image.GetRootFramePixelBuffer().DangerousGetSingleMemory().Span; @@ -276,7 +373,7 @@ public void WrapSystemDrawingBitmap_FromBytes_WhenObserved() image.GetPixelMemoryGroup().Fill(bg); image.ProcessPixelRows(accessor => { - for (var i = 10; i < 20; i++) + for (int i = 10; i < 20; i++) { accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); } @@ -300,16 +397,16 @@ public void WrapSystemDrawingBitmap_FromBytes_WhenObserved() [InlineData(65536, 65537, 65536)] public unsafe void WrapMemory_Throws_OnTooLessWrongSize(int size, int width, int height) { - var cfg = Configuration.CreateDefaultInstance(); - var metaData = new ImageMetadata(); + Configuration cfg = Configuration.CreateDefaultInstance(); + ImageMetadata metaData = new(); - var array = new Rgba32[25]; + Rgba32[] array = new Rgba32[size]; Exception thrownException = null; fixed (void* ptr = array) { try { - using var image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData); + using Image image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData); } catch (Exception e) { @@ -328,14 +425,14 @@ public unsafe void WrapMemory_Throws_OnTooLessWrongSize(int size, int width, int [InlineData(2048, 32, 32)] public unsafe void WrapMemory_FromPointer_CreatedImageIsCorrect(int size, int width, int height) { - var cfg = Configuration.CreateDefaultInstance(); - var metaData = new ImageMetadata(); + Configuration cfg = Configuration.CreateDefaultInstance(); + ImageMetadata metaData = new(); - var array = new Rgba32[size]; + Rgba32[] array = new Rgba32[size]; fixed (void* ptr = array) { - using (var image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData)) + using (Image image = Image.WrapMemory(cfg, ptr, size * sizeof(Rgba32), width, height, metaData)) { Assert.True(image.DangerousTryGetSinglePixelMemory(out Memory imageMem)); Span imageSpan = imageMem.Span; @@ -359,17 +456,17 @@ public unsafe void WrapSystemDrawingBitmap_FromPointer() return; } - using (var bmp = new Bitmap(51, 23)) + using (Bitmap bmp = new(51, 23)) { - using (var memoryManager = new BitmapMemoryManager(bmp)) + using (BitmapMemoryManager memoryManager = new(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) { - using (var image = Image.WrapMemory(p, pixelMemory.Length, bmp.Width, bmp.Height)) + using (Image image = Image.WrapMemory(p, pixelMemory.Length, bmp.Width, bmp.Height)) { Span pixelSpan = pixelMemory.Span; Span imageSpan = image.GetRootFramePixelBuffer().DangerousGetSingleMemory().Span; @@ -381,7 +478,7 @@ public unsafe void WrapSystemDrawingBitmap_FromPointer() image.GetPixelMemoryGroup().Fill(bg); image.ProcessPixelRows(accessor => { - for (var i = 10; i < 20; i++) + for (int i = 10; i < 20; i++) { accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); } @@ -407,8 +504,8 @@ public unsafe void WrapSystemDrawingBitmap_FromPointer() [InlineData(65536, 65537, 65536)] public void WrapMemory_MemoryOfT_InvalidSize(int size, int height, int width) { - var array = new Rgba32[size]; - var memory = new Memory(array); + Rgba32[] array = new Rgba32[size]; + Memory memory = new(array); Assert.Throws(() => Image.WrapMemory(memory, height, width)); } @@ -421,8 +518,8 @@ public void WrapMemory_MemoryOfT_InvalidSize(int size, int height, int width) [InlineData(2048, 32, 32)] public void WrapMemory_MemoryOfT_ValidSize(int size, int height, int width) { - var array = new Rgba32[size]; - var memory = new Memory(array); + Rgba32[] array = new Rgba32[size]; + Memory memory = new(array); Image.WrapMemory(memory, height, width); } @@ -443,8 +540,8 @@ private class TestMemoryOwner : IMemoryOwner [InlineData(65536, 65537, 65536)] public void WrapMemory_IMemoryOwnerOfT_InvalidSize(int size, int height, int width) { - var array = new Rgba32[size]; - var memory = new TestMemoryOwner { Memory = array }; + Rgba32[] array = new Rgba32[size]; + TestMemoryOwner memory = new() { Memory = array }; Assert.Throws(() => Image.WrapMemory(memory, height, width)); } @@ -457,10 +554,10 @@ public void WrapMemory_IMemoryOwnerOfT_InvalidSize(int size, int height, int wid [InlineData(2048, 32, 32)] public void WrapMemory_IMemoryOwnerOfT_ValidSize(int size, int height, int width) { - var array = new Rgba32[size]; - var memory = new TestMemoryOwner { Memory = array }; + Rgba32[] array = new Rgba32[size]; + TestMemoryOwner memory = new() { Memory = array }; - using (var img = Image.WrapMemory(memory, width, height)) + using (Image img = Image.WrapMemory(memory, width, height)) { Assert.Equal(width, img.Width); Assert.Equal(height, img.Height); @@ -469,7 +566,7 @@ public void WrapMemory_IMemoryOwnerOfT_ValidSize(int size, int height, int width { for (int i = 0; i < height; ++i) { - var arrayIndex = width * i; + int arrayIndex = width * i; Span rowSpan = accessor.GetRowSpan(i); ref Rgba32 r0 = ref rowSpan[0]; @@ -490,8 +587,8 @@ public void WrapMemory_IMemoryOwnerOfT_ValidSize(int size, int height, int width [InlineData(65536, 65537, 65536)] public void WrapMemory_IMemoryOwnerOfByte_InvalidSize(int size, int height, int width) { - var array = new byte[size * Unsafe.SizeOf()]; - var memory = new TestMemoryOwner { Memory = array }; + byte[] array = new byte[size * Unsafe.SizeOf()]; + TestMemoryOwner memory = new() { Memory = array }; Assert.Throws(() => Image.WrapMemory(memory, height, width)); } @@ -504,11 +601,11 @@ public void WrapMemory_IMemoryOwnerOfByte_InvalidSize(int size, int height, int [InlineData(2048, 32, 32)] public void WrapMemory_IMemoryOwnerOfByte_ValidSize(int size, int height, int width) { - var pixelSize = Unsafe.SizeOf(); - var array = new byte[size * pixelSize]; - var memory = new TestMemoryOwner { Memory = array }; + int pixelSize = Unsafe.SizeOf(); + byte[] array = new byte[size * pixelSize]; + TestMemoryOwner memory = new() { Memory = array }; - using (var img = Image.WrapMemory(memory, width, height)) + using (Image img = Image.WrapMemory(memory, width, height)) { Assert.Equal(width, img.Width); Assert.Equal(height, img.Height); @@ -517,7 +614,7 @@ public void WrapMemory_IMemoryOwnerOfByte_ValidSize(int size, int height, int wi { for (int i = 0; i < height; ++i) { - var arrayIndex = pixelSize * width * i; + int arrayIndex = pixelSize * width * i; Span rowSpan = acccessor.GetRowSpan(i); ref Rgba32 r0 = ref rowSpan[0]; @@ -535,10 +632,11 @@ public void WrapMemory_IMemoryOwnerOfByte_ValidSize(int size, int height, int wi [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()]; - var memory = new Memory(array); + byte[] array = new byte[size * Unsafe.SizeOf()]; + Memory memory = new(array); Assert.Throws(() => Image.WrapMemory(memory, height, width)); } @@ -551,8 +649,8 @@ public void WrapMemory_MemoryOfByte_InvalidSize(int size, int height, int width) [InlineData(2048, 32, 32)] public void WrapMemory_MemoryOfByte_ValidSize(int size, int height, int width) { - var array = new byte[size * Unsafe.SizeOf()]; - var memory = new Memory(array); + byte[] array = new byte[size * Unsafe.SizeOf()]; + Memory memory = new(array); Image.WrapMemory(memory, height, width); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index ca51f7f5cb..16b8962c70 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -61,7 +61,7 @@ public void Configuration_Width_Height() 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 void GetSet(bool enforceDisco) 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() @@ -190,7 +190,7 @@ public void CopyPixelDataTo_Success(bool disco, bool byteSpan) } byte[] expected = TestUtils.FillImageWithRandomBytes(image); - byte[] actual = new byte[expected.Length]; + Span actual = new byte[expected.Length]; if (byteSpan) { image.CopyPixelDataTo(actual); diff --git a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs index 12caa6e7a9..8d1b54658f 100644 --- a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs +++ b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs @@ -31,7 +31,7 @@ static void RunTest() Configuration configuration = Configuration.Default.Clone(); configuration.PreferContiguousImageBuffers = true; - using var image = new Image(configuration, 2048, 2048); + using Image image = new(configuration, 2048, 2048); Assert.True(image.DangerousTryGetSinglePixelMemory(out Memory mem)); Assert.Equal(2048 * 2048, mem.Length); } @@ -69,7 +69,7 @@ static void RunTest(string formatInner) Configuration = configuration }; - using var image = Image.Load(options, path); + using Image image = Image.Load(options, path); File.Delete(path); Assert.Equal(1, image.GetPixelMemoryGroup().Count); } diff --git a/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs b/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs index 27cbe1a7ea..a5dfb0d23f 100644 --- a/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs @@ -32,7 +32,7 @@ protected abstract void ProcessPixelRowsImpl( [Fact] public void PixelAccessorDimensionsAreCorrect() { - using var image = new Image(123, 456); + using Image image = new(123, 456); this.ProcessPixelRowsImpl(image, accessor => { Assert.Equal(123, accessor.Width); @@ -43,7 +43,7 @@ public void PixelAccessorDimensionsAreCorrect() [Fact] public void WriteImagePixels_SingleImage() { - using var image = new Image(256, 256); + using Image image = new(256, 256); this.ProcessPixelRowsImpl(image, accessor => { for (int y = 0; y < accessor.Height; y++) @@ -71,7 +71,7 @@ public void WriteImagePixels_SingleImage() [Fact] public void WriteImagePixels_MultiImage2() { - using var img1 = new Image(256, 256); + using Image img1 = new(256, 256); Buffer2D buffer = img1.Frames.RootFrame.PixelBuffer; for (int y = 0; y < 256; y++) { @@ -82,7 +82,7 @@ public void WriteImagePixels_MultiImage2() } } - using var img2 = new Image(256, 256); + using Image img2 = new(256, 256); this.ProcessPixelRowsImpl(img1, img2, (accessor1, accessor2) => { @@ -109,7 +109,7 @@ public void WriteImagePixels_MultiImage2() [Fact] public void WriteImagePixels_MultiImage3() { - using var img1 = new Image(256, 256); + using Image img1 = new(256, 256); Buffer2D buffer2 = img1.Frames.RootFrame.PixelBuffer; for (int y = 0; y < 256; y++) { @@ -120,8 +120,8 @@ public void WriteImagePixels_MultiImage3() } } - using var img2 = new Image(256, 256); - using var img3 = new Image(256, 256); + using Image img2 = new(256, 256); + using Image img3 = new(256, 256); this.ProcessPixelRowsImpl(img1, img2, img3, (accessor1, accessor2, accessor3) => { @@ -154,8 +154,8 @@ public void WriteImagePixels_MultiImage3() [Fact] public void Disposed_ThrowsObjectDisposedException() { - using var nonDisposed = new Image(1, 1); - var disposed = new Image(1, 1); + using Image nonDisposed = new(1, 1); + Image disposed = new(1, 1); disposed.Dispose(); Assert.Throws(() => this.ProcessPixelRowsImpl(disposed, _ => { })); @@ -178,11 +178,11 @@ public void RetainsUnmangedBuffers1(bool throwException) static void RunTest(string testTypeName, string throwExceptionStr) { bool throwExceptionInner = bool.Parse(throwExceptionStr); - var buffer = UnmanagedBuffer.Allocate(100); - var allocator = new MockUnmanagedMemoryAllocator(buffer); + UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(100); + MockUnmanagedMemoryAllocator allocator = new(buffer); Configuration.Default.MemoryAllocator = allocator; - var image = new Image(10, 10); + Image image = new(10, 10); Assert.Equal(1, UnmanagedMemoryHandle.TotalOutstandingHandles); try @@ -215,13 +215,13 @@ public void RetainsUnmangedBuffers2(bool throwException) static void RunTest(string testTypeName, string throwExceptionStr) { bool throwExceptionInner = bool.Parse(throwExceptionStr); - var buffer1 = UnmanagedBuffer.Allocate(100); - var buffer2 = UnmanagedBuffer.Allocate(100); - var allocator = new MockUnmanagedMemoryAllocator(buffer1, buffer2); + UnmanagedBuffer buffer1 = UnmanagedBuffer.Allocate(100); + UnmanagedBuffer buffer2 = UnmanagedBuffer.Allocate(100); + MockUnmanagedMemoryAllocator allocator = new(buffer1, buffer2); Configuration.Default.MemoryAllocator = allocator; - var image1 = new Image(10, 10); - var image2 = new Image(10, 10); + Image image1 = new(10, 10); + Image image2 = new(10, 10); Assert.Equal(2, UnmanagedMemoryHandle.TotalOutstandingHandles); try @@ -255,15 +255,15 @@ public void RetainsUnmangedBuffers3(bool throwException) static void RunTest(string testTypeName, string throwExceptionStr) { bool throwExceptionInner = bool.Parse(throwExceptionStr); - var buffer1 = UnmanagedBuffer.Allocate(100); - var buffer2 = UnmanagedBuffer.Allocate(100); - var buffer3 = UnmanagedBuffer.Allocate(100); - var allocator = new MockUnmanagedMemoryAllocator(buffer1, buffer2, buffer3); + UnmanagedBuffer buffer1 = UnmanagedBuffer.Allocate(100); + UnmanagedBuffer buffer2 = UnmanagedBuffer.Allocate(100); + UnmanagedBuffer buffer3 = UnmanagedBuffer.Allocate(100); + MockUnmanagedMemoryAllocator allocator = new(buffer1, buffer2, buffer3); Configuration.Default.MemoryAllocator = allocator; - var image1 = new Image(10, 10); - var image2 = new Image(10, 10); - var image3 = new Image(10, 10); + Image image1 = new(10, 10); + Image image2 = new(10, 10); + Image image3 = new(10, 10); Assert.Equal(3, UnmanagedMemoryHandle.TotalOutstandingHandles); try @@ -313,7 +313,15 @@ public MockUnmanagedMemoryAllocator(params UnmanagedBuffer[] buffers) protected internal override int GetBufferCapacityInBytes() => int.MaxValue; - public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) => - this.buffers.Pop() as IMemoryOwner; + protected override AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) + { + object buffer = this.buffers.Pop(); + if (buffer is AllocationTrackedMemoryManager trackedBuffer) + { + return trackedBuffer; + } + + throw new InvalidMemoryOperationException("The requested buffer type does not match the mock allocator buffer type."); + } } } diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 73324eccd7..748c8a4f6d 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Tests; @@ -15,12 +15,14 @@ public void ImageInfoInitializesCorrectly() 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 void ImageInfoInitializesCorrectlyWithFrameMetadata() 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); @@ -49,4 +54,34 @@ public void ImageInfoInitializesCorrectlyWithFrameMetadata() Assert.Equal(meta, info.Metadata); Assert.Equal(frameMetadata.Count, info.FrameMetadataCollection.Count); } + + [Fact] + public void GetPixelMemorySize_UsesSingleFrameWhenFrameMetadataIsEmpty() + { + const int width = 10; + const int height = 20; + + ImageMetadata meta = new() { DecodedImageFormat = PngFormat.Instance }; + meta.GetPngMetadata(); + + ImageInfo info = new(new Size(width, height), meta); + + Assert.Equal(width * height * 4, info.GetPixelMemorySize()); + } + + [Fact] + public void GetPixelMemorySize_UsesFrameMetadataCountWhenAvailable() + { + const int width = 10; + const int height = 20; + IReadOnlyList frameMetadata = [new(), new(), new()]; + + ImageMetadata meta = new() { DecodedImageFormat = PngFormat.Instance }; + meta.GetPngMetadata(); + + ImageInfo info = new(new Size(width, height), meta, frameMetadata); + + Assert.Equal(width * height * 4 * frameMetadata.Count, info.GetPixelMemorySize()); + } + } diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index dc081e0bea..a1d5b0ee2b 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;net10.0 - net6.0 + net8.0 @@ -32,10 +32,17 @@ True PixelOperationsTests.Specialized.Generated.tt + + PreserveNewest + - + + @@ -43,6 +50,7 @@ + @@ -76,9 +84,5 @@ - - - - diff --git a/tests/ImageSharp.Tests/Issues/Issue594.cs b/tests/ImageSharp.Tests/Issues/Issue594.cs index 51f1ef7c67..36308edaeb 100644 --- a/tests/ImageSharp.Tests/Issues/Issue594.cs +++ b/tests/ImageSharp.Tests/Issues/Issue594.cs @@ -8,10 +8,8 @@ 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")] - public void NormalizedByte4() + [Fact] + public void NormalizedByte4Test() { // Test PackedValue Assert.Equal(0x0U, new NormalizedByte4(Vector4.Zero).PackedValue); @@ -33,68 +31,25 @@ public void NormalizedByte4() 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 NormalizedByte4(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] + public void NormalizedShort4Test() { // Test PackedValue Assert.Equal(0x0UL, new NormalizedShort4(Vector4.Zero).PackedValue); @@ -116,59 +71,20 @@ public void NormalizedShort4() 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] + public void Short4Test() { // Test the limits. Assert.Equal(0x0UL, new Short4(Vector4.Zero).PackedValue); @@ -192,8 +108,7 @@ public void Short4() 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 +127,11 @@ public void Short4() 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/Issues/Issue_396.cs b/tests/ImageSharp.Tests/Issues/Issue_396.cs new file mode 100644 index 0000000000..0d76f0c808 --- /dev/null +++ b/tests/ImageSharp.Tests/Issues/Issue_396.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Tests.Issues; + +// https://github.com/SixLabors/ImageSharp.Drawing/discussions/396 +public class Issue_396 +{ + [Theory] + [WithFile(TestImages.Png.Issue396Dragon, PixelTypes.Rgba32)] + public void GeneratesGifForInspection(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Image[] handImages = + [ + Image.Load(TestFile.Create(TestImages.Png.Issue396Hand1).Bytes), + Image.Load(TestFile.Create(TestImages.Png.Issue396Hand2).Bytes) + ]; + + try + { + using Image inputImage = provider.GetImage(); + using Image outputImage = new(handImages[0].Width, handImages[0].Height); + + const float scaleFactor = 0.6F; + const float squashStepFactor = 0.22F; + + for (int i = 0; i < handImages.Length; i++) + { + using Image frameImage = new(handImages[i].Width, handImages[i].Height); + float squashFactorY = 1 - (i * squashStepFactor); + + frameImage.ProcessPixelRows(accessor => + { + Rgba32 gray = Color.Gray.ToPixel(); + + for (int y = 0; y < accessor.Height; y++) + { + accessor.GetRowSpan(y).Fill(gray); + } + }); + + Rectangle targetRect = new( + (int)((frameImage.Width - (frameImage.Width * scaleFactor)) / 2), + (int)(frameImage.Height * (((1 - scaleFactor) / 2) + (scaleFactor * (1 - squashFactorY)))), + (int)(frameImage.Width * scaleFactor), + (int)(frameImage.Height * scaleFactor * squashFactorY)); + + using Image dragonFrame = inputImage.CloneAs(); + dragonFrame.Mutate(context => context.Resize(targetRect.Size)); + + frameImage.Mutate(context => + { + context.DrawImage(dragonFrame, targetRect.Location, 1F); + context.DrawImage(handImages[i], Point.Empty, 1F); + }); + + ImageFrame outputFrame = outputImage.Frames.AddFrame(frameImage.Frames.RootFrame); + GifFrameMetadata gifFrameMetadata = outputFrame.Metadata.GetGifMetadata(); + gifFrameMetadata.FrameDelay = 40; + gifFrameMetadata.DisposalMode = FrameDisposalMode.RestoreToBackground; + } + + for (int i = handImages.Length - 1; i > 0; i--) + { + outputImage.Frames.AddFrame(outputImage.Frames[i]); + } + + outputImage.Frames.RemoveFrame(0); + GifMetadata gifMetadata = outputImage.Metadata.GetGifMetadata(); + gifMetadata.RepeatCount = 0; + + Assert.Equal((handImages.Length * 2) - 1, outputImage.Frames.Count); + foreach (ImageFrame frame in outputImage.Frames) + { + Assert.Equal(FrameDisposalMode.RestoreToBackground, frame.Metadata.GetGifMetadata().DisposalMode); + } + + outputImage.DebugSaveMultiFrame(provider, "Issue396-source-frames", appendPixelTypeToFileName: false); + provider.Utility.SaveTestOutputFile( + outputImage, + "gif", + new GifEncoder(), + "Issue396-encoded", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + + // Save and decode the GIF so the encoder's optimized frame diffs can be inspected separately. + using MemoryStream gifStream = new(); + outputImage.SaveAsGif(gifStream); + gifStream.Position = 0; + + using Image decodedImage = Image.Load(gifStream); + decodedImage.DebugSaveMultiFrame(provider, "Issue396-decoded-frames", appendPixelTypeToFileName: false); + + Assert.Equal(outputImage.Frames.Count, decodedImage.Frames.Count); + } + finally + { + foreach (Image handImage in handImages) + { + handImage.Dispose(); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs index 33950c4697..c4cef5d93e 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/BufferTestSuite.cs @@ -58,7 +58,7 @@ public override int GetHashCode() } } - public static readonly TheoryData LengthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; + public static readonly TheoryData LengthValues = new() { 0, 1, 7, 1023, 1024 }; [Theory] [MemberData(nameof(LengthValues))] @@ -172,7 +172,7 @@ private void TestWriteAndReadElements(int desiredLength, Func getExpe { using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(desiredLength)) { - var expectedVals = new T[buffer.Length()]; + T[] expectedVals = new T[buffer.Length()]; for (int i = 0; i < buffer.Length(); i++) { @@ -213,7 +213,7 @@ public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) private T TestIndexOutOfRangeShouldThrow(int desiredLength) where T : struct, IEquatable { - var dummy = default(T); + T dummy = default(T); using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(desiredLength)) { diff --git a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs index b8dd4be8df..c4b75b5cfa 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs @@ -27,7 +27,7 @@ static void RunTest(string isGroupInner) int leakCounter = 0; MemoryDiagnostics.UndisposedAllocation += _ => Interlocked.Increment(ref leakCounter); - List buffers = new(); + List buffers = []; Assert.Equal(0, MemoryDiagnostics.TotalUndisposedAllocationCount); for (int length = 1024; length <= 64 * OneMb; length *= 2) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs index 517eda7076..8eb364828e 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/RefCountedLifetimeGuardTests.cs @@ -14,7 +14,7 @@ public class RefCountedLifetimeGuardTests [InlineData(3)] public void Dispose_ResultsInSingleRelease(int disposeCount) { - var guard = new MockLifetimeGuard(); + MockLifetimeGuard guard = new(); Assert.Equal(0, guard.ReleaseInvocationCount); for (int i = 0; i < disposeCount; i++) @@ -45,7 +45,7 @@ static void RunTest() [InlineData(3)] public void AddRef_PreventsReleaseOnDispose(int addRefCount) { - var guard = new MockLifetimeGuard(); + MockLifetimeGuard guard = new(); for (int i = 0; i < addRefCount; i++) { @@ -80,7 +80,7 @@ static void RunTest() [Fact] public void AddRefReleaseRefMisuse_DoesntLeadToMultipleReleases() { - var guard = new MockLifetimeGuard(); + MockLifetimeGuard guard = new(); guard.Dispose(); guard.AddRef(); guard.ReleaseRef(); @@ -91,8 +91,8 @@ public void AddRefReleaseRefMisuse_DoesntLeadToMultipleReleases() [Fact] public void UnmanagedBufferLifetimeGuard_Handle_IsReturnedByRef() { - var h = UnmanagedMemoryHandle.Allocate(10); - using var guard = new UnmanagedBufferLifetimeGuard.FreeHandle(h); + UnmanagedMemoryHandle h = UnmanagedMemoryHandle.Allocate(10); + using UnmanagedBufferLifetimeGuard.FreeHandle guard = new(h); Assert.True(guard.Handle.IsValid); guard.Handle.Free(); Assert.False(guard.Handle.IsValid); @@ -101,7 +101,7 @@ public void UnmanagedBufferLifetimeGuard_Handle_IsReturnedByRef() [MethodImpl(MethodImplOptions.NoInlining)] private static void LeakGuard(bool addRef) { - var guard = new MockLifetimeGuard(); + MockLifetimeGuard guard = new(); if (addRef) { guard.AddRef(); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/SharedArrayPoolBufferTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/SharedArrayPoolBufferTests.cs index a956190cd2..72b90c2387 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/SharedArrayPoolBufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/SharedArrayPoolBufferTests.cs @@ -16,7 +16,7 @@ public void AllocatesArrayPoolArray() static void RunTest() { - using (var buffer = new SharedArrayPoolBuffer(900)) + using (SharedArrayPoolBuffer buffer = new(900)) { Assert.Equal(900, buffer.GetSpan().Length); buffer.GetSpan().Fill(42); @@ -36,7 +36,7 @@ public void OutstandingReferences_RetainArrays() static void RunTest() { - var buffer = new SharedArrayPoolBuffer(900); + SharedArrayPoolBuffer buffer = new(900); Span span = buffer.GetSpan(); buffer.AddRef(); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs index 665f34a342..c33e6d1070 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs @@ -17,7 +17,7 @@ public BufferTests() } } - protected SimpleGcMemoryAllocator MemoryAllocator { get; } = new SimpleGcMemoryAllocator(); + protected SimpleGcMemoryAllocator MemoryAllocator { get; } = new(); public static TheoryData InvalidLengths { get; set; } = new() { @@ -48,6 +48,50 @@ public unsafe void Allocate_MemoryIsPinnableMultipleTimes() } } + [Fact] + public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose() + { + SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = 1 + }); + const int oneMb = 1 << 20; + + // Reserve the full limit with a single owner. + IMemoryOwner b0 = allocator.Allocate(oneMb); + + // Additional allocation should exceed the limit while the owner is live. + Assert.Throws(() => allocator.Allocate(1)); + + // Disposing the owner releases the reservation. + b0.Dispose(); + + // Allocation should succeed after the reservation is released. + allocator.Allocate(oneMb).Dispose(); + } + + [Fact] + public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose() + { + SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = 1 + }); + const int oneMb = 1 << 20; + + // Reserve the full limit with a single group. + MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024); + + // Additional allocation should exceed the limit while the group is live. + Assert.Throws(() => allocator.AllocateGroup(1, 1024)); + + // Disposing the group releases the reservation. + g0.Dispose(); + + // Allocation should succeed after the reservation is released. + allocator.AllocateGroup(oneMb, 1024).Dispose(); + } + [StructLayout(LayoutKind.Explicit, Size = 512)] private struct BigStruct { diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs index fd8b6af591..fd9760f48f 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs @@ -24,8 +24,8 @@ public void TrimPeriodElapsed_TrimsHalfOfUnusedArrays() RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() { - var trimSettings = new UniformUnmanagedMemoryPool.TrimSettings { TrimPeriodMilliseconds = 5_000 }; - var pool = new UniformUnmanagedMemoryPool(128, 256, trimSettings); + UniformUnmanagedMemoryPool.TrimSettings trimSettings = new() { TrimPeriodMilliseconds = 5_000 }; + UniformUnmanagedMemoryPool pool = new(128, 256, trimSettings); UnmanagedMemoryHandle[] a = pool.Rent(64); UnmanagedMemoryHandle[] b = pool.Rent(64); @@ -77,11 +77,11 @@ public void MultiplePoolInstances_TrimPeriodElapsed_AllAreTrimmed() static void RunTest() { - var trimSettings1 = new UniformUnmanagedMemoryPool.TrimSettings { TrimPeriodMilliseconds = 6_000 }; - var pool1 = new UniformUnmanagedMemoryPool(128, 256, trimSettings1); + UniformUnmanagedMemoryPool.TrimSettings trimSettings1 = new() { TrimPeriodMilliseconds = 6_000 }; + UniformUnmanagedMemoryPool pool1 = new(128, 256, trimSettings1); Thread.Sleep(8_000); // Let some callbacks fire already - var trimSettings2 = new UniformUnmanagedMemoryPool.TrimSettings { TrimPeriodMilliseconds = 3_000 }; - var pool2 = new UniformUnmanagedMemoryPool(128, 256, trimSettings2); + UniformUnmanagedMemoryPool.TrimSettings trimSettings2 = new() { TrimPeriodMilliseconds = 3_000 }; + UniformUnmanagedMemoryPool pool2 = new(128, 256, trimSettings2); pool1.Return(pool1.Rent(64)); pool2.Return(pool2.Rent(64)); @@ -104,14 +104,14 @@ static void RunTest() [MethodImpl(MethodImplOptions.NoInlining)] static void LeakPoolInstance() { - var trimSettings = new UniformUnmanagedMemoryPool.TrimSettings { TrimPeriodMilliseconds = 4_000 }; + UniformUnmanagedMemoryPool.TrimSettings trimSettings = new() { TrimPeriodMilliseconds = 4_000 }; _ = new UniformUnmanagedMemoryPool(128, 256, trimSettings); } } } public static readonly bool Is32BitProcess = !Environment.Is64BitProcess; - private static readonly List PressureArrays = new(); + private static readonly List PressureArrays = []; [Fact] public static void GC_Collect_OnHighLoad_TrimsEntirePool() @@ -129,13 +129,13 @@ static void RunTest() Assert.False(Environment.Is64BitProcess); const int oneMb = 1 << 20; - var trimSettings = new UniformUnmanagedMemoryPool.TrimSettings { HighPressureThresholdRate = 0.2f }; + UniformUnmanagedMemoryPool.TrimSettings trimSettings = new() { HighPressureThresholdRate = 0.2f }; GCMemoryInfo memInfo = GC.GetGCMemoryInfo(); int highLoadThreshold = (int)(memInfo.HighMemoryLoadThresholdBytes / oneMb); highLoadThreshold = (int)(trimSettings.HighPressureThresholdRate * highLoadThreshold); - var pool = new UniformUnmanagedMemoryPool(oneMb, 16, trimSettings); + UniformUnmanagedMemoryPool pool = new(oneMb, 16, trimSettings); pool.Return(pool.Rent(16)); Assert.Equal(16, UnmanagedMemoryHandle.TotalOutstandingHandles); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs index 69fc1a5f7d..ec79d91c3d 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs @@ -55,7 +55,7 @@ public void Dispose() [InlineData(7, 4)] public void Constructor_InitializesProperties(int arrayLength, int capacity) { - var pool = new UniformUnmanagedMemoryPool(arrayLength, capacity); + UniformUnmanagedMemoryPool pool = new(arrayLength, capacity); Assert.Equal(arrayLength, pool.BufferLength); Assert.Equal(capacity, pool.Capacity); } @@ -65,8 +65,8 @@ public void Constructor_InitializesProperties(int arrayLength, int capacity) [InlineData(8, 10)] public void Rent_SingleBuffer_ReturnsCorrectBuffer(int length, int capacity) { - var pool = new UniformUnmanagedMemoryPool(length, capacity); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(length, capacity); + using CleanupUtil cleanup = new(pool); for (int i = 0; i < capacity; i++) { @@ -83,7 +83,7 @@ public void Return_DoesNotDeallocateMemory() static void RunTest() { - var pool = new UniformUnmanagedMemoryPool(16, 16); + UniformUnmanagedMemoryPool pool = new(16, 16); UnmanagedMemoryHandle a = pool.Rent(); UnmanagedMemoryHandle[] b = pool.Rent(2); @@ -105,7 +105,7 @@ private static void CheckBuffer(int length, UniformUnmanagedMemoryPool pool, Unm Assert.True(span.SequenceEqual(expected)); } - private static unsafe Span GetSpan(UnmanagedMemoryHandle h, int length) => new Span(h.Pointer, length); + private static unsafe Span GetSpan(UnmanagedMemoryHandle h, int length) => new(h.Pointer, length); [Theory] [InlineData(1, 1)] @@ -114,8 +114,8 @@ private static void CheckBuffer(int length, UniformUnmanagedMemoryPool pool, Unm [InlineData(5, 10)] public void Rent_MultiBuffer_ReturnsCorrectBuffers(int length, int bufferCount) { - var pool = new UniformUnmanagedMemoryPool(length, 10); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(length, 10); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle[] handles = pool.Rent(bufferCount); cleanup.Register(handles); @@ -131,8 +131,8 @@ public void Rent_MultiBuffer_ReturnsCorrectBuffers(int length, int bufferCount) [Fact] public void Rent_MultipleTimesWithoutReturn_ReturnsDifferentHandles() { - var pool = new UniformUnmanagedMemoryPool(128, 10); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(128, 10); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle[] a = pool.Rent(2); cleanup.Register(a); UnmanagedMemoryHandle b = pool.Rent(); @@ -149,10 +149,10 @@ public void Rent_MultipleTimesWithoutReturn_ReturnsDifferentHandles() [InlineData(12, 4, 12)] public void RentReturnRent_SameBuffers(int totalCount, int rentUnit, int capacity) { - var pool = new UniformUnmanagedMemoryPool(128, capacity); - using var cleanup = new CleanupUtil(pool); - var allHandles = new HashSet(); - var handleUnits = new List(); + UniformUnmanagedMemoryPool pool = new(128, capacity); + using CleanupUtil cleanup = new(pool); + HashSet allHandles = new(); + List handleUnits = new(); UnmanagedMemoryHandle[] handles; for (int i = 0; i < totalCount; i += rentUnit) @@ -197,8 +197,8 @@ public void RentReturnRent_SameBuffers(int totalCount, int rentUnit, int capacit [Fact] public void Rent_SingleBuffer_OverCapacity_ReturnsInvalidBuffer() { - var pool = new UniformUnmanagedMemoryPool(7, 1000); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(7, 1000); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle[] initial = pool.Rent(1000); Assert.NotNull(initial); cleanup.Register(initial); @@ -212,8 +212,8 @@ public void Rent_SingleBuffer_OverCapacity_ReturnsInvalidBuffer() [InlineData(4, 7, 10)] public void Rent_MultiBuffer_OverCapacity_ReturnsNull(int initialRent, int attempt, int capacity) { - var pool = new UniformUnmanagedMemoryPool(128, capacity); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(128, capacity); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle[] initial = pool.Rent(initialRent); Assert.NotNull(initial); cleanup.Register(initial); @@ -228,8 +228,8 @@ public void Rent_MultiBuffer_OverCapacity_ReturnsNull(int initialRent, int attem [InlineData(3, 3, 7)] public void Rent_MultiBuff_BelowCapacity_Succeeds(int initialRent, int attempt, int capacity) { - var pool = new UniformUnmanagedMemoryPool(128, capacity); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(128, capacity); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle[] b0 = pool.Rent(initialRent); Assert.NotNull(b0); cleanup.Register(b0); @@ -250,8 +250,8 @@ public void RentReturnRelease_SubsequentRentReturnsDifferentHandles(bool multipl static void RunTest(string multipleInner) { - var pool = new UniformUnmanagedMemoryPool(16, 16); - using var cleanup = new CleanupUtil(pool); + UniformUnmanagedMemoryPool pool = new(16, 16); + using CleanupUtil cleanup = new(pool); UnmanagedMemoryHandle b0 = pool.Rent(); IntPtr h0 = b0.Handle; UnmanagedMemoryHandle b1 = pool.Rent(); @@ -290,7 +290,7 @@ public void Release_ShouldFreeRetainedMemory() static void RunTest() { - var pool = new UniformUnmanagedMemoryPool(16, 16); + UniformUnmanagedMemoryPool pool = new(16, 16); UnmanagedMemoryHandle a = pool.Rent(); UnmanagedMemoryHandle[] b = pool.Rent(2); pool.Return(a); @@ -306,13 +306,13 @@ static void RunTest() public void RentReturn_IsThreadSafe() { int count = Environment.ProcessorCount * 200; - var pool = new UniformUnmanagedMemoryPool(8, count); - using var cleanup = new CleanupUtil(pool); - var rnd = new Random(0); + UniformUnmanagedMemoryPool pool = new(8, count); + using CleanupUtil cleanup = new(pool); + Random rnd = new(0); Parallel.For(0, Environment.ProcessorCount, (int i) => { - var allHandles = new List(); + List allHandles = new(); int pauseAt = rnd.Next(100); for (int j = 0; j < 100; j++) { @@ -359,7 +359,7 @@ static void RunTest(string withGuardedBuffersInner) [MethodImpl(MethodImplOptions.NoInlining)] static void LeakPoolInstance(bool withGuardedBuffers) { - var pool = new UniformUnmanagedMemoryPool(16, 128); + UniformUnmanagedMemoryPool pool = new(16, 128); if (withGuardedBuffers) { UnmanagedMemoryHandle h = pool.Rent(); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index aa34a5c108..b029566664 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers; +using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; @@ -15,8 +16,8 @@ public class UniformUnmanagedPoolMemoryAllocatorTests { public class BufferTests1 : BufferTestSuite { - private static MemoryAllocator CreateMemoryAllocator() => - new UniformUnmanagedMemoryPoolMemoryAllocator( + private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() => + new( sharedArrayPoolThresholdInBytes: 1024, poolBufferSizeInBytes: 2048, maxPoolSizeInBytes: 2048 * 4, @@ -30,8 +31,8 @@ public BufferTests1() public class BufferTests2 : BufferTestSuite { - private static MemoryAllocator CreateMemoryAllocator() => - new UniformUnmanagedMemoryPoolMemoryAllocator( + private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() => + new( sharedArrayPoolThresholdInBytes: 512, poolBufferSizeInBytes: 1024, maxPoolSizeInBytes: 1024 * 4, @@ -44,7 +45,7 @@ public BufferTests2() } public static TheoryData AllocateData = - new TheoryData() + new() { { default(S4), 16, 256, 256, 1024, 64, 64, 1, -1, 64 }, { default(S4), 16, 256, 256, 1024, 256, 256, 1, -1, 256 }, @@ -69,7 +70,7 @@ public void AllocateGroup_BufferSizesAreCorrect( int expectedSizeOfLastBuffer) where T : struct { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator( + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new( sharedArrayPoolThresholdInBytes, maxContiguousPoolBufferInBytes, maxPoolSizeInBytes, @@ -86,13 +87,13 @@ public void AllocateGroup_BufferSizesAreCorrect( [Fact] public void AllocateGroup_MultipleTimes_ExceedPoolLimit() { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator( + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new( 64, 128, 1024, 1024); - var groups = new List>(); + List> groups = []; for (int i = 0; i < 16; i++) { int lengthInElements = 128 / Unsafe.SizeOf(); @@ -110,7 +111,7 @@ public void AllocateGroup_MultipleTimes_ExceedPoolLimit() [Fact] public void AllocateGroup_SizeInBytesOverLongMaxValue_ThrowsInvalidMemoryOperationException() { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(null); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(null); Assert.Throws(() => allocator.AllocateGroup(int.MaxValue * (long)int.MaxValue, int.MaxValue)); } @@ -128,7 +129,7 @@ public void Allocate_IncorrectAmount_ThrowsCorrect_InvalidMemoryOperationExcepti [Fact] public unsafe void Allocate_MemoryIsPinnableMultipleTimes() { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(null); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(null); using IMemoryOwner memoryOwner = allocator.Allocate(100); using (MemoryHandle pin = memoryOwner.Memory.Pin()) @@ -149,7 +150,7 @@ public void MemoryAllocator_Create_WithoutSettings_AllocatesDiscontiguousMemory( static void RunTest() { - var allocator = MemoryAllocator.Create(); + MemoryAllocator allocator = MemoryAllocator.Create(); long sixteenMegabytes = 16 * (1 << 20); // Should allocate 4 times 4MB discontiguos blocks: @@ -165,7 +166,7 @@ public void MemoryAllocator_Create_LimitPoolSize() static void RunTest() { - var allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions { MaximumPoolSizeMegabytes = 8 }); @@ -178,8 +179,8 @@ static void RunTest() g1.Dispose(); // Do some unmanaged allocations to make sure new non-pooled unmanaged allocations will grab different memory: - IntPtr dummy1 = Marshal.AllocHGlobal((IntPtr)B(8)); - IntPtr dummy2 = Marshal.AllocHGlobal((IntPtr)B(8)); + IntPtr dummy1 = Marshal.AllocHGlobal(checked((IntPtr)B(8))); + IntPtr dummy2 = Marshal.AllocHGlobal(checked((IntPtr)B(8))); using MemoryGroup g2 = allocator.AllocateGroup(B(8), 1024); using MemoryGroup g3 = allocator.AllocateGroup(B(8), 1024); @@ -205,7 +206,7 @@ public void BufferDisposal_ReturnsToPool(bool shared) static void RunTest(string sharedStr) { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(512, 1024, 16 * 1024, 1024); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(512, 1024, 16 * 1024, 1024); IMemoryOwner buffer0 = allocator.Allocate(bool.Parse(sharedStr) ? 300 : 600); buffer0.GetSpan()[0] = 42; buffer0.Dispose(); @@ -223,7 +224,7 @@ public void MemoryGroupDisposal_ReturnsToPool(bool shared) static void RunTest(string sharedStr) { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(512, 1024, 16 * 1024, 1024); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(512, 1024, 16 * 1024, 1024); MemoryGroup g0 = allocator.AllocateGroup(bool.Parse(sharedStr) ? 300 : 600, 100); g0.Single().Span[0] = 42; g0.Dispose(); @@ -238,7 +239,7 @@ public void ReleaseRetainedResources_ShouldFreePooledMemory() RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(128, 512, 16 * 512, 1024); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(128, 512, 16 * 512, 1024); MemoryGroup g = allocator.AllocateGroup(2048, 128); g.Dispose(); Assert.Equal(4, UnmanagedMemoryHandle.TotalOutstandingHandles); @@ -253,7 +254,7 @@ public void ReleaseRetainedResources_DoesNotFreeOutstandingBuffers() RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(128, 512, 16 * 512, 1024); + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(128, 512, 16 * 512, 1024); IMemoryOwner b = allocator.Allocate(256); MemoryGroup g = allocator.AllocateGroup(2048, 128); Assert.Equal(5, UnmanagedMemoryHandle.TotalOutstandingHandles); @@ -273,67 +274,75 @@ static void RunTest() [InlineData(1200)] // Group of two UniformUnmanagedMemoryPool buffers public void AllocateMemoryGroup_Finalization_ReturnsToPool(int length) { - if (TestEnvironment.IsMacOS) - { - // Skip on macOS: https://github.com/SixLabors/ImageSharp/issues/1887 - return; - } - - if (TestEnvironment.OSArchitecture == Architecture.Arm64) - { - // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 - return; - } - - if (!TestEnvironment.RunsOnCI) - { - // This may fail in local runs resulting in high memory load. - // Remove the condition for local debugging! - return; - } - - // RunTest(length.ToString()); - RemoteExecutor.Invoke(RunTest, length.ToString()).Dispose(); + RemoteExecutor.Invoke(RunTest, length.ToString(CultureInfo.InvariantCulture)).Dispose(); static void RunTest(string lengthStr) { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(512, 1024, 16 * 1024, 1024); - int lengthInner = int.Parse(lengthStr); - + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(512, 1024, 16 * 1024, 1024); + int lengthInner = int.Parse(lengthStr, CultureInfo.InvariantCulture); + + // We want to verify that a leaked (not disposed) `MemoryGroup` still returns its + // unmanaged handles into the pool when it is finalized. + // + // We intentionally do NOT validate this by checking the contents of the re-rented memory + // (contents are not guaranteed to be preserved) nor by comparing pointer values + // (the pool may return a different handle while still correctly pooling). + // + // Instead, we validate that after a forced GC+finalization cycle, a subsequent allocation + // of the same size does not cause the number of outstanding unmanaged handles to *increase* + // compared to a known baseline. + + // Establish a baseline: create one allocation and dispose it so the pool is initialized. + // (This ensures subsequent observations are not biased by first-time pool growth.) + allocator.AllocateGroup(lengthInner, 100).Dispose(); + int baselineHandles = UnmanagedMemoryHandle.TotalOutstandingHandles; + + // Leak one allocation and force finalization. AllocateGroupAndForget(allocator, lengthInner); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); - AllocateGroupAndForget(allocator, lengthInner, true); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - - using MemoryGroup g = allocator.AllocateGroup(lengthInner, 100); - Assert.Equal(42, g.First().Span[0]); + // Allocate again. If the leaked group was finalized correctly and returned to the pool, + // this should not require additional unmanaged allocations (ie, the handle count must not grow). + allocator.AllocateGroup(lengthInner, 100).Dispose(); + + // Note: we use "<=" instead of "==" here. + // + // After we record the baseline, the pool is allowed to legitimately *decrease* + // `UnmanagedMemoryHandle.TotalOutstandingHandles` by trimming retained buffers + // (eg. via the pool's trim timer/GC callbacks/high-pressure logic). + // + // What must not happen is the opposite: the leaked (non-disposed) group should be finalized + // and its handles returned to the pool such that allocating again does NOT require creating + // additional unmanaged handles. Therefore the only invariant we can reliably assert here is + // "no growth" relative to the baseline. + Assert.True(UnmanagedMemoryHandle.TotalOutstandingHandles <= baselineHandles); } } - private static void AllocateGroupAndForget(UniformUnmanagedMemoryPoolMemoryAllocator allocator, int length, bool check = false) + [MethodImpl(MethodImplOptions.NoInlining)] + private static void AllocateGroupAndForget(MemoryAllocator allocator, int length) { + // Allocate a group and drop the reference without disposing. + // The test relies on the group's finalizer to return the rented memory to the pool. MemoryGroup g = allocator.AllocateGroup(length, 100); - if (check) - { - Assert.Equal(42, g.First().Span[0]); - } - g.First().Span[0] = 42; + // Touch the memory to ensure the buffer is actually materialized/usable. + g[0].Span[0] = 42; if (length < 512) { - // For ArrayPool.Shared, first array will be returned to the TLS storage of the finalizer thread, - // repeat rental to make sure per-core buckets are also utilized. + // For ArrayPool.Shared, the first rented array may be stored in TLS on the finalizer thread. + // Repeat rental to increase the chance that per-core buckets are involved when length + // is small and allocations go through ArrayPool. MemoryGroup g1 = allocator.AllocateGroup(length, 100); - g1.First().Span[0] = 42; + g1[0].Span[0] = 42; + g1 = null; } + + g = null; } [Theory] @@ -341,88 +350,119 @@ private static void AllocateGroupAndForget(UniformUnmanagedMemoryPoolMemoryAlloc [InlineData(600)] // Group of single UniformUnmanagedMemoryPool buffer public void AllocateSingleMemoryOwner_Finalization_ReturnsToPool(int length) { - if (TestEnvironment.IsMacOS) - { - // Skip on macOS: https://github.com/SixLabors/ImageSharp/issues/1887 - return; - } - - if (TestEnvironment.OSArchitecture == Architecture.Arm64) - { - // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 - return; - } - - if (!TestEnvironment.RunsOnCI) - { - // This may fail in local runs resulting in high memory load. - // Remove the condition for local debugging! - return; - } - - // RunTest(length.ToString()); - RemoteExecutor.Invoke(RunTest, length.ToString()).Dispose(); + RemoteExecutor.Invoke(RunTest, length.ToString(CultureInfo.InvariantCulture)).Dispose(); static void RunTest(string lengthStr) { - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator(512, 1024, 16 * 1024, 1024); - int lengthInner = int.Parse(lengthStr); - + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(512, 1024, 16 * 1024, 1024); + int lengthInner = int.Parse(lengthStr, CultureInfo.InvariantCulture); + + // This test verifies pooling behavior when an `IMemoryOwner` is leaked (not disposed) + // and must be returned to the pool by finalization. + // + // We do NOT use a sentinel byte value to prove reuse because the contents of pooled buffers + // are not required to be preserved across rentals. + // + // Instead, we assert that after forcing GC+finalization, renting the same size again does not + // increase `UnmanagedMemoryHandle.TotalOutstandingHandles` above a baseline. + + // Establish a baseline: allocate+dispose once so the pool has a chance to materialize/retain buffers. + allocator.Allocate(lengthInner).Dispose(); + int baselineHandles = UnmanagedMemoryHandle.TotalOutstandingHandles; + + // Leak one allocation and force finalization. AllocateSingleAndForget(allocator, lengthInner); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); - AllocateSingleAndForget(allocator, lengthInner, true); + // Allocate again. If the leaked owner was finalized correctly and returned to the pool, + // this should not require additional unmanaged allocations (ie, the handle count must not grow). + allocator.Allocate(lengthInner).Dispose(); + + // Note: we use "<=" rather than "==". The pool may legitimately trim and free retained buffers, + // reducing the handle count between baseline and check. The invariant is "no growth". + Assert.True(UnmanagedMemoryHandle.TotalOutstandingHandles <= baselineHandles); + } + } + + [Theory] + [InlineData(1)] // SharedArrayPoolBuffer + [InlineData(2)] // UniformUnmanagedMemoryPool buffer + public void Allocate_AccumulativeLimit_Finalization_ReleasesOwnerReservation(int megabytes) + { + RemoteExecutor.Invoke(RunTest, megabytes.ToString(CultureInfo.InvariantCulture)).Dispose(); + + static void RunTest(string megabytesStr) + { + int megabytesInner = int.Parse(megabytesStr, CultureInfo.InvariantCulture); + int length = megabytesInner * (1 << 20); + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = megabytesInner + }); + + AllocateSingleAndForget(allocator, length); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); - using IMemoryOwner g = allocator.Allocate(lengthInner); - Assert.Equal(42, g.GetSpan()[0]); - GC.KeepAlive(allocator); + allocator.Allocate(length).Dispose(); } } [MethodImpl(MethodImplOptions.NoInlining)] - private static void AllocateSingleAndForget(UniformUnmanagedMemoryPoolMemoryAllocator allocator, int length, bool check = false) + private static void AllocateSingleAndForget(MemoryAllocator allocator, int length) { + // Allocate and intentionally do not dispose. IMemoryOwner g = allocator.Allocate(length); - if (check) - { - Assert.Equal(42, g.GetSpan()[0]); - } + // Touch the memory to ensure the buffer is actually materialized/usable. g.GetSpan()[0] = 42; if (length < 512) { - // For ArrayPool.Shared, first array will be returned to the TLS storage of the finalizer thread, - // repeat rental to make sure per-core buckets are also utilized. + // For ArrayPool.Shared, the first rented array may be stored in TLS on the finalizer thread. + // Repeat rental to increase the chance that per-core buckets are involved when length + // is small and allocations go through ArrayPool. IMemoryOwner g1 = allocator.Allocate(length); g1.GetSpan()[0] = 42; + g1 = null; } + + g = null; } [Fact] - public void Issue2001_NegativeMemoryReportedByGc() + public void AllocateGroup_AccumulativeLimit_Finalization_ReleasesGroupReservation() { RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() { - // Emulate GC.GetGCMemoryInfo() issue https://github.com/dotnet/runtime/issues/65466 - UniformUnmanagedMemoryPoolMemoryAllocator.GetTotalAvailableMemoryBytes = () => -402354176; - _ = MemoryAllocator.Create(); + const int megabytes = 5; + int length = megabytes * (1 << 20); + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = megabytes + }); + + AllocateGroupAndForget(allocator, length); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + + allocator.AllocateGroup(length, 1024).Dispose(); } } [Fact] public void Allocate_OverLimit_ThrowsInvalidMemoryOperationException() { - MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions { AllocationLimitMegabytes = 4 }); @@ -434,7 +474,7 @@ public void Allocate_OverLimit_ThrowsInvalidMemoryOperationException() [Fact] public void AllocateGroup_OverLimit_ThrowsInvalidMemoryOperationException() { - MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions { AllocationLimitMegabytes = 4 }); @@ -443,6 +483,143 @@ public void AllocateGroup_OverLimit_ThrowsInvalidMemoryOperationException() Assert.Throws(() => allocator.AllocateGroup(5 * oneMb, 1024)); } + [Fact] + public void Allocate_OverSingleBufferLimit_ThrowsInvalidMemoryOperationException() + { + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + SingleBufferAllocationLimitMegabytes = 2 + }); + const int oneMb = 1 << 20; + allocator.Allocate(2 * oneMb).Dispose(); // Should work + Assert.Throws(() => allocator.Allocate(3 * oneMb)); + + // The group limit is unchanged, so the same size still allocates as a discontiguous group. + allocator.AllocateGroup(3 * oneMb, 1024).Dispose(); + } + + [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] + public void MemoryAllocator_Create_RaisesSingleBufferLimit() + { + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + SingleBufferAllocationLimitMegabytes = 2047 + }); + + Assert.Equal(2047L * 1024 * 1024, (long)allocator.SingleBufferAllocationLimitBytes); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + [InlineData(2048)] + public void MemoryAllocatorOptions_InvalidSingleBufferLimit_Throws(int value) + { + MemoryAllocatorOptions options = default; + Assert.Throws(() => options.SingleBufferAllocationLimitMegabytes = value); + } + + [Fact] + public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose() + { + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = 1 + }); + const int oneMb = 1 << 20; + + // Reserve the full limit with a single owner. + IMemoryOwner b0 = allocator.Allocate(oneMb); + + // Additional allocation should exceed the limit while the owner is live. + Assert.Throws(() => allocator.Allocate(1)); + + // Disposing the owner releases the reservation. + b0.Dispose(); + + // Allocation should succeed after the reservation is released. + allocator.Allocate(oneMb).Dispose(); + } + + [Fact] + public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose() + { + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions + { + AccumulativeAllocationLimitMegabytes = 1 + }); + const int oneMb = 1 << 20; + + // Reserve the full limit with a single group. + MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024); + + // Additional allocation should exceed the limit while the group is live. + Assert.Throws(() => allocator.AllocateGroup(1, 1024)); + + // Disposing the group releases the reservation. + g0.Dispose(); + + // Allocation should succeed after the reservation is released. + allocator.AllocateGroup(oneMb, 1024).Dispose(); + } + + [Fact] + public void AllocateGroup_AccumulativeLimit_NonPoolFallback_TracksOncePerGroup() + { + // Configure the pool with zero capacity so multi-segment requests bypass both the + // single-buffer-from-pool path and MemoryGroup.TryAllocate(pool, ...) and fall + // through to MemoryGroup.Allocate(nonPoolAllocator, ...). The unmanaged segment + // size is small enough that the request must span multiple segments, which is the + // path where per-segment double-counting could regress. + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new( + sharedArrayPoolThresholdInBytes: 64 * 1024, + poolBufferSizeInBytes: 128 * 1024, + maxPoolSizeInBytes: 0, + unmanagedBufferSizeInBytes: 256 * 1024, + new MemoryAllocatorOptions { AccumulativeAllocationLimitMegabytes = 1 }); + + // 768 KB exceeds the pool buffer size, so the request takes the multi-segment + // non-pool fallback (three 256 KB segments). If tracking double-counted (group + // plus each segment), reservation would be 768 KB + 768 KB = 1.5 MB and exceed + // the 1 MB limit on allocation itself. + MemoryGroup g = allocator.AllocateGroup(768 * 1024, 1024); + Assert.True(g.Count > 1, "Test setup must exercise the multi-segment fallback path."); + + // Reservation should be exactly 768 KB; another 512 KB would push to 1.25 MB and throw. + Assert.Throws(() => allocator.Allocate(512 * 1024)); + + g.Dispose(); + + // After disposal the reservation is fully released; a second equivalent group succeeds. + allocator.AllocateGroup(768 * 1024, 1024).Dispose(); + } + + [Fact] + public void AllocateGroup_AccumulativeLimit_NonPoolFallback_Finalization_ReleasesGroupReservation() + { + RemoteExecutor.Invoke(RunTest).Dispose(); + + static void RunTest() + { + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new( + sharedArrayPoolThresholdInBytes: 64 * 1024, + poolBufferSizeInBytes: 128 * 1024, + maxPoolSizeInBytes: 0, + unmanagedBufferSizeInBytes: 256 * 1024, + new MemoryAllocatorOptions { AccumulativeAllocationLimitMegabytes = 1 }); + + // This exercises the non-pool multi-segment fallback, where reservation ownership has + // to follow the finalizable segment guards because the MemoryGroup itself has no finalizer. + AllocateGroupAndForget(allocator, 768 * 1024); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + + allocator.AllocateGroup(768 * 1024, 1024).Dispose(); + } + } + [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] public void MemoryAllocator_Create_SetHighLimit() { @@ -450,7 +627,7 @@ public void MemoryAllocator_Create_SetHighLimit() static void RunTest() { const long threeGB = 3L * (1 << 30); - MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions { AllocationLimitMegabytes = (int)(threeGB / 1024) }); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedBufferTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedBufferTests.cs index d0a5cfa9a7..3b33eae5e9 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedBufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedBufferTests.cs @@ -21,7 +21,7 @@ public AllocatorBufferTests() [Fact] public void Allocate_CreatesValidBuffer() { - using var buffer = UnmanagedBuffer.Allocate(10); + using UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(10); Span span = buffer.GetSpan(); Assert.Equal(10, span.Length); span[9] = 123; @@ -35,7 +35,7 @@ public unsafe void Dispose_DoesNotReleaseOutstandingReferences() static void RunTest() { - var buffer = UnmanagedBuffer.Allocate(10); + UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(10); Assert.Equal(1, UnmanagedMemoryHandle.TotalOutstandingHandles); Span span = buffer.GetSpan(); @@ -76,10 +76,10 @@ static void RunTest(string countStr) static List> FillList(int countInner) { - var l = new List>(); + List> l = new(); for (int i = 0; i < countInner; i++) { - var h = UnmanagedBuffer.Allocate(42); + UnmanagedBuffer h = UnmanagedBuffer.Allocate(42); l.Add(h); } diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedMemoryHandleTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedMemoryHandleTests.cs index 7a0736b545..59b793e012 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedMemoryHandleTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UnmanagedMemoryHandleTests.cs @@ -11,7 +11,7 @@ public class UnmanagedMemoryHandleTests [Fact] public unsafe void Allocate_AllocatesReadWriteMemory() { - var h = UnmanagedMemoryHandle.Allocate(128); + UnmanagedMemoryHandle h = UnmanagedMemoryHandle.Allocate(128); Assert.False(h.IsInvalid); Assert.True(h.IsValid); byte* ptr = (byte*)h.Handle; @@ -31,7 +31,7 @@ public unsafe void Allocate_AllocatesReadWriteMemory() [Fact] public void Free_ClosesHandle() { - var h = UnmanagedMemoryHandle.Allocate(128); + UnmanagedMemoryHandle h = UnmanagedMemoryHandle.Allocate(128); h.Free(); Assert.True(h.IsInvalid); Assert.Equal(IntPtr.Zero, h.Handle); @@ -47,11 +47,11 @@ public void Create_Free_AllocationsAreTracked(int count) static void RunTest(string countStr) { int countInner = int.Parse(countStr); - var l = new List(); + List l = new(); for (int i = 0; i < countInner; i++) { Assert.Equal(i, UnmanagedMemoryHandle.TotalOutstandingHandles); - var h = UnmanagedMemoryHandle.Allocate(42); + UnmanagedMemoryHandle h = UnmanagedMemoryHandle.Allocate(42); Assert.Equal(i + 1, UnmanagedMemoryHandle.TotalOutstandingHandles); l.Add(h); } @@ -68,7 +68,7 @@ static void RunTest(string countStr) [Fact] public void Equality_WhenTrue() { - var h1 = UnmanagedMemoryHandle.Allocate(10); + UnmanagedMemoryHandle h1 = UnmanagedMemoryHandle.Allocate(10); UnmanagedMemoryHandle h2 = h1; Assert.True(h1.Equals(h2)); @@ -82,8 +82,8 @@ public void Equality_WhenTrue() [Fact] public void Equality_WhenFalse() { - var h1 = UnmanagedMemoryHandle.Allocate(10); - var h2 = UnmanagedMemoryHandle.Allocate(10); + UnmanagedMemoryHandle h1 = UnmanagedMemoryHandle.Allocate(10); + UnmanagedMemoryHandle h2 = UnmanagedMemoryHandle.Allocate(10); Assert.False(h1.Equals(h2)); Assert.False(h2.Equals(h1)); diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.CopyTo.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.CopyTo.cs new file mode 100644 index 0000000000..b19095be9e --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.CopyTo.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Tests.Memory; + +public partial class Buffer2DTests +{ + [Fact] + public void CopyToSpan_StridedSource_WritesDestinationUsingSourceStride() + { + int[] source = [1, 2, 3, 777, 4, 5, 6, 888]; + int[] destination = [-1, -1, -1, -1, -1, -1, -1]; + + using Buffer2D buffer = Buffer2D.WrapMemory(source.AsMemory(), width: 3, height: 2, stride: 4); + buffer.CopyTo(destination); + + Assert.Equal(new[] { 1, 2, 3, -1, 4, 5, 6 }, destination); + } + + [Fact] + public void CopyToSpan_PackedSource_WritesPackedDestination() + { + int[] source = [1, 2, 3, 4, 5, 6]; + int[] destination = new int[6]; + + using Buffer2D buffer = Buffer2D.WrapMemory(source.AsMemory(), width: 3, height: 2); + buffer.CopyTo(destination); + + Assert.Equal(new[] { 1, 2, 3, 4, 5, 6 }, destination); + } + + [Fact] + public void CopyToSpan_StridedSource_ThrowsWhenDestinationTooShort() + { + int[] source = [1, 2, 3, 777, 4, 5, 6, 888]; + int[] destination = new int[6]; + + using Buffer2D buffer = Buffer2D.WrapMemory(source.AsMemory(), width: 3, height: 2, stride: 4); + Assert.Throws(() => buffer.CopyTo(destination)); + } +} diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs index 88c05fdd0e..6e9069a45e 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Memory; @@ -10,38 +10,36 @@ public partial class Buffer2DTests { public class SwapOrCopyContent { - private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator(); + private readonly TestMemoryAllocator memoryAllocator = new(); [Fact] public void SwapOrCopyContent_WhenBothAllocated() { - using (Buffer2D a = this.memoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean)) - using (Buffer2D b = this.memoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean)) - { - a[1, 3] = 666; - b[1, 3] = 444; + using Buffer2D a = this.memoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean); + using Buffer2D b = this.memoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean); + a[1, 3] = 666; + b[1, 3] = 444; - Memory aa = a.FastMemoryGroup.Single(); - Memory bb = b.FastMemoryGroup.Single(); + Memory aa = a.FastMemoryGroup.Single(); + Memory bb = b.FastMemoryGroup.Single(); - Buffer2D.SwapOrCopyContent(a, b); + Buffer2D.SwapOrCopyContent(a, b); - Assert.Equal(bb, a.FastMemoryGroup.Single()); - Assert.Equal(aa, b.FastMemoryGroup.Single()); + Assert.Equal(bb, a.FastMemoryGroup.Single()); + Assert.Equal(aa, b.FastMemoryGroup.Single()); - Assert.Equal(new Size(3, 7), a.Size()); - Assert.Equal(new Size(10, 5), b.Size()); + Assert.Equal(new Size(3, 7), a.Size); + Assert.Equal(new Size(10, 5), b.Size); - Assert.Equal(666, b[1, 3]); - Assert.Equal(444, a[1, 3]); - } + Assert.Equal(666, b[1, 3]); + Assert.Equal(444, a[1, 3]); } [Fact] public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer() { - using var destData = MemoryGroup.Wrap(new int[100]); - using var dest = new Buffer2D(destData, 10, 10); + using MemoryGroup destData = MemoryGroup.Wrap(new int[100]); + using Buffer2D dest = new(destData, 10, 10); using (Buffer2D source = this.memoryAllocator.Allocate2D(10, 10, AllocationOptions.Clean)) { @@ -112,11 +110,11 @@ public void WhenBothAreMemoryOwners_ShouldReplaceViews() [InlineData(true)] public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated) { - var data = new Rgba32[21]; - var color = new Rgba32(1, 2, 3, 4); + Rgba32[] data = new Rgba32[21]; + Rgba32 color = new(1, 2, 3, 4); - using var destOwner = new TestMemoryManager(data); - using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1); + using TestMemoryManager destOwner = new(data); + using Buffer2D dest = new(MemoryGroup.Wrap(destOwner.Memory), 21, 1); using Buffer2D source = this.memoryAllocator.Allocate2D(21, 1); @@ -136,11 +134,11 @@ public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated) [InlineData(true)] public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) { - var data = new Rgba32[21]; - var color = new Rgba32(1, 2, 3, 4); + Rgba32[] data = new Rgba32[21]; + Rgba32 color = new(1, 2, 3, 4); - using var destOwner = new TestMemoryManager(data); - using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1); + using TestMemoryManager destOwner = new(data); + using Buffer2D dest = new(MemoryGroup.Wrap(destOwner.Memory), 21, 1); using Buffer2D source = this.memoryAllocator.Allocate2D(22, 1); @@ -152,5 +150,74 @@ public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) Assert.Equal(color, source.MemoryGroup[0].Span[10]); Assert.NotEqual(color, dest.MemoryGroup[0].Span[10]); } + + [Fact] + public void WhenDestIsNotMemoryOwner_DifferentSizeSameTotal_PackedLayout_ShouldCopy() + { + int[] data = new int[6]; + using TestMemoryManager destOwner = new(data); + using Buffer2D dest = new(MemoryGroup.Wrap(destOwner.Memory), 2, 3); + using Buffer2D source = this.memoryAllocator.Allocate2D(3, 2, AllocationOptions.Clean); + + source[0, 0] = 1; + source[1, 0] = 2; + source[2, 0] = 3; + source[0, 1] = 4; + source[1, 1] = 5; + source[2, 1] = 6; + + bool swap = Buffer2D.SwapOrCopyContent(dest, source); + + Assert.False(swap); + Assert.Equal(new Size(3, 2), dest.Size); + Assert.Equal(6, dest[2, 1]); + } + + [Fact] + public void WhenDestIsNotMemoryOwner_DifferentSizeSameTotal_StridedLayout_ShouldCopy() + { + int[] data = new int[5]; + using Buffer2D dest = Buffer2D.WrapMemory(data.AsMemory(), width: 2, height: 2, stride: 3); + using Buffer2D source = this.memoryAllocator.Allocate2D(1, 5, AllocationOptions.Clean); + + source[0, 0] = 1; + source[0, 1] = 2; + source[0, 2] = 3; + source[0, 3] = 4; + source[0, 4] = 5; + + bool swap = Buffer2D.SwapOrCopyContent(dest, source); + + Assert.False(swap); + Assert.Equal(new Size(1, 5), dest.Size); + Assert.Equal(1, dest[0, 0]); + Assert.Equal(2, dest[0, 1]); + Assert.Equal(3, dest[0, 2]); + Assert.Equal(4, dest[0, 3]); + Assert.Equal(5, dest[0, 4]); + } + + [Fact] + public void WhenDestIsNotMemoryOwner_DifferentSizeDifferentTotal_ButBothLayoutsFit_ShouldCopy() + { + int[] data = new int[5]; + using TestMemoryManager destOwner = new(data); + using Buffer2D dest = new(MemoryGroup.Wrap(destOwner.Memory), 1, 3); + using Buffer2D source = this.memoryAllocator.Allocate2D(2, 2, AllocationOptions.Clean); + + source[0, 0] = 1; + source[1, 0] = 2; + source[0, 1] = 3; + source[1, 1] = 4; + + bool swap = Buffer2D.SwapOrCopyContent(dest, source); + + Assert.False(swap); + Assert.Equal(new Size(2, 2), dest.Size); + Assert.Equal(1, dest[0, 0]); + Assert.Equal(2, dest[1, 0]); + Assert.Equal(3, dest[0, 1]); + Assert.Equal(4, dest[1, 1]); + } } } diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.WrapMemory.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.WrapMemory.cs new file mode 100644 index 0000000000..f65f193eae --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.WrapMemory.cs @@ -0,0 +1,91 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Tests.Memory; + +public partial class Buffer2DTests +{ + [Fact] + public void WrapMemory_Packed_DangerousTryGetSinglePixelMemory_ReturnsTrue() + { + int[] data = [1, 2, 3, 4, 5, 6]; + + using Buffer2D buffer = Buffer2D.WrapMemory(data.AsMemory(), width: 3, height: 2, stride: 3); + + Assert.True(buffer.DangerousTryGetSingleMemory(out Memory memory)); + Assert.Equal(3, buffer.RowStride); + Assert.Equal(6, memory.Length); + Assert.True(Unsafe.AreSame(ref data[0], ref memory.Span[0])); + Assert.SpanPointsTo(buffer.DangerousGetRowSpan(1), data.AsMemory(), bufferOffset: 3); + } + + [Fact] + public void WrapMemory_Strided_DangerousTryGetSinglePixelMemory_ReturnsFalse() + { + int[] data = [1, 2, 3, 777, 4, 5, 6, 888]; + + using Buffer2D buffer = Buffer2D.WrapMemory(data.AsMemory(), width: 3, height: 2, stride: 4); + + Assert.False(buffer.DangerousTryGetSingleMemory(out Memory _)); + Assert.Equal(4, buffer.RowStride); + Assert.Equal(4, new Buffer2DRegion(buffer).Stride); + + Span row0 = buffer.DangerousGetRowSpan(0); + Span row1 = buffer.DangerousGetRowSpan(1); + + Assert.Equal(3, row0.Length); + Assert.Equal(3, row1.Length); + Assert.Equal(1, row0[0]); + Assert.Equal(3, row0[2]); + Assert.Equal(4, row1[0]); + Assert.Equal(6, row1[2]); + + Assert.SpanPointsTo(row0, data.AsMemory(), bufferOffset: 0); + Assert.SpanPointsTo(row1, data.AsMemory(), bufferOffset: 4); + } + + [Fact] + public void WrapMemory_Packed_WithTrailingData_DangerousTryGetSinglePixelMemory_IsLogicalSize() + { + int[] data = [1, 2, 3, 4, 5, 6, 777, 888]; + + using Buffer2D buffer = Buffer2D.WrapMemory(data.AsMemory(), width: 3, height: 2, stride: 3); + + Assert.True(buffer.DangerousTryGetSingleMemory(out Memory memory)); + Assert.Equal(6, memory.Length); + Assert.True(Unsafe.AreSame(ref data[0], ref memory.Span[0])); + } + + [Fact] + public void WrapMemory_Strided_ThrowsWhenStrideIsLessThanWidth() + { + int[] data = new int[10]; + + Assert.Throws(() => Buffer2D.WrapMemory(data.AsMemory(), width: 3, height: 2, stride: 2)); + } + + [Fact] + public void WrapMemory_Strided_ThrowsWhenInputMemoryTooSmall() + { + int[] data = new int[6]; + + Assert.Throws(() => Buffer2D.WrapMemory(data.AsMemory(), width: 3, height: 2, stride: 4)); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(0, 0)] + [InlineData(-1, 1)] + [InlineData(1, -1)] + public void WrapMemory_ThrowsWhenWidthOrHeightIsNotPositive(int width, int height) + { + int[] data = new int[16]; + + Assert.Throws(() => Buffer2D.WrapMemory(data.AsMemory(), width, height)); + Assert.Throws(() => Buffer2D.WrapMemory(data.AsMemory(), width, height, stride: 4)); + } +} diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 8ba3bf70a2..8e3f3d286a 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -24,7 +24,7 @@ public static void SpanPointsTo(Span span, Memory buffer, int bufferOff } } - private TestMemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); + private TestMemoryAllocator MemoryAllocator { get; } = new(); private const int Big = 99999; @@ -50,17 +50,11 @@ public unsafe void Construct(int bufferCapacity, int width, int height) [InlineData(Big, 1, 0)] [InlineData(60, 42, 0)] [InlineData(3, 0, 0)] - public unsafe void Construct_Empty(int bufferCapacity, int width, int height) + public unsafe void Construct_Empty_Throws(int bufferCapacity, int width, int height) { this.MemoryAllocator.BufferCapacityInBytes = sizeof(TestStructs.Foo) * bufferCapacity; - using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) - { - Assert.Equal(width, buffer.Width); - Assert.Equal(height, buffer.Height); - Assert.Equal(0, buffer.FastMemoryGroup.TotalLength); - Assert.Equal(0, buffer.DangerousGetSingleSpan().Length); - } + Assert.Throws(() => this.MemoryAllocator.Allocate2D(width, height)); } [Theory] @@ -147,7 +141,7 @@ public unsafe void DangerousGetRowSpan_UnmanagedAllocator(int width, int height) const int unpooledBufferSize = 8_000; int elementSize = sizeof(TestStructs.Foo); - var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator( + UniformUnmanagedMemoryPoolMemoryAllocator allocator = new( sharedPoolThreshold * elementSize, poolBufferSize * elementSize, maxPoolSize * elementSize, @@ -155,7 +149,7 @@ public unsafe void DangerousGetRowSpan_UnmanagedAllocator(int width, int height) using Buffer2D buffer = allocator.Allocate2D(width, height); - var rnd = new Random(42); + Random rnd = new(42); for (int y = 0; y < buffer.Height; y++) { @@ -208,7 +202,7 @@ public void TryGetPaddedRowSpanY(int bufferCapacity, int y, int padding, int exp } } - public static TheoryData GetRowSpanY_OutOfRange_Data = new TheoryData() + public static TheoryData GetRowSpanY_OutOfRange_Data = new() { { Big, 10, 8, -1 }, { Big, 10, 8, 8 }, @@ -227,7 +221,7 @@ public void GetRowSpan_OutOfRange(int bufferCapacity, int width, int height, int Assert.True(ex is ArgumentOutOfRangeException || ex is IndexOutOfRangeException); } - public static TheoryData Indexer_OutOfRange_Data = new TheoryData() + public static TheoryData Indexer_OutOfRange_Data = new() { { Big, 10, 8, 1, -1 }, { Big, 10, 8, 1, 8 }, @@ -284,7 +278,7 @@ public unsafe void Indexer(int bufferCapacity, int width, int height, int x, int [InlineData(5, 1, 1, 3, 2)] public void CopyColumns(int width, int height, int startIndex, int destIndex, int columnCount) { - var rnd = new Random(123); + Random rnd = new(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) { rnd.RandomFill(b.DangerousGetSingleSpan(), 0, 1); @@ -306,7 +300,7 @@ public void CopyColumns(int width, int height, int startIndex, int destIndex, in [Fact] public void CopyColumns_InvokeMultipleTimes() { - var rnd = new Random(123); + Random rnd = new(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) { rnd.RandomFill(b.DangerousGetSingleSpan(), 0, 1); @@ -339,25 +333,47 @@ public void PublicMemoryGroup_IsMemoryGroupView() Assert.NotSame(mgBefore, buffer1.MemoryGroup); } - public static TheoryData InvalidLengths { get; set; } = new() + public static TheoryData InvalidDimensions { get; set; } = new() + { + { new Size(-1, -1) }, + { new Size(0, 1) }, + { new Size(1, 0) }, + { new Size(0, 0) } + }; + + public static TheoryData OverflowDimensions { get; set; } = new() { - { new(-1, -1) }, - { new(32768, 32769) }, - { new(32769, 32768) } + { new Size(32768, 32769) }, + { new Size(32769, 32768) } }; [Theory] - [MemberData(nameof(InvalidLengths))] - public void Allocate_IncorrectAmount_ThrowsCorrect_InvalidMemoryOperationException(Size size) + [MemberData(nameof(InvalidDimensions))] + public void Allocate_InvalidDimensions_ThrowsArgumentOutOfRangeException(Size size) + => Assert.Throws(() => this.MemoryAllocator.Allocate2D(size.Width, size.Height)); + + [Theory] + [MemberData(nameof(InvalidDimensions))] + public void Allocate_InvalidDimensions_SizeOverload_ThrowsArgumentOutOfRangeException(Size size) + => Assert.Throws(() => this.MemoryAllocator.Allocate2D(new Size(size))); + + [Theory] + [MemberData(nameof(InvalidDimensions))] + public void Allocate_InvalidDimensions_OverAligned_ThrowsArgumentOutOfRangeException(Size size) + => Assert.Throws(() => this.MemoryAllocator.Allocate2DOveraligned(size.Width, size.Height, 1)); + + [Theory] + [MemberData(nameof(OverflowDimensions))] + public void Allocate_OverflowDimensions_ThrowsInvalidMemoryOperationException(Size size) => Assert.Throws(() => this.MemoryAllocator.Allocate2D(size.Width, size.Height)); [Theory] - [MemberData(nameof(InvalidLengths))] - public void Allocate_IncorrectAmount_ThrowsCorrect_InvalidMemoryOperationException_Size(Size size) + [MemberData(nameof(OverflowDimensions))] + public void Allocate_OverflowDimensions_SizeOverload_ThrowsInvalidMemoryOperationException(Size size) => Assert.Throws(() => this.MemoryAllocator.Allocate2D(new Size(size))); [Theory] - [MemberData(nameof(InvalidLengths))] - public void Allocate_IncorrectAmount_ThrowsCorrect_InvalidMemoryOperationException_OverAligned(Size size) + [MemberData(nameof(OverflowDimensions))] + public void Allocate_OverflowDimensions_OverAligned_ThrowsInvalidMemoryOperationException(Size size) => Assert.Throws(() => this.MemoryAllocator.Allocate2DOveraligned(size.Width, size.Height, 1)); } diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 46907b9c0e..a44d6ce1a1 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -7,17 +7,17 @@ namespace SixLabors.ImageSharp.Tests.Memory; public class BufferAreaTests { - private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator(); + private readonly TestMemoryAllocator memoryAllocator = new(); [Fact] public void Construct() { using Buffer2D buffer = this.memoryAllocator.Allocate2D(10, 20); - var rectangle = new Rectangle(3, 2, 5, 6); - var area = new Buffer2DRegion(buffer, rectangle); + Rectangle rectangle = new(3, 2, 5, 6); + Buffer2DRegion area = new(buffer, rectangle); Assert.Equal(buffer, area.Buffer); - Assert.Equal(rectangle, area.Rectangle); + Assert.Equal(rectangle, area.Bounds); } private Buffer2D CreateTestBuffer(int w, int h) @@ -43,7 +43,7 @@ public void Indexer(int bufferCapacity, int rx, int ry, int x, int y) { this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; using Buffer2D buffer = this.CreateTestBuffer(20, 30); - var r = new Rectangle(rx, ry, 5, 6); + Rectangle r = new(rx, ry, 5, 6); Buffer2DRegion region = buffer.GetRegion(r); @@ -62,7 +62,7 @@ public void GetRowSpan(int bufferCapacity, int rx, int ry, int y, int w, int h) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; using Buffer2D buffer = this.CreateTestBuffer(20, 30); - var r = new Rectangle(rx, ry, w, h); + Rectangle r = new(rx, ry, w, h); Buffer2DRegion region = buffer.GetRegion(r); @@ -87,10 +87,10 @@ public void GetSubArea() Buffer2DRegion area1 = area0.GetSubRegion(4, 4, 5, 5); - var expectedRect = new Rectangle(10, 12, 5, 5); + Rectangle expectedRect = new(10, 12, 5, 5); Assert.Equal(buffer, area1.Buffer); - Assert.Equal(expectedRect, area1.Rectangle); + Assert.Equal(expectedRect, area1.Bounds); int value00 = (12 * 100) + 10; Assert.Equal(value00, area1[0, 0]); @@ -120,7 +120,7 @@ public void Clear_FullArea(int bufferCapacity) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; using Buffer2D buffer = this.CreateTestBuffer(22, 13); - var emptyRow = new int[22]; + int[] emptyRow = new int[22]; buffer.GetRegion().Clear(); for (int y = 0; y < 13; y++) @@ -147,9 +147,9 @@ public void Clear_SubArea(int bufferCapacity) Assert.Equal(0, buffer[5, 5]); Assert.Equal(0, buffer[14, 14]); - for (int y = region.Rectangle.Y; y < region.Rectangle.Bottom; y++) + for (int y = region.Bounds.Y; y < region.Bounds.Bottom; y++) { - Span span = buffer.DangerousGetRowSpan(y).Slice(region.Rectangle.X, region.Width); + Span span = buffer.DangerousGetRowSpan(y).Slice(region.Bounds.X, region.Width); Assert.True(span.SequenceEqual(new int[region.Width])); } } diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs index a49ab77781..4e67df53b1 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs @@ -8,8 +8,8 @@ public class MemoryGroupIndexTests [Fact] public void Equal() { - var a = new MemoryGroupIndex(10, 1, 3); - var b = new MemoryGroupIndex(10, 1, 3); + MemoryGroupIndex a = new(10, 1, 3); + MemoryGroupIndex b = new(10, 1, 3); Assert.True(a.Equals(b)); Assert.True(a == b); @@ -21,8 +21,8 @@ public void Equal() [Fact] public void SmallerBufferIndex() { - var a = new MemoryGroupIndex(10, 3, 3); - var b = new MemoryGroupIndex(10, 5, 3); + MemoryGroupIndex a = new(10, 3, 3); + MemoryGroupIndex b = new(10, 5, 3); Assert.False(a == b); Assert.True(a != b); @@ -33,8 +33,8 @@ public void SmallerBufferIndex() [Fact] public void SmallerElementIndex() { - var a = new MemoryGroupIndex(10, 3, 3); - var b = new MemoryGroupIndex(10, 3, 9); + MemoryGroupIndex a = new(10, 3, 3); + MemoryGroupIndex b = new(10, 3, 9); Assert.False(a == b); Assert.True(a != b); @@ -45,7 +45,7 @@ public void SmallerElementIndex() [Fact] public void Increment() { - var a = new MemoryGroupIndex(10, 3, 3); + MemoryGroupIndex a = new(10, 3, 3); a += 1; Assert.Equal(new MemoryGroupIndex(10, 3, 4), a); } @@ -53,8 +53,8 @@ public void Increment() [Fact] public void Increment_OverflowBuffer() { - var a = new MemoryGroupIndex(10, 5, 3); - var b = new MemoryGroupIndex(10, 5, 9); + MemoryGroupIndex a = new(10, 5, 3); + MemoryGroupIndex b = new(10, 5, 9); a += 8; b += 1; diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs index 4c7de5412c..cca2230e6f 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs @@ -59,7 +59,7 @@ public void Allocate_FromMemoryAllocator_BufferSizesAreCorrect( this.MemoryAllocator.BufferCapacityInBytes = bufferCapacity; // Act: - using var g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferAlignment); + using MemoryGroup g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferAlignment); // Assert: ValidateAllocateMemoryGroup(expectedNumberOfBuffers, expectedBufferSize, expectedSizeOfLastBuffer, g); @@ -83,7 +83,7 @@ public void Allocate_FromPool_BufferSizesAreCorrect( return; } - var pool = new UniformUnmanagedMemoryPool(bufferCapacity, expectedNumberOfBuffers); + UniformUnmanagedMemoryPool pool = new(bufferCapacity, expectedNumberOfBuffers); // Act: Assert.True(MemoryGroup.TryAllocate(pool, totalLength, bufferAlignment, AllocationOptions.None, out MemoryGroup g)); @@ -98,7 +98,15 @@ public void Allocate_FromPool_BufferSizesAreCorrect( [InlineData(AllocationOptions.Clean)] public unsafe void Allocate_FromPool_AllocationOptionsAreApplied(AllocationOptions options) { - var pool = new UniformUnmanagedMemoryPool(10, 5); + // Disable trimming to avoid buffers being freed between Return and TryAllocate by the + // trim timer or the Gen2 GC callback. + UniformUnmanagedMemoryPool pool = new( + 10, + 5, + new UniformUnmanagedMemoryPool.TrimSettings + { + Rate = 0 + }); UnmanagedMemoryHandle[] buffers = pool.Rent(5); foreach (UnmanagedMemoryHandle b in buffers) { @@ -128,7 +136,7 @@ public void Allocate_FromPool_AroundLimit( int requestBytes, bool shouldSucceed) { - var pool = new UniformUnmanagedMemoryPool(bufferCapacityBytes, poolCapacity); + UniformUnmanagedMemoryPool pool = new(bufferCapacityBytes, poolCapacity); int alignmentElements = alignmentBytes / Unsafe.SizeOf(); int requestElements = requestBytes / Unsafe.SizeOf(); @@ -194,7 +202,7 @@ public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptio HashSet bufferHashes; int expectedBlockCount = 5; - using (var g = MemoryGroup.Allocate(this.MemoryAllocator, 500, 100, allocationOptions)) + using (MemoryGroup g = MemoryGroup.Allocate(this.MemoryAllocator, 500, 100, allocationOptions)) { IReadOnlyList allocationLog = this.MemoryAllocator.AllocationLog; Assert.Equal(expectedBlockCount, allocationLog.Count); diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs index 23aaf3c559..ee5cd9cd05 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs @@ -9,100 +9,74 @@ public partial class MemoryGroupTests { public class CopyTo : MemoryGroupTestsBase { - public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = - CopyAndTransformData; - - [Theory] - [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] - public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) + [Fact] + public void GroupToSpan_StridedSource_DoesNotRequireTrailingPadding() { - using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); - using MemoryGroup trg = this.CreateTestGroup(trgTotal, trgBufLen, false); - - src.CopyTo(trg); + using MemoryGroup src = this.CreateTestGroup(totalLength: 7, bufferLength: 4, fillSequence: true); + int[] trg = new int[6]; - int pos = 0; - MemoryGroupIndex i = src.MinIndex(); - MemoryGroupIndex j = trg.MinIndex(); - for (; i < src.MaxIndex(); i += 1, j += 1, pos++) - { - int a = src.GetElementAt(i); - int b = trg.GetElementAt(j); + src.CopyTo( + sourceStride: 4, + trg, + targetStride: 3, + width: 3, + height: 2); - Assert.True(a == b, $"Mismatch @ {pos} Expected: {a} Actual: {b}"); - } + Assert.Equal(new[] { 1, 2, 3, 5, 6, 7 }, trg); } [Fact] - public void WhenTargetBufferTooShort_Throws() + public void GroupToGroup_StridedCopy_DoesNotRequireTrailingPadding() { - using MemoryGroup src = this.CreateTestGroup(10, 20, true); - using MemoryGroup trg = this.CreateTestGroup(5, 20, false); - - Assert.Throws(() => src.CopyTo(trg)); + using MemoryGroup src = this.CreateTestGroup(totalLength: 11, bufferLength: 5, fillSequence: true); + using MemoryGroup trg = this.CreateTestGroup(totalLength: 13, bufferLength: 6, fillSequence: false); + + src.CopyTo( + sourceStride: 4, + trg, + targetStride: 5, + width: 3, + height: 3); + + Assert.Equal(1, GetElementAtLinearIndex(trg, 0)); + Assert.Equal(2, GetElementAtLinearIndex(trg, 1)); + Assert.Equal(3, GetElementAtLinearIndex(trg, 2)); + Assert.Equal(5, GetElementAtLinearIndex(trg, 5)); + Assert.Equal(6, GetElementAtLinearIndex(trg, 6)); + Assert.Equal(7, GetElementAtLinearIndex(trg, 7)); + Assert.Equal(9, GetElementAtLinearIndex(trg, 10)); + Assert.Equal(10, GetElementAtLinearIndex(trg, 11)); + Assert.Equal(11, GetElementAtLinearIndex(trg, 12)); } - [Theory] - [InlineData(30, 10, 40)] - [InlineData(42, 23, 42)] - [InlineData(1, 3, 10)] - [InlineData(0, 4, 0)] - public void GroupToSpan_Success(long totalLength, int bufferLength, int spanLength) + [Fact] + public void GroupToSpan_StridedSource_HeightOne() { - using MemoryGroup src = this.CreateTestGroup(totalLength, bufferLength, true); - var trg = new int[spanLength]; - src.CopyTo(trg); + using MemoryGroup src = this.CreateTestGroup(totalLength: 3, bufferLength: 2, fillSequence: true); + int[] trg = new int[3]; - int expected = 1; - foreach (int val in trg.AsSpan().Slice(0, (int)totalLength)) - { - Assert.Equal(expected, val); - expected++; - } - } + src.CopyTo( + sourceStride: 8, + trg, + targetStride: 3, + width: 3, + height: 1); - [Theory] - [InlineData(20, 7, 19)] - [InlineData(2, 1, 1)] - public void GroupToSpan_OutOfRange(long totalLength, int bufferLength, int spanLength) - { - using MemoryGroup src = this.CreateTestGroup(totalLength, bufferLength, true); - var trg = new int[spanLength]; - Assert.ThrowsAny(() => src.CopyTo(trg)); + Assert.Equal(new[] { 1, 2, 3 }, trg); } - [Theory] - [InlineData(30, 35, 10)] - [InlineData(42, 23, 42)] - [InlineData(10, 3, 1)] - [InlineData(0, 3, 0)] - public void SpanToGroup_Success(long totalLength, int bufferLength, int spanLength) + private static int GetElementAtLinearIndex(MemoryGroup group, int index) { - var src = new int[spanLength]; - for (int i = 0; i < src.Length; i++) - { - src[i] = i + 1; - } - - using MemoryGroup trg = this.CreateTestGroup(totalLength, bufferLength); - src.AsSpan().CopyTo(trg); - - int position = 0; - for (MemoryGroupIndex i = trg.MinIndex(); position < spanLength; i += 1, position++) + int pos = 0; + for (MemoryGroupIndex i = group.MinIndex(); i < group.MaxIndex(); i += 1, pos++) { - int expected = position + 1; - Assert.Equal(expected, trg.GetElementAt(i)); + if (pos == index) + { + return group.GetElementAt(i); + } } - } - [Theory] - [InlineData(10, 3, 11)] - [InlineData(0, 3, 1)] - public void SpanToGroup_OutOfRange(long totalLength, int bufferLength, int spanLength) - { - var src = new int[spanLength]; - using MemoryGroup trg = this.CreateTestGroup(totalLength, bufferLength, true); - Assert.ThrowsAny(() => src.AsSpan().CopyTo(trg)); + throw new ArgumentOutOfRangeException(nameof(index)); } } } diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index 316150c305..b82b20120a 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -12,7 +12,7 @@ public partial class MemoryGroupTests : MemoryGroupTestsBase [Fact] public void IsValid_TrueAfterCreation() { - using var g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); + using MemoryGroup g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); Assert.True(g.IsValid); } @@ -20,92 +20,22 @@ public void IsValid_TrueAfterCreation() [Fact] public void IsValid_FalseAfterDisposal() { - using var g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); + using MemoryGroup g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); g.Dispose(); Assert.False(g.IsValid); } -#pragma warning disable SA1509 - private static readonly TheoryData CopyAndTransformData = - new TheoryData() - { - { 20, 10, 20, 10 }, - { 20, 5, 20, 4 }, - { 20, 4, 20, 5 }, - { 18, 6, 20, 5 }, - { 19, 10, 20, 10 }, - { 21, 10, 22, 2 }, - { 1, 5, 5, 4 }, - - { 30, 12, 40, 5 }, - { 30, 5, 40, 12 }, - }; - - public class TransformTo : MemoryGroupTestsBase - { - public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = - CopyAndTransformData; - - [Theory] - [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] - public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) - { - using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); - using MemoryGroup trg = this.CreateTestGroup(trgTotal, trgBufLen, false); - - src.TransformTo(trg, MultiplyAllBy2); - - int pos = 0; - MemoryGroupIndex i = src.MinIndex(); - MemoryGroupIndex j = trg.MinIndex(); - for (; i < src.MaxIndex(); i += 1, j += 1, pos++) - { - int a = src.GetElementAt(i); - int b = trg.GetElementAt(j); - - Assert.True(b == 2 * a, $"Mismatch @ {pos} Expected: {a} Actual: {b}"); - } - } - - [Fact] - public void WhenTargetBufferTooShort_Throws() - { - using MemoryGroup src = this.CreateTestGroup(10, 20, true); - using MemoryGroup trg = this.CreateTestGroup(5, 20, false); - - Assert.Throws(() => src.TransformTo(trg, MultiplyAllBy2)); - } - } - - [Theory] - [InlineData(100, 5)] - [InlineData(100, 101)] - public void TransformInplace(int totalLength, int bufferLength) - { - using MemoryGroup src = this.CreateTestGroup(10, 20, true); - - src.TransformInplace(s => MultiplyAllBy2(s, s)); - - int cnt = 1; - for (MemoryGroupIndex i = src.MinIndex(); i < src.MaxIndex(); i += 1) - { - int val = src.GetElementAt(i); - Assert.Equal(expected: cnt * 2, val); - cnt++; - } - } - [Fact] public void Wrap() { int[] data0 = { 1, 2, 3, 4 }; int[] data1 = { 5, 6, 7, 8 }; int[] data2 = { 9, 10 }; - using var mgr0 = new TestMemoryManager(data0); - using var mgr1 = new TestMemoryManager(data1); + using TestMemoryManager mgr0 = new(data0); + using TestMemoryManager mgr1 = new(data1); - using var group = MemoryGroup.Wrap(mgr0.Memory, mgr1.Memory, data2); + using MemoryGroup group = MemoryGroup.Wrap(mgr0.Memory, mgr1.Memory, data2); Assert.Equal(3, group.Count); Assert.Equal(4, group.BufferLength); @@ -124,7 +54,7 @@ public void Wrap() } } - public static TheoryData GetBoundedSlice_SuccessData = new TheoryData() + public static TheoryData GetBoundedSlice_SuccessData = new() { { 300, 100, 110, 80 }, { 300, 100, 100, 100 }, @@ -153,7 +83,7 @@ public void GetBoundedSlice_WhenArgsAreCorrect(long totalLength, int bufferLengt } } - public static TheoryData GetBoundedSlice_ErrorData = new TheoryData() + public static TheoryData GetBoundedSlice_ErrorData = new() { { 300, 100, -1, 91 }, { 300, 100, 110, 91 }, @@ -217,19 +147,11 @@ public void Clear() using MemoryGroup group = this.CreateTestGroup(100, 10, true); group.Clear(); - var expectedRow = new int[10]; + int[] expectedRow = new int[10]; foreach (Memory memory in group) { Assert.True(memory.Span.SequenceEqual(expectedRow)); } } - private static void MultiplyAllBy2(ReadOnlySpan source, Span target) - { - Assert.Equal(source.Length, target.Length); - for (int k = 0; k < source.Length; k++) - { - target[k] = source[k] * 2; - } - } } diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs index 8fe6ef3ddb..0eaebbfef5 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers; public abstract class MemoryGroupTestsBase { - internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator(); + internal readonly TestMemoryAllocator MemoryAllocator = new(); /// /// Create a group, either uninitialized or filled with incrementing numbers starting with 1. @@ -15,7 +15,7 @@ public abstract class MemoryGroupTestsBase internal MemoryGroup CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false) { this.MemoryAllocator.BufferCapacityInBytes = bufferLength * sizeof(int); - var g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferLength); + MemoryGroup g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferLength); if (!fillSequence) { diff --git a/tests/ImageSharp.Tests/Memory/TestStructs.cs b/tests/ImageSharp.Tests/Memory/TestStructs.cs index ccbee5bd51..bb6a07ec96 100644 --- a/tests/ImageSharp.Tests/Memory/TestStructs.cs +++ b/tests/ImageSharp.Tests/Memory/TestStructs.cs @@ -19,7 +19,7 @@ public Foo(int a, double b) internal static Foo[] CreateArray(int size) { - var result = new Foo[size]; + Foo[] result = new Foo[size]; for (int i = 0; i < size; i++) { result[i] = new Foo(i + 1, i + 1); @@ -70,7 +70,7 @@ public AlignedFoo(int a, int b) internal static AlignedFoo[] CreateArray(int size) { - var result = new AlignedFoo[size]; + AlignedFoo[] result = new AlignedFoo[size]; for (int i = 0; i < size; i++) { result[i] = new AlignedFoo(i + 1, i + 1); diff --git a/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs b/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs index 252784a7c5..6c8dd2618c 100644 --- a/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs +++ b/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs @@ -20,25 +20,18 @@ static MemoryAllocatorValidator() private static void MemoryDiagnostics_MemoryReleased() { TestMemoryDiagnostics backing = LocalInstance.Value; - if (backing != null) - { - backing.TotalRemainingAllocated--; - } + backing?.OnReleased(); } private static void MemoryDiagnostics_MemoryAllocated() { TestMemoryDiagnostics backing = LocalInstance.Value; - if (backing != null) - { - backing.TotalAllocated++; - backing.TotalRemainingAllocated++; - } + backing?.OnAllocated(); } public static TestMemoryDiagnostics MonitorAllocations() { - var diag = new TestMemoryDiagnostics(); + TestMemoryDiagnostics diag = new(); LocalInstance.Value = diag; return diag; } @@ -48,11 +41,23 @@ public static TestMemoryDiagnostics MonitorAllocations() public static void ValidateAllocations(int expectedAllocationCount = 0) => LocalInstance.Value?.Validate(expectedAllocationCount); - public class TestMemoryDiagnostics : IDisposable + public sealed class TestMemoryDiagnostics : IDisposable { - public int TotalAllocated { get; set; } + private int totalAllocated; + private int totalRemainingAllocated; + + public int TotalAllocated => Volatile.Read(ref this.totalAllocated); + + public int TotalRemainingAllocated => Volatile.Read(ref this.totalRemainingAllocated); + + internal void OnAllocated() + { + Interlocked.Increment(ref this.totalAllocated); + Interlocked.Increment(ref this.totalRemainingAllocated); + } - public int TotalRemainingAllocated { get; set; } + internal void OnReleased() + => Interlocked.Decrement(ref this.totalRemainingAllocated); public void Validate(int expectedAllocationCount) { diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs index bcc9675404..e50d0b6174 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,20 +22,20 @@ public void ConstructorImageFrameMetadata() { 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(); 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] @@ -44,10 +45,10 @@ public void CloneIsDeep() ExifProfile exifProfile = new(); exifProfile.SetValue(ExifTag.Software, "UnitTest"); exifProfile.SetValue(ExifTag.Artist, "UnitTest"); - XmpProfile xmpProfile = new(Array.Empty()); + XmpProfile xmpProfile = new([]); IccProfile iccProfile = new() { - Header = new IccProfileHeader() + Header = new IccProfileHeader { CmmType = "Unittest" } @@ -73,7 +74,7 @@ public void CloneIsDeep() Assert.False(metaData.ExifProfile.Equals(clone.ExifProfile)); Assert.True(metaData.ExifProfile.Values.Count == clone.ExifProfile.Values.Count); Assert.False(ReferenceEquals(metaData.XmpProfile, clone.XmpProfile)); - Assert.True(metaData.XmpProfile.Data.Equals(clone.XmpProfile.Data)); + Assert.False(ReferenceEquals(metaData.XmpProfile.Data, clone.XmpProfile.Data)); Assert.False(metaData.GetGifMetadata().Equals(clone.GetGifMetadata())); Assert.False(metaData.IccProfile.Equals(clone.IccProfile)); Assert.False(metaData.IptcProfile.Equals(clone.IptcProfile)); diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs index 330b701474..ae02c3d57b 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 void ConstructorImageMetadata() [Fact] public void CloneIsDeep() { - var metaData = new ImageMetadata + ImageMetadata metaData = new() { ExifProfile = new ExifProfile(), HorizontalResolution = 4, @@ -53,7 +53,7 @@ public void CloneIsDeep() [Fact] public void HorizontalResolution() { - var metaData = new ImageMetadata(); + ImageMetadata metaData = new(); Assert.Equal(96, metaData.HorizontalResolution); metaData.HorizontalResolution = 0; @@ -69,7 +69,7 @@ public void HorizontalResolution() [Fact] public void VerticalResolution() { - var metaData = new ImageMetadata(); + ImageMetadata metaData = new(); Assert.Equal(96, metaData.VerticalResolution); metaData.VerticalResolution = 0; @@ -85,20 +85,19 @@ public void VerticalResolution() [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()); } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs index 76e2d35c45..ff8b034c56 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs @@ -25,10 +25,10 @@ public async Task ReadCicpMetadata_FromPng_Works(TestImageProvider(1, 1); - var original = CreateCicpProfile(); + using Image image = new(1, 1); + CicpProfile original = CreateCicpProfile(); image.Metadata.CicpProfile = original; - var encoder = new PngEncoder(); + PngEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -49,7 +49,7 @@ private static void CicpProfileContainsExpectedValues(CicpProfile cicp) private static CicpProfile CreateCicpProfile() { - var profile = new CicpProfile() + CicpProfile profile = new() { ColorPrimaries = CicpColorPrimaries.ItuRBt2020_2, TransferCharacteristics = CicpTransferCharacteristics.SmpteSt2084, @@ -70,7 +70,7 @@ private static void CicpProfileIsValidAndEqual(CicpProfile actual, CicpProfile o private static Image WriteAndRead(Image image, IImageEncoder encoder) { - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { image.Save(memStream, encoder); image.Dispose(); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 8072bebd77..2b47d09843 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -82,13 +82,13 @@ public void Constructor(TestImageWriteFormat imageFormat) public void ConstructorEmpty() { new ExifProfile(null); - new ExifProfile(Array.Empty()); + new ExifProfile([]); } [Fact] public void EmptyWriter() { - var profile = new ExifProfile() { Parts = ExifParts.GpsTags }; + ExifProfile profile = new() { Parts = ExifParts.GpsTags }; profile.SetValue(ExifTag.Copyright, "Copyright text"); byte[] bytes = profile.ToByteArray(); @@ -120,14 +120,14 @@ public void ConstructorCopy() [InlineData(TestImageWriteFormat.WebpLossy)] public void WriteFraction(TestImageWriteFormat imageFormat) { - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); double exposureTime = 1.0 / 1600; ExifProfile profile = GetExifProfile(); profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); - var image = new Image(1, 1); + Image image = new(1, 1); image.Metadata.ExifProfile = profile; image = WriteAndRead(image, imageFormat); @@ -231,7 +231,7 @@ public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueC IExifValue referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); Assert.Null(referenceBlackWhite.Value); - var expectedLatitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; + Rational[] expectedLatitude = [new(12.3), new(4.56), new(789.0)]; image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, expectedLatitude); IExifValue latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); @@ -308,11 +308,11 @@ public void RemoveEntry_Works() [Fact] public void Syncs() { - var exifProfile = new ExifProfile(); + ExifProfile exifProfile = new(); exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); - var metaData = new ImageMetadata + ImageMetadata metaData = new() { ExifProfile = exifProfile, HorizontalResolution = 200, @@ -362,19 +362,19 @@ public void Values() [Fact] public void ReadWriteLargeProfileJpg() { - ExifTag[] tags = { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription }; + ExifTag[] tags = [ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription]; foreach (ExifTag tag in tags) { // Arrange - var junk = new StringBuilder(); + StringBuilder junk = new(); for (int i = 0; i < 65600; i++) { junk.Append('a'); } - var image = new Image(100, 100); + Image image = new(100, 100); ExifProfile expectedProfile = CreateExifProfile(); - var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); + List expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); expectedProfile.SetValue(tag, junk.ToString()); image.Metadata.ExifProfile = expectedProfile; @@ -437,7 +437,7 @@ public void TestArrayValueWithUnspecifiedSize() byte[] bytes = profile.ToByteArray(); Assert.Equal(531, bytes.Length); - var profile2 = new ExifProfile(bytes); + ExifProfile profile2 = new(bytes); Assert.Equal(25, profile2.Values.Count); } @@ -449,7 +449,7 @@ public void TestArrayValueWithUnspecifiedSize() public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) { // Arrange - var image = new Image(1, 1); + Image image = new(1, 1); image.Metadata.ExifProfile = CreateExifProfile(); // Act @@ -472,11 +472,11 @@ public void ProfileToByteArray() // Arrange byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker.ToArray(); ExifProfile expectedProfile = CreateExifProfile(); - var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); + List expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); // Act byte[] actualBytes = expectedProfile.ToByteArray(); - var actualProfile = new ExifProfile(actualBytes); + ExifProfile actualProfile = new(actualBytes); // Assert Assert.NotNull(actualBytes); @@ -490,9 +490,50 @@ public void ProfileToByteArray() } } + /// + /// Lazy profile serialization filters selected sections without changing the all-parts passthrough. + /// + /// The sections to serialize. + /// Whether the IFD tag should remain. + /// Whether the GPS tag should remain. + [Theory] + [InlineData(ExifParts.All, true, true)] + [InlineData(ExifParts.IfdTags | ExifParts.ExifTags, true, false)] + [InlineData(ExifParts.GpsTags, false, true)] + [InlineData(ExifParts.None, false, false)] + public void ProfileToByteArray_AppliesPartsToLazyValues(ExifParts parts, bool keepMake, bool keepGps) + { + ExifProfile source = new(); + source.SetValue(ExifTag.Make, "POC"); + source.SetValue(ExifTag.GPSLatitudeRef, "N"); + byte[] originalData = source.ToByteArray(); + ExifProfile lazy = new(originalData) { Parts = parts }; + + byte[] filteredData = lazy.ToByteArray(); + ExifProfile result = new(filteredData); + + Assert.Equal(keepMake, result.TryGetValue(ExifTag.Make, out IExifValue make)); + Assert.Equal(keepGps, result.TryGetValue(ExifTag.GPSLatitudeRef, out IExifValue gps)); + + if (keepMake) + { + Assert.Equal("POC", make.Value); + } + + if (keepGps) + { + Assert.Equal("N", gps.Value); + } + + if (parts is ExifParts.All) + { + Assert.Same(originalData, filteredData); + } + } + private static ExifProfile CreateExifProfile() { - var profile = new ExifProfile(); + ExifProfile profile = new(); foreach (KeyValuePair exifProfileValue in TestProfileValues) { @@ -505,7 +546,7 @@ private static ExifProfile CreateExifProfile() [Fact] public void IfdStructure() { - var exif = new ExifProfile(); + ExifProfile exif = new(); exif.SetValue(ExifTag.XPAuthor, "Dan Petitt"); Span actualBytes = exif.ToByteArray(); @@ -547,7 +588,7 @@ private static Image WriteAndRead(Image image, TestImageWriteFor private static Image WriteAndReadJpeg(Image image) { - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); image.SaveAsJpeg(memStream); image.Dispose(); @@ -557,7 +598,7 @@ private static Image WriteAndReadJpeg(Image image) private static Image WriteAndReadPng(Image image) { - using var memStream = new MemoryStream(); + using MemoryStream memStream = new(); image.SaveAsPng(memStream); image.Dispose(); @@ -567,8 +608,8 @@ private static Image WriteAndReadPng(Image image) private static Image WriteAndReadWebp(Image image, WebpFileFormatType fileFormat) { - using var memStream = new MemoryStream(); - image.SaveAsWebp(memStream, new WebpEncoder() { FileFormat = fileFormat }); + using MemoryStream memStream = new(); + image.SaveAsWebp(memStream, new WebpEncoder { FileFormat = fileFormat }); image.Dispose(); memStream.Position = 0; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs index 8fe19b37de..983ff44937 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs @@ -11,7 +11,7 @@ public class ExifReaderTests [Fact] public void Read_DataIsEmpty_ReturnsEmptyCollection() { - var reader = new ExifReader(Array.Empty()); + ExifReader reader = new(Array.Empty()); IList result = reader.ReadValues(); @@ -21,7 +21,7 @@ public void Read_DataIsEmpty_ReturnsEmptyCollection() [Fact] public void Read_DataIsMinimal_ReturnsEmptyCollection() { - var reader = new ExifReader(new byte[] { 69, 120, 105, 102, 0, 0 }); + ExifReader reader = new(new byte[] { 69, 120, 105, 102, 0, 0 }); IList result = reader.ReadValues(); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 1154b3439c..3f13afa6fc 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -12,7 +12,7 @@ public class ExifTagDescriptionAttributeTests [Fact] public void TestExifTag() { - var exifProfile = new ExifProfile(); + ExifProfile exifProfile = new(); exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)1); IExifValue value = exifProfile.GetValue(ExifTag.ResolutionUnit); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index 99cafa8960..d7d6741baf 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -9,14 +9,14 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values; [Trait("Profile", "Exif")] public class ExifValuesTests { - public static TheoryData ByteTags => new TheoryData + public static TheoryData ByteTags => new() { { ExifTag.FaxProfile }, { ExifTag.ModeNumber }, { ExifTag.GPSAltitudeRef } }; - public static TheoryData ByteArrayTags => new TheoryData + public static TheoryData ByteArrayTags => new() { { ExifTag.ClipPath }, { ExifTag.VersionYear }, @@ -26,7 +26,7 @@ public class ExifValuesTests { ExifTag.GPSVersionID }, }; - public static TheoryData DoubleArrayTags => new TheoryData + public static TheoryData DoubleArrayTags => new() { { ExifTag.PixelScale }, { ExifTag.IntergraphMatrix }, @@ -34,7 +34,7 @@ public class ExifValuesTests { ExifTag.ModelTransform } }; - public static TheoryData LongTags => new TheoryData + public static TheoryData LongTags => new() { { ExifTag.SubfileType }, { ExifTag.SubIFDOffset }, @@ -59,7 +59,7 @@ public class ExifValuesTests { ExifTag.ImageNumber }, }; - public static TheoryData LongArrayTags => new TheoryData + public static TheoryData LongArrayTags => new() { { ExifTag.FreeOffsets }, { ExifTag.FreeByteCounts }, @@ -73,7 +73,7 @@ public class ExifValuesTests { ExifTag.IntergraphRegisters } }; - public static TheoryData NumberTags => new TheoryData + public static TheoryData NumberTags => new() { { ExifTag.ImageWidth }, { ExifTag.ImageLength }, @@ -85,7 +85,7 @@ public class ExifValuesTests { ExifTag.PixelYDimension } }; - public static TheoryData NumberArrayTags => new TheoryData + public static TheoryData NumberArrayTags => new() { { ExifTag.StripOffsets }, { ExifTag.StripByteCounts }, @@ -94,7 +94,7 @@ public class ExifValuesTests { ExifTag.ImageLayer } }; - public static TheoryData RationalTags => new TheoryData + public static TheoryData RationalTags => new() { { ExifTag.XPosition }, { ExifTag.YPosition }, @@ -131,7 +131,7 @@ public class ExifValuesTests { ExifTag.GPSHPositioningError }, }; - public static TheoryData RationalArrayTags => new TheoryData + public static TheoryData RationalArrayTags => new() { { ExifTag.WhitePoint }, { ExifTag.PrimaryChromaticities }, @@ -145,7 +145,7 @@ public class ExifValuesTests { ExifTag.LensSpecification } }; - public static TheoryData ShortTags => new TheoryData + public static TheoryData ShortTags => new() { { ExifTag.OldSubfileType }, { ExifTag.Compression }, @@ -196,7 +196,7 @@ public class ExifValuesTests { ExifTag.GPSDifferential } }; - public static TheoryData ShortArrayTags => new TheoryData + public static TheoryData ShortArrayTags => new() { { ExifTag.BitsPerSample }, { ExifTag.MinSampleValue }, @@ -220,7 +220,7 @@ public class ExifValuesTests { ExifTag.SubjectLocation } }; - public static TheoryData SignedRationalTags => new TheoryData + public static TheoryData SignedRationalTags => new() { { ExifTag.ShutterSpeedValue }, { ExifTag.BrightnessValue }, @@ -230,17 +230,17 @@ public class ExifValuesTests { ExifTag.CameraElevationAngle } }; - public static TheoryData SignedRationalArrayTags => new TheoryData + public static TheoryData SignedRationalArrayTags => new() { { ExifTag.Decode } }; - public static TheoryData SignedShortArrayTags => new TheoryData + public static TheoryData SignedShortArrayTags => new() { { ExifTag.TimeZoneOffset } }; - public static TheoryData StringTags => new TheoryData + public static TheoryData StringTags => new() { { ExifTag.ImageDescription }, { ExifTag.Make }, @@ -298,13 +298,13 @@ public class ExifValuesTests { ExifTag.GPSDateStamp }, }; - public static TheoryData UndefinedTags => new TheoryData + public static TheoryData UndefinedTags => new() { { ExifTag.FileSource }, { ExifTag.SceneType } }; - public static TheoryData UndefinedArrayTags => new TheoryData + public static TheoryData UndefinedArrayTags => new() { { ExifTag.JPEGTables }, { ExifTag.OECF }, @@ -320,14 +320,14 @@ public class ExifValuesTests { ExifTag.ImageSourceData }, }; - public static TheoryData EncodedStringTags => new TheoryData + public static TheoryData EncodedStringTags => new() { { ExifTag.UserComment }, { ExifTag.GPSProcessingMethod }, { ExifTag.GPSAreaInformation } }; - public static TheoryData Ucs2StringTags => new TheoryData + public static TheoryData Ucs2StringTags => new() { { ExifTag.XPTitle }, { ExifTag.XPComment }, @@ -347,7 +347,7 @@ public void ExifByteTests(ExifTag tag) Assert.True(value.TrySetValue((int)expected)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifByte)value; + ExifByte typed = (ExifByte)value; Assert.Equal(expected, typed.Value); } @@ -355,13 +355,13 @@ public void ExifByteTests(ExifTag tag) [MemberData(nameof(ByteArrayTags))] public void ExifByteArrayTests(ExifTag tag) { - byte[] expected = new[] { byte.MaxValue }; + byte[] expected = [byte.MaxValue]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifByteArray)value; + ExifByteArray typed = (ExifByteArray)value; Assert.Equal(expected, typed.Value); } @@ -369,13 +369,13 @@ public void ExifByteArrayTests(ExifTag tag) [MemberData(nameof(DoubleArrayTags))] public void ExifDoubleArrayTests(ExifTag tag) { - double[] expected = new[] { double.MaxValue }; + double[] expected = [double.MaxValue]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifDoubleArray)value; + ExifDoubleArray typed = (ExifDoubleArray)value; Assert.Equal(expected, typed.Value); } @@ -389,7 +389,7 @@ public void ExifLongTests(ExifTag tag) Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifLong)value; + ExifLong typed = (ExifLong)value; Assert.Equal(expected, typed.Value); } @@ -397,13 +397,13 @@ public void ExifLongTests(ExifTag tag) [MemberData(nameof(LongArrayTags))] public void ExifLongArrayTests(ExifTag tag) { - uint[] expected = new[] { uint.MaxValue }; + uint[] expected = [uint.MaxValue]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifLongArray)value; + ExifLongArray typed = (ExifLongArray)value; Assert.Equal(expected, typed.Value); } @@ -447,7 +447,7 @@ public void ExifNumberTests(ExifTag tag) Assert.True(value.TrySetValue((int)expected)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifNumber)value; + ExifNumber typed = (ExifNumber)value; Assert.Equal(expected, typed.Value); typed.Value = ushort.MaxValue + 1; @@ -458,13 +458,13 @@ public void ExifNumberTests(ExifTag tag) [MemberData(nameof(NumberArrayTags))] public void ExifNumberArrayTests(ExifTag tag) { - Number[] expected = new[] { new Number(uint.MaxValue) }; + Number[] expected = [new Number(uint.MaxValue)]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifNumberArray)value; + ExifNumberArray typed = (ExifNumberArray)value; Assert.Equal(expected, typed.Value); Assert.True(value.TrySetValue(int.MaxValue)); @@ -481,14 +481,14 @@ public void ExifNumberArrayTests(ExifTag tag) [MemberData(nameof(RationalTags))] public void ExifRationalTests(ExifTag tag) { - var expected = new Rational(21, 42); + Rational expected = new(21, 42); ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(new SignedRational(expected.ToDouble()))); Assert.True(value.TrySetValue(expected)); - var typed = (ExifRational)value; + ExifRational typed = (ExifRational)value; Assert.Equal(expected, typed.Value); } @@ -496,13 +496,13 @@ public void ExifRationalTests(ExifTag tag) [MemberData(nameof(RationalArrayTags))] public void ExifRationalArrayTests(ExifTag tag) { - Rational[] expected = new[] { new Rational(21, 42) }; + Rational[] expected = [new Rational(21, 42)]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifRationalArray)value; + ExifRationalArray typed = (ExifRationalArray)value; Assert.Equal(expected, typed.Value); } @@ -518,7 +518,7 @@ public void ExifShortTests(ExifTag tag) Assert.True(value.TrySetValue((short)expected)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifShort)value; + ExifShort typed = (ExifShort)value; Assert.Equal(expected, typed.Value); } @@ -526,13 +526,13 @@ public void ExifShortTests(ExifTag tag) [MemberData(nameof(ShortArrayTags))] public void ExifShortArrayTests(ExifTag tag) { - ushort[] expected = new[] { ushort.MaxValue }; + ushort[] expected = [ushort.MaxValue]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifShortArray)value; + ExifShortArray typed = (ExifShortArray)value; Assert.Equal(expected, typed.Value); } @@ -540,13 +540,13 @@ public void ExifShortArrayTests(ExifTag tag) [MemberData(nameof(SignedRationalTags))] public void ExifSignedRationalTests(ExifTag tag) { - var expected = new SignedRational(21, 42); + SignedRational expected = new(21, 42); ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifSignedRational)value; + ExifSignedRational typed = (ExifSignedRational)value; Assert.Equal(expected, typed.Value); } @@ -554,13 +554,13 @@ public void ExifSignedRationalTests(ExifTag tag) [MemberData(nameof(SignedRationalArrayTags))] public void ExifSignedRationalArrayTests(ExifTag tag) { - SignedRational[] expected = new[] { new SignedRational(21, 42) }; + SignedRational[] expected = [new SignedRational(21, 42)]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifSignedRationalArray)value; + ExifSignedRationalArray typed = (ExifSignedRationalArray)value; Assert.Equal(expected, typed.Value); } @@ -569,13 +569,13 @@ public void ExifSignedRationalArrayTests(ExifTag tag) [MemberData(nameof(SignedShortArrayTags))] public void ExifSignedShortArrayTests(ExifTag tag) { - short[] expected = new short[] { 21, 42 }; + short[] expected = [21, 42]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifSignedShortArray)value; + ExifSignedShortArray typed = (ExifSignedShortArray)value; Assert.Equal(expected, typed.Value); } @@ -589,7 +589,7 @@ public void ExifStringTests(ExifTag tag) Assert.False(value.TrySetValue(0M)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifString)value; + ExifString typed = (ExifString)value; Assert.Equal(expected, typed.Value); } @@ -604,7 +604,7 @@ public void ExifUndefinedTests(ExifTag tag) Assert.True(value.TrySetValue((int)expected)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifByte)value; + ExifByte typed = (ExifByte)value; Assert.Equal(expected, typed.Value); } @@ -612,13 +612,13 @@ public void ExifUndefinedTests(ExifTag tag) [MemberData(nameof(UndefinedArrayTags))] public void ExifUndefinedArrayTests(ExifTag tag) { - byte[] expected = new[] { byte.MaxValue }; + byte[] expected = [byte.MaxValue]; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(expected.ToString())); Assert.True(value.TrySetValue(expected)); - var typed = (ExifByteArray)value; + ExifByteArray typed = (ExifByteArray)value; Assert.Equal(expected, typed.Value); } @@ -628,18 +628,18 @@ public void ExifEncodedStringTests(ExifTag tag) { foreach (object code in Enum.GetValues(typeof(EncodedString.CharacterCode))) { - var charCode = (EncodedString.CharacterCode)code; + EncodedString.CharacterCode charCode = (EncodedString.CharacterCode)code; Assert.Equal(ExifEncodedStringHelpers.CharacterCodeBytesLength, ExifEncodedStringHelpers.GetCodeBytes(charCode).Length); const string expectedText = "test string"; - var expected = new EncodedString(charCode, expectedText); + EncodedString expected = new(charCode, expectedText); ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(123)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifEncodedString)value; + ExifEncodedString typed = (ExifEncodedString)value; Assert.Equal(expected, typed.Value); Assert.Equal(expectedText, (string)typed.Value); Assert.Equal(charCode, typed.Value.Code); @@ -656,7 +656,7 @@ public void ExifUcs2StringTests(ExifTag tag) Assert.False(value.TrySetValue(123)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifUcs2String)value; + ExifUcs2String typed = (ExifUcs2String)value; Assert.Equal(expected, typed.Value); Assert.True(value.TrySetValue(Encoding.GetEncoding("UCS-2").GetBytes(expected))); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs index 73ae46c2c9..86c6a5e9f2 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderCurvesTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs index 63585a3bd4..530da5594b 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderLutTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; @@ -52,6 +53,36 @@ internal void ReadClutF32(byte[] data, IccClut expected, int inChannelCount, int Assert.Equal(expected, output); } + [Fact] + public void ReadClut_WithOversizedDimensions_ThrowsInvalidIccProfileException() + { + byte[] gridPointCount = Enumerable.Repeat((byte)3, 15).ToArray(); + + Assert.Throws(() => CreateReader(new byte[8]).ReadClut8(15, 15, gridPointCount)); + Assert.Throws(() => CreateReader(new byte[8]).ReadClut16(15, 15, gridPointCount)); + Assert.Throws(() => CreateReader(new byte[8]).ReadClutF32(15, 15, gridPointCount)); + } + + /// + /// A complete profile header and element table do not make absent CLUT values readable. + /// + [Fact] + public void ReadTagDataEntry_WithTruncatedClut_RejectsMissingValues() + { + // A2B0 starts at byte 144; its element at byte 168 declares a 15-channel, three-point grid. + byte[] data = Convert.FromHexString( + "000000C874657374040000006D6E74725247422058595A200000000000000000000000006163737000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000014132423000000090000000386D7065740000" + + "000000000000000000010000001800000020636C7574000F000F030303030303030303030303030303000000000000000000"); + + IccDataReader reader = new(data); + IccTagTableEntry tag = new(IccProfileTag.AToB0, 144, 56); + + Assert.Throws(() => reader.ReadTagDataEntry(tag)); + Assert.Empty(new IccProfile(data).Entries); + } + [Theory] [MemberData(nameof(IccTestDataLut.Lut8TestData), MemberType = typeof(IccTestDataLut))] internal void ReadLut8(byte[] data, IccLut expected) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs index b81395bb2e..5ef102cd93 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMatrixTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; @@ -9,7 +10,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; public class IccDataReaderMatrixTests { [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix2D_FloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix2DFloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void ReadMatrix2D(byte[] data, int xCount, int yCount, bool isSingle, float[,] expected) { IccDataReader reader = CreateReader(data); @@ -20,7 +21,7 @@ public void ReadMatrix2D(byte[] data, int xCount, int yCount, bool isSingle, flo } [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix1D_ArrayTestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix1DArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void ReadMatrix1D(byte[] data, int yCount, bool isSingle, float[] expected) { IccDataReader reader = CreateReader(data); @@ -30,8 +31,5 @@ public void ReadMatrix1D(byte[] data, int yCount, bool isSingle, float[] expecte Assert.Equal(expected, output); } - private static IccDataReader CreateReader(byte[] data) - { - return new IccDataReader(data); - } + private static IccDataReader CreateReader(byte[] data) => new(data); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs index 9023b1b723..930665a07c 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderMultiProcessElementTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs index 91294a3dab..ee0464bb23 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderNonPrimitivesTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs index b6135cd197..9c5be4c675 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderPrimitivesTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs index d41707b7ce..e0cfa65431 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTagDataEntryTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; @@ -166,7 +167,7 @@ internal void ReadMeasurementTagDataEntry(byte[] data, IccMeasurementTagDataEntr [Theory] [MemberData( - nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData_Read), + nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestDataRead), MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiLocalizedUnicodeTagDataEntry(byte[] data, IccMultiLocalizedUnicodeTagDataEntry expected) { diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs index f2150cc03a..6f2e868c56 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -9,8 +9,5 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataReader; public class IccDataReaderTests { [Fact] - public void ConstructorThrowsNullException() - { - Assert.Throws(() => new IccDataReader(null)); - } + public void ConstructorThrowsNullException() => Assert.Throws(() => new IccDataReader(null)); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs index 1a23c8d002..79578f1ada 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterCurvesTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -80,8 +81,5 @@ internal void WriteSampledCurveElement(byte[] expected, IccSampledCurveElement d Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs index 4a3dc48bcb..27db7e4e61 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -80,8 +81,5 @@ internal void WriteLut16(byte[] expected, IccLut data, int count) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs index 1973d94b89..9317b45034 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests1.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -80,8 +81,5 @@ internal void WriteLut16(byte[] expected, IccLut data, int count) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs index 4ffc9e0c36..147a332c39 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterLutTests2.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -80,8 +81,5 @@ internal void WriteLut16(byte[] expected, IccLut data, int count) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs index 7d046aa49b..c72d4386ad 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMatrixTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; public class IccDataWriterMatrixTests { [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix2D_FloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix2DFloatArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix2D_Array(byte[] expected, int xCount, int yCount, bool isSingle, float[,] data) { using IccDataWriter writer = CreateWriter(); @@ -22,7 +23,7 @@ public void WriteMatrix2D_Array(byte[] expected, int xCount, int yCount, bool is } [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix2D_Matrix4x4TestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix2DMatrix4X4TestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix2D_Matrix4x4(byte[] expected, int xCount, int yCount, bool isSingle, Matrix4x4 data) { using IccDataWriter writer = CreateWriter(); @@ -34,7 +35,7 @@ public void WriteMatrix2D_Matrix4x4(byte[] expected, int xCount, int yCount, boo } [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix2D_DenseMatrixTestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix2DDenseMatrixTestData), MemberType = typeof(IccTestDataMatrix))] internal void WriteMatrix2D_DenseMatrix(byte[] expected, int xCount, int yCount, bool isSingle, in DenseMatrix data) { using IccDataWriter writer = CreateWriter(); @@ -46,7 +47,7 @@ internal void WriteMatrix2D_DenseMatrix(byte[] expected, int xCount, int yCount, } [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix1D_ArrayTestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix1DArrayTestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix1D_Array(byte[] expected, int yCount, bool isSingle, float[] data) { using IccDataWriter writer = CreateWriter(); @@ -58,7 +59,7 @@ public void WriteMatrix1D_Array(byte[] expected, int yCount, bool isSingle, floa } [Theory] - [MemberData(nameof(IccTestDataMatrix.Matrix1D_Vector3TestData), MemberType = typeof(IccTestDataMatrix))] + [MemberData(nameof(IccTestDataMatrix.Matrix1DVector3TestData), MemberType = typeof(IccTestDataMatrix))] public void WriteMatrix1D_Vector3(byte[] expected, int yCount, bool isSingle, Vector3 data) { using IccDataWriter writer = CreateWriter(); @@ -69,8 +70,5 @@ public void WriteMatrix1D_Vector3(byte[] expected, int yCount, bool isSingle, Ve Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs index ba2add5eb9..b7259d536a 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterMultiProcessElementTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -56,8 +57,5 @@ internal void WriteClutProcessElement(byte[] expected, IccClutProcessElement dat Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs index b17ed44419..b1b30d49fa 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterNonPrimitivesTests.cs @@ -3,6 +3,7 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -117,8 +118,5 @@ internal void WriteScreeningChannel(byte[] expected, IccScreeningChannel data) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs index fbe8fe1ced..c8f46d3aa4 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -112,8 +113,5 @@ public void WriteUFix8(byte[] expected, float data) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs index 7eda24c8cf..791bcee5e6 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTagDataEntryTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -153,7 +154,7 @@ internal void WriteMeasurementTagDataEntry(byte[] expected, IccMeasurementTagDat } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData_Write), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestDataWrite), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteMultiLocalizedUnicodeTagDataEntry(byte[] expected, IccMultiLocalizedUnicodeTagDataEntry data) { using IccDataWriter writer = CreateWriter(); @@ -404,8 +405,5 @@ internal void WriteUcrBgTagDataEntry(byte[] expected, IccUcrBgTagDataEntry data, Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 205941fcec..606a69d390 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.DataWriter; @@ -105,8 +106,5 @@ public void WriteArrayUInt64(byte[] expected, ulong[] data) Assert.Equal(expected, output); } - private static IccDataWriter CreateWriter() - { - return new IccDataWriter(); - } + private static IccDataWriter CreateWriter() => new(); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs index fbbea97fb3..27ce0ffa86 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc; @@ -20,8 +21,8 @@ public void CalculateHash_WithByteArray_CalculatesProfileHash(byte[] data, IccPr [Fact] public void CalculateHash_WithByteArray_DoesNotModifyData() { - byte[] data = IccTestDataProfiles.Profile_Random_Array; - var copy = new byte[data.Length]; + byte[] data = IccTestDataProfiles.ProfileRandomArray; + byte[] copy = new byte[data.Length]; Buffer.BlockCopy(data, 0, copy, 0, data.Length); IccProfile.CalculateHash(data); @@ -33,7 +34,7 @@ public void CalculateHash_WithByteArray_DoesNotModifyData() [MemberData(nameof(IccTestDataProfiles.ProfileValidityTestData), MemberType = typeof(IccTestDataProfiles))] public void CheckIsValid_WithProfiles_ReturnsValidity(byte[] data, bool expected) { - var profile = new IccProfile(data); + IccProfile profile = new(data); bool result = profile.CheckIsValid(); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs index 9b2ca2a275..610c831e8f 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs @@ -1,25 +1,47 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Buffers.Binary; using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc; [Trait("Profile", "Icc")] public class IccReaderTests { + [Theory] + [WithFile(TestImages.Jpeg.ICC.AdobeRgb, PixelTypes.Rgb24, 10, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 560)] + [WithFile(TestImages.Jpeg.ICC.AppleRGB, PixelTypes.Rgb24, 10, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 552)] + [WithFile(TestImages.Jpeg.ICC.ColorMatch, PixelTypes.Rgb24, 10, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 560)] + [WithFile(TestImages.Jpeg.ICC.WideRGB, PixelTypes.Rgb24, 10, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 560)] + [WithFile(TestImages.Jpeg.ICC.SRgb, PixelTypes.Rgb24, 17, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 3144)] + [WithFile(TestImages.Jpeg.ICC.ProPhoto, PixelTypes.Rgb24, 12, IccColorSpaceType.Rgb, IccColorSpaceType.CieXyz, 940)] + [WithFile(TestImages.Jpeg.ICC.CMYK, PixelTypes.Rgb24, 10, IccColorSpaceType.Cmyk, IccColorSpaceType.CieLab, 557168)] + public void ReadProfile_Works(TestImageProvider provider, int expectedEntries, IccColorSpaceType expectedDataColorSpace, IccColorSpaceType expectedConnectionSpace, uint expectedDataSize) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + IccProfile profile = image.Metadata.IccProfile; + + Assert.NotNull(profile); + Assert.Equal(expectedEntries, profile.Entries.Length); + Assert.Equal(expectedDataColorSpace, profile.Header.DataColorSpace); + Assert.Equal(expectedConnectionSpace, profile.Header.ProfileConnectionSpace); + Assert.Equal(expectedDataSize, profile.Header.Size); + } + [Fact] public void ReadProfile_NoEntries() { - IccReader reader = this.CreateReader(); - - IccProfile output = IccReader.Read(IccTestDataProfiles.Header_Random_Array); + IccProfile output = IccReader.Read(IccTestDataProfiles.HeaderRandomArray); Assert.Equal(0, output.Entries.Length); Assert.NotNull(output.Header); IccProfileHeader header = output.Header; - IccProfileHeader expected = IccTestDataProfiles.Header_Random_Read; + IccProfileHeader expected = IccTestDataProfiles.HeaderRandomRead; Assert.Equal(header.Class, expected.Class); Assert.Equal(header.CmmType, expected.CmmType); Assert.Equal(header.CreationDate, expected.CreationDate); @@ -40,18 +62,24 @@ public void ReadProfile_NoEntries() } [Fact] - public void ReadProfile_DuplicateEntry() + public void ReadProfile_WithUndersizedArrayTags_IgnoresTags() { - IccReader reader = this.CreateReader(); + const int tagCount = 100; + const int dataOffset = 132 + (tagCount * 12); + byte[] data = new byte[dataOffset + 16]; + BinaryPrimitives.WriteUInt32BigEndian(data.AsSpan(128), tagCount); - IccProfile output = IccReader.Read(IccTestDataProfiles.Profile_Random_Array); + for (int i = 0; i < tagCount; i++) + { + Span entry = data.AsSpan(132 + (i * 12), 12); + BinaryPrimitives.WriteUInt32BigEndian(entry, 0x73663332); + BinaryPrimitives.WriteUInt32BigEndian(entry[4..], dataOffset); + BinaryPrimitives.WriteUInt32BigEndian(entry[8..], 0); + } - Assert.Equal(2, output.Entries.Length); - Assert.True(ReferenceEquals(output.Entries[0], output.Entries[1])); - } + BinaryPrimitives.WriteUInt32BigEndian(data.AsSpan(dataOffset), 0x73663332); + IccProfile profile = new(data); - private IccReader CreateReader() - { - return new IccReader(); + Assert.Empty(profile.Entries); } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs index 89b63b7dcc..71c58c25dd 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.Tests.TestDataIcc; namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Icc; @@ -11,29 +12,20 @@ public class IccWriterTests [Fact] public void WriteProfile_NoEntries() { - IccWriter writer = this.CreateWriter(); - - var profile = new IccProfile + IccProfile profile = new() { - Header = IccTestDataProfiles.Header_Random_Write + Header = IccTestDataProfiles.HeaderRandomWrite }; byte[] output = IccWriter.Write(profile); - Assert.Equal(IccTestDataProfiles.Header_Random_Array, output); + Assert.Equal(IccTestDataProfiles.HeaderRandomArray, output); } [Fact] public void WriteProfile_DuplicateEntry() { - IccWriter writer = this.CreateWriter(); - - byte[] output = IccWriter.Write(IccTestDataProfiles.Profile_Random_Val); + byte[] output = IccWriter.Write(IccTestDataProfiles.ProfileRandomVal); - Assert.Equal(IccTestDataProfiles.Profile_Random_Array, output); - } - - private IccWriter CreateWriter() - { - return new IccWriter(); + Assert.Equal(IccTestDataProfiles.ProfileRandomArray, output); } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccLutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccLutTests.cs new file mode 100644 index 0000000000..f6705a1d08 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccLutTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.ICC.Various; + +[Trait("Profile", "Icc")] +public class IccLutTests +{ + /// + /// Gets lengths that exercise scalar execution, every SIMD width, mixed-width remainders, and multiple vectors. + /// + public static TheoryData LutLengths => new() + { + 0, + 1, + 7, + 8, + 9, + 15, + 16, + 17, + 31, + 32, + 33, + 63, + 64, + 65, + 255, + 256, + 257 + }; + + /// + /// Verifies that byte LUT construction preserves the scalar conversion result for every traversal shape. + /// + /// The number of LUT entries. + [Theory] + [MemberData(nameof(LutLengths))] + public void ByteConstructorMatchesScalarFormula(int length) + { + byte[] values = new byte[length]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = (byte)((i * 73) + 19); + } + + IccLut actual = new(values); + + Assert.Equal(values.Length, actual.Values.Length); + + for (int i = 0; i < values.Length; i++) + { + float expected = values[i] / (float)byte.MaxValue; + Assert.Equal(BitConverter.SingleToInt32Bits(expected), BitConverter.SingleToInt32Bits(actual.Values[i])); + } + } + + /// + /// Verifies that unsigned-short LUT construction preserves the scalar conversion result for every traversal shape. + /// + /// The number of LUT entries. + [Theory] + [MemberData(nameof(LutLengths))] + public void UInt16ConstructorMatchesScalarFormula(int length) + { + ushort[] values = new ushort[length]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = (ushort)((i * 12_347) + 1_019); + } + + IccLut actual = new(values); + + Assert.Equal(values.Length, actual.Values.Length); + + for (int i = 0; i < values.Length; i++) + { + float expected = values[i] / (float)ushort.MaxValue; + Assert.Equal(BitConverter.SingleToInt32Bits(expected), BitConverter.SingleToInt32Bits(actual.Values[i])); + } + } + + /// + /// Verifies bit-exact normalization for every possible unsigned-short input. + /// + [Fact] + public void UInt16ConstructorMatchesScalarFormulaForEveryValue() + { + ushort[] values = new ushort[ushort.MaxValue + 1]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = (ushort)i; + } + + IccLut actual = new(values); + + for (int i = 0; i < values.Length; i++) + { + float expected = values[i] / (float)ushort.MaxValue; + Assert.Equal(BitConverter.SingleToInt32Bits(expected), BitConverter.SingleToInt32Bits(actual.Values[i])); + } + } +} diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs index 968fa86c4e..5af672c210 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs @@ -19,7 +19,7 @@ public void ZeroIsEqualToDefault() [Fact] public void SetIsTrueWhenNonDefaultValue() { - var id = new IccProfileId(1, 2, 3, 4); + IccProfileId id = new(1, 2, 3, 4); Assert.True(id.IsSet); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 1a52ade629..2dbe5d343d 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -14,7 +14,7 @@ public static IEnumerable AllIptcTags() { foreach (object tag in Enum.GetValues(typeof(IptcTag))) { - yield return new object[] { tag }; + yield return [tag]; } } @@ -22,10 +22,10 @@ public static IEnumerable AllIptcTags() public void IptcProfile_WithUtf8Data_WritesEnvelopeRecord_Works() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); profile.SetValue(IptcTag.City, "ESPAÑA"); profile.UpdateData(); - byte[] expectedEnvelopeData = { 28, 1, 90, 0, 3, 27, 37, 71 }; + byte[] expectedEnvelopeData = [28, 1, 90, 0, 3, 27, 37, 71]; // act byte[] profileBytes = profile.Data; @@ -39,7 +39,7 @@ public void IptcProfile_WithUtf8Data_WritesEnvelopeRecord_Works() public void IptcProfile_SetValue_WithStrictEnabled_Works(IptcTag tag) { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); string value = new('s', tag.MaxLength() + 1); int expectedLength = tag.MaxLength(); @@ -56,7 +56,7 @@ public void IptcProfile_SetValue_WithStrictEnabled_Works(IptcTag tag) public void IptcProfile_SetValue_WithStrictDisabled_Works(IptcTag tag) { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); string value = new('s', tag.MaxLength() + 1); int expectedLength = value.Length; @@ -77,8 +77,8 @@ public void IptcProfile_SetValue_WithStrictDisabled_Works(IptcTag tag) public void IptcProfile_SetDateValue_Works(IptcTag tag) { // arrange - var profile = new IptcProfile(); - var datetime = new DateTimeOffset(new DateTime(1994, 3, 17)); + IptcProfile profile = new(); + DateTimeOffset datetime = new(new DateTime(1994, 3, 17)); // act profile.SetDateTimeValue(tag, datetime); @@ -96,8 +96,8 @@ public void IptcProfile_SetDateValue_Works(IptcTag tag) public void IptcProfile_SetTimeValue_Works(IptcTag tag) { // arrange - var profile = new IptcProfile(); - var dateTimeUtc = new DateTime(1994, 3, 17, 14, 15, 16, DateTimeKind.Utc); + IptcProfile profile = new(); + DateTime dateTimeUtc = new(1994, 3, 17, 14, 15, 16, DateTimeKind.Utc); DateTimeOffset dateTimeOffset = new DateTimeOffset(dateTimeUtc).ToOffset(TimeSpan.FromHours(2)); // act @@ -116,7 +116,7 @@ public void ReadIptcMetadata_FromJpg_Works(TestImageProvider pro using (Image image = provider.GetImage(JpegDecoder.Instance)) { Assert.NotNull(image.Metadata.IptcProfile); - var iptcValues = image.Metadata.IptcProfile.Values.ToList(); + List iptcValues = image.Metadata.IptcProfile.Values.ToList(); IptcProfileContainsExpectedValues(iptcValues); } } @@ -130,7 +130,7 @@ public void ReadIptcMetadata_FromTiff_Works(TestImageProvider pr { IptcProfile iptc = image.Frames.RootFrame.Metadata.IptcProfile; Assert.NotNull(iptc); - var iptcValues = iptc.Values.ToList(); + List iptcValues = iptc.Values.ToList(); IptcProfileContainsExpectedValues(iptcValues); } } @@ -170,7 +170,7 @@ public void ReadApp13_WithEmptyIptc_Works(TestImageProvider prov public void IptcProfile_ToAndFromByteArray_Works() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); const string expectedCaptionWriter = "unittest"; const string expectedCaption = "test"; profile.SetValue(IptcTag.CaptionWriter, expectedCaptionWriter); @@ -179,10 +179,10 @@ public void IptcProfile_ToAndFromByteArray_Works() // act profile.UpdateData(); byte[] profileBytes = profile.Data; - var profileFromBytes = new IptcProfile(profileBytes); + IptcProfile profileFromBytes = new(profileBytes); // assert - var iptcValues = profileFromBytes.Values.ToList(); + List iptcValues = profileFromBytes.Values.ToList(); ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, expectedCaptionWriter); ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption); } @@ -191,7 +191,7 @@ public void IptcProfile_ToAndFromByteArray_Works() public void IptcProfile_CloneIsDeep() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); const string captionWriter = "unittest"; const string caption = "test"; profile.SetValue(IptcTag.CaptionWriter, captionWriter); @@ -203,7 +203,7 @@ public void IptcProfile_CloneIsDeep() // assert Assert.Equal(2, clone.Values.Count()); - var cloneValues = clone.Values.ToList(); + List cloneValues = clone.Values.ToList(); ContainsIptcValue(cloneValues, IptcTag.CaptionWriter, captionWriter); ContainsIptcValue(cloneValues, IptcTag.Caption, "changed"); ContainsIptcValue(profile.Values.ToList(), IptcTag.Caption, caption); @@ -213,7 +213,7 @@ public void IptcProfile_CloneIsDeep() public void IptcValue_CloneIsDeep() { // arrange - var iptcValue = new IptcValue(IptcTag.Caption, System.Text.Encoding.UTF8, "test", true); + IptcValue iptcValue = new(IptcTag.Caption, System.Text.Encoding.UTF8, "test", true); // act IptcValue clone = iptcValue.DeepClone(); @@ -240,7 +240,7 @@ public void WritingImage_PreservesIptcProfile() // assert IptcProfile actual = reloadedImage.Metadata.IptcProfile; Assert.NotNull(actual); - var iptcValues = actual.Values.ToList(); + List iptcValues = actual.Values.ToList(); ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, expectedCaptionWriter); ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption); } @@ -263,7 +263,7 @@ public void WritingImage_PreservesIptcProfile() public void IptcProfile_AddRepeatable_Works(IptcTag tag) { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); const string expectedValue1 = "test"; const string expectedValue2 = "another one"; profile.SetValue(tag, expectedValue1, false); @@ -272,7 +272,7 @@ public void IptcProfile_AddRepeatable_Works(IptcTag tag) profile.SetValue(tag, expectedValue2, false); // assert - var values = profile.Values.ToList(); + List values = profile.Values.ToList(); Assert.Equal(2, values.Count); ContainsIptcValue(values, tag, expectedValue1); ContainsIptcValue(values, tag, expectedValue2); @@ -315,7 +315,7 @@ public void IptcProfile_AddRepeatable_Works(IptcTag tag) public void IptcProfile_AddNoneRepeatable_DoesOverrideOldValue(IptcTag tag) { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); const string expectedValue = "another one"; profile.SetValue(tag, "test", false); @@ -323,7 +323,7 @@ public void IptcProfile_AddNoneRepeatable_DoesOverrideOldValue(IptcTag tag) profile.SetValue(tag, expectedValue, false); // assert - var values = profile.Values.ToList(); + List values = profile.Values.ToList(); Assert.Equal(1, values.Count); ContainsIptcValue(values, tag, expectedValue); } @@ -332,7 +332,7 @@ public void IptcProfile_AddNoneRepeatable_DoesOverrideOldValue(IptcTag tag) public void IptcProfile_RemoveByTag_RemovesAllEntrys() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test2"); @@ -348,7 +348,7 @@ public void IptcProfile_RemoveByTag_RemovesAllEntrys() public void IptcProfile_RemoveByTagAndValue_Works() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test2"); @@ -364,7 +364,7 @@ public void IptcProfile_RemoveByTagAndValue_Works() public void IptcProfile_GetValue_RetrievesAllEntries() { // arrange - var profile = new IptcProfile(); + IptcProfile profile = new(); profile.SetValue(IptcTag.Byline, "test"); profile.SetValue(IptcTag.Byline, "test2"); profile.SetValue(IptcTag.Caption, "test"); @@ -385,7 +385,7 @@ private static void ContainsIptcValue(List values, IptcTag tag, strin private static Image WriteAndReadJpeg(Image image) { - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { image.SaveAsJpeg(memStream); image.Dispose(); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs index 5dc6ac6db7..32d4bc7cd1 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs @@ -78,6 +78,34 @@ public async Task ReadXmpMetadata_FromWebp_Works(TestImageProvider(1, 1); + using Image image = new(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; - var encoder = new GifEncoder(); + GifEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -129,10 +157,10 @@ public void WritingGif_PreservesXmpProfile() public void WritingJpeg_PreservesXmpProfile() { // arrange - using var image = new Image(1, 1); + using Image image = new(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; - var encoder = new JpegEncoder(); + JpegEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -147,10 +175,10 @@ public void WritingJpeg_PreservesXmpProfile() public async Task WritingJpeg_PreservesExtendedXmpProfile() { // arrange - var provider = TestImageProvider.File(TestImages.Jpeg.Baseline.ExtendedXmp); + TestImageProvider provider = TestImageProvider.File(TestImages.Jpeg.Baseline.ExtendedXmp); using Image image = await provider.GetImageAsync(JpegDecoder.Instance); XmpProfile original = image.Metadata.XmpProfile; - var encoder = new JpegEncoder(); + JpegEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -165,10 +193,10 @@ public async Task WritingJpeg_PreservesExtendedXmpProfile() public void WritingPng_PreservesXmpProfile() { // arrange - using var image = new Image(1, 1); + using Image image = new(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; - var encoder = new PngEncoder(); + PngEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -183,10 +211,10 @@ public void WritingPng_PreservesXmpProfile() public void WritingTiff_PreservesXmpProfile() { // arrange - using var image = new Image(1, 1); + using Image image = new(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Frames.RootFrame.Metadata.XmpProfile = original; - var encoder = new TiffEncoder(); + TiffEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -201,10 +229,10 @@ public void WritingTiff_PreservesXmpProfile() public void WritingWebp_PreservesXmpProfile() { // arrange - using var image = new Image(1, 1); + using Image image = new(1, 1); XmpProfile original = CreateMinimalXmlProfile(); image.Metadata.XmpProfile = original; - var encoder = new WebpEncoder(); + WebpEncoder encoder = new(); // act using Image reloadedImage = WriteAndRead(image, encoder); @@ -218,7 +246,7 @@ public void WritingWebp_PreservesXmpProfile() private static void XmpProfileContainsExpectedValues(XmpProfile xmp) { Assert.NotNull(xmp); - XDocument document = xmp.GetDocument(); + XDocument document = xmp.ToXDocument(); Assert.NotNull(document); Assert.Equal("xmpmeta", document.Root.Name.LocalName); Assert.Equal("adobe:ns:meta/", document.Root.Name.NamespaceName); @@ -228,13 +256,15 @@ private static XmpProfile CreateMinimalXmlProfile() { string content = $" "; byte[] data = Encoding.UTF8.GetBytes(content); - var profile = new XmpProfile(data); + XmpProfile profile = new(data); return profile; } + private static XDocument CreateMinimalXDocument() => CreateMinimalXmlProfile().ToXDocument(); + private static Image WriteAndRead(Image image, IImageEncoder encoder) { - using (var memStream = new MemoryStream()) + using (MemoryStream memStream = new()) { image.Save(memStream, encoder); image.Dispose(); diff --git a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs index 04183571b5..f194484c4a 100644 --- a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs @@ -14,15 +14,15 @@ public class SignedRationalTests [Fact] public void AreEqual() { - var r1 = new SignedRational(3, 2); - var r2 = new SignedRational(3, 2); + SignedRational r1 = new(3, 2); + SignedRational r2 = new(3, 2); Assert.Equal(r1, r2); Assert.True(r1 == r2); - var r3 = new SignedRational(7.55); - var r4 = new SignedRational(755, 100); - var r5 = new SignedRational(151, 20); + SignedRational r3 = new(7.55); + SignedRational r4 = new(755, 100); + SignedRational r5 = new(151, 20); Assert.Equal(r3, r4); Assert.Equal(r4, r5); @@ -34,8 +34,8 @@ public void AreEqual() [Fact] public void AreNotEqual() { - var first = new SignedRational(0, 100); - var second = new SignedRational(100, 100); + SignedRational first = new(0, 100); + SignedRational second = new(100, 100); Assert.NotEqual(first, second); Assert.True(first != second); @@ -47,7 +47,7 @@ public void AreNotEqual() [Fact] public void ConstructorAssignsProperties() { - var rational = new SignedRational(7, -55); + SignedRational rational = new(7, -55); Assert.Equal(7, rational.Numerator); Assert.Equal(-55, rational.Denominator); @@ -75,15 +75,15 @@ public void ConstructorAssignsProperties() [Fact] public void Fraction() { - var first = new SignedRational(1.0 / 1600); - var second = new SignedRational(1.0 / 1600, true); + SignedRational first = new(1.0 / 1600); + SignedRational second = new(1.0 / 1600, true); Assert.False(first.Equals(second)); } [Fact] public void ToDouble() { - var rational = new SignedRational(0, 0); + SignedRational rational = new(0, 0); Assert.Equal(double.NaN, rational.ToDouble()); rational = new SignedRational(2, 0); @@ -96,7 +96,7 @@ public void ToDouble() [Fact] public void ToStringRepresentation() { - var rational = new SignedRational(0, 0); + SignedRational rational = new(0, 0); Assert.Equal("[ Indeterminate ]", rational.ToString()); rational = new SignedRational(double.PositiveInfinity); diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index 95574a4d6c..4b4e84b4b3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -28,8 +29,8 @@ public void A8_Constructor() [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); @@ -40,12 +41,11 @@ public void A8_Equality() 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 @@ -56,7 +56,7 @@ public void A8_FromScaledVector4() public void A8_ToScaledVector4() { // Arrange - var alpha = new A8(.5F); + A8 alpha = new(.5F); // Act Vector4 actual = alpha.ToScaledVector4(); @@ -72,10 +72,10 @@ public void A8_ToScaledVector4() 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,11 +87,10 @@ public void A8_ToVector4() [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); + Rgba32 actual = input.ToRgba32(); Assert.Equal(expected, actual); } @@ -99,13 +98,27 @@ public void A8_ToRgba32() public void A8_FromBgra5551() { // arrange - var alpha = default(A8); - 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); } + + [Fact] + public void A8_PixelInformation() + { + 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); + 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 13c84784cc..98fdce5dbd 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,8 +16,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 +28,8 @@ public void AreEqual() [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 +47,7 @@ public void AreNotEqual() [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 +58,7 @@ public void Constructor(byte b, byte g, byte r, byte a) [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 +71,8 @@ public unsafe void ByteLayoutIsSequentialBgra() [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 +86,8 @@ public void Equality_WhenTrue(byte r, byte g, byte b, byte a) [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,8 +96,7 @@ public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte [Fact] public void FromRgba32() { - var abgr = default(Abgr32); - 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); @@ -104,7 +104,7 @@ public void FromRgba32() 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,8 +113,7 @@ public void FromRgba32() [Fact] public void FromVector4() { - var c = default(Abgr32); - 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); @@ -125,7 +124,7 @@ public void FromVector4() [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,13 +133,30 @@ public void ToVector4() public void Abgr32_FromBgra5551() { // arrange - var abgr = default(Abgr32); - 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); } + + [Fact] + public void Abgr32_PixelInformation() + { + 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); + 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 70012afb02..bcaf9265a3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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(1f, 0.0f, 1f, 1f)); + Argb32 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +31,10 @@ public void AreEqual() [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(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); @@ -45,25 +46,25 @@ public void AreNotEqual() [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 +94,7 @@ public void Argb32_ToVector4() public void Argb32_ToScaledVector4() { // arrange - var argb = new Argb32(Vector4.One); + Argb32 argb = new(Vector4.One); // act Vector4 actual = argb.ToScaledVector4(); @@ -110,11 +111,10 @@ public void Argb32_FromScaledVector4() { // arrange Vector4 scaled = new Argb32(Vector4.One).ToScaledVector4(); - var pixel = default(Argb32); - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - pixel.FromScaledVector4(scaled); + Argb32 pixel = Argb32.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -125,11 +125,10 @@ public void Argb32_FromScaledVector4() public void Argb32_FromBgra5551() { // arrange - var argb = default(Argb32); - 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); @@ -141,4 +140,22 @@ public void Argb32_Clamping() 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(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Alpha | PixelColorType.RGB, info.ColorType); + + 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/AssociatedAlphaPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs new file mode 100644 index 0000000000..c115850fc0 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/AssociatedAlphaPixelTests.cs @@ -0,0 +1,1364 @@ +// 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; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +/// +/// Verifies the shared contract for pixel formats that store associated alpha. +/// +/// The associated-alpha pixel format. +[Trait("Category", "PixelFormats")] +public abstract class AssociatedAlphaPixelTests + where TPixel : unmanaged, IPixel +{ + /// + /// Gets the color channels described by the pixel format. + /// + protected virtual PixelColorType ExpectedColorType => PixelColorType.RGB | PixelColorType.Alpha; + + [Fact] + public void PixelInformationDescribesAssociatedAlpha() + { + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + int expectedComponentPrecision = (Unsafe.SizeOf() * 8) / 4; + + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.Associated, info.AlphaRepresentation); + Assert.Equal(this.ExpectedColorType, info.ColorType); + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + + for (int i = 0; i < componentInfo.ComponentCount; i++) + { + Assert.Equal(expectedComponentPrecision, componentInfo.GetComponentPrecision(i)); + } + } +} + +/// +/// Tests the pixel format. +/// +public class Rgba32PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void ByteLayoutAndPackedValue() + { + Rgba32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 1, 2, 3, 4 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x04030201U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Bgra32PTests : AssociatedAlphaPixelTests +{ + /// + protected override PixelColorType ExpectedColorType => PixelColorType.BGR | PixelColorType.Alpha; + + [Fact] + public void ByteLayoutAndPackedValue() + { + Bgra32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 3, 2, 1, 4 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x04010203U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Argb32PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void ByteLayoutAndPackedValue() + { + Argb32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 4, 1, 2, 3 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x03020104U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class Abgr32PTests : AssociatedAlphaPixelTests +{ + /// + protected override PixelColorType ExpectedColorType => PixelColorType.BGR | PixelColorType.Alpha; + + [Fact] + public void ByteLayoutAndPackedValue() + { + Abgr32P[] pixels = [new(1, 2, 3, 4)]; + + Assert.Equal(new byte[] { 4, 3, 2, 1 }, MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray()); + Assert.Equal(0x01020304U, pixels[0].PackedValue); + } +} + +/// +/// Tests the pixel format. +/// +public class NormalizedByte4PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void PackedValueMatchesNormalizedByte4ForAssociatedVector() + { + Vector4 associated = new(.1F, -.3F, .5F, -.7F); + + Assert.Equal(new NormalizedByte4(associated).PackedValue, new NormalizedByte4P(associated).PackedValue); + } + + [Fact] + public void MinimumStorageCodeDecodesAsNegativeOne() + { + NormalizedByte4P pixel = new() { PackedValue = 0x80808080 }; + + Assert.Equal(-Vector4.One, pixel.ToVector4()); + Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); + Assert.Equal(-Vector4.One, pixel.ToUnassociatedVector4()); + Assert.Equal(Vector4.Zero, pixel.ToUnassociatedScaledVector4()); + } + + [Fact] + public void AssociatedScaledVectorsMatchUnassociatedVectorsWithinTwoUlpsForEveryValidComponentAndAlpha() + { + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + uint component = (byte)(associated - 127); + uint alphaComponent = (byte)(alpha - 127); + NormalizedByte4P pixel = new() { PackedValue = component | (component << 8) | (component << 16) | (alphaComponent << 24) }; + Vector4 expected = pixel.ToUnassociatedScaledVector4(); + expected.X *= expected.W; + expected.Y *= expected.W; + expected.Z *= expected.W; + Vector4 actual = pixel.ToAssociatedScaledVector4(); + + // All 32,640 valid storage pairs are covered. Two ULP is the measured maximum from the independently ordered division and multiplication. + AlphaRepresentationTestAssertions.EqualWithinTwoUlps(expected, actual); + Assert.Equal(BitConverter.SingleToInt32Bits(expected.W), BitConverter.SingleToInt32Bits(actual.W)); + } + } + } + + [Fact] + public void BulkConversionsMatchScalarAcrossHardwareWidths() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertBulkConversionsMatchScalar, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertBulkConversionsMatchScalar() + { + // Values immediately below, at, and above each vector width force every hardware regime through its vector body and scalar tail. + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + foreach (int length in lengths) + { + NormalizedByte4P[] pixels = new NormalizedByte4P[length]; + Vector4[] expectedNative = new Vector4[length]; + Vector4[] expectedAssociatedNative = new Vector4[length]; + Vector4[] expectedAssociatedScaled = new Vector4[length]; + Vector4[] expectedUnassociatedNative = new Vector4[length]; + Vector4[] expectedUnassociatedScaled = new Vector4[length]; + Vector4[] actualNative = new Vector4[length]; + Vector4[] actualAssociatedNative = new Vector4[length]; + Vector4[] actualAssociatedScaled = new Vector4[length]; + Vector4[] actualUnassociatedNative = new Vector4[length]; + Vector4[] actualUnassociatedScaled = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + pixels[i].PackedValue = (uint)((byte)(i * 37 + 128) | ((byte)(i * 73 + 64) << 8) | ((byte)(i * 109 + 192) << 16) | ((byte)(i * 151 + 128) << 24)); + expectedNative[i] = pixels[i].ToVector4(); + expectedAssociatedNative[i] = pixels[i].ToAssociatedVector4(); + expectedAssociatedScaled[i] = pixels[i].ToAssociatedScaledVector4(); + expectedUnassociatedNative[i] = pixels[i].ToUnassociatedVector4(); + expectedUnassociatedScaled[i] = pixels[i].ToUnassociatedScaledVector4(); + } + + if (length > 0) + { + // PackedValue permits every signed byte pattern. Explicit -128 components ensure bulk sign extension matches scalar decoding even though the packer emits no -128 values. + pixels[0].PackedValue = 0x80808080; + expectedNative[0] = pixels[0].ToVector4(); + expectedAssociatedNative[0] = pixels[0].ToAssociatedVector4(); + expectedAssociatedScaled[0] = pixels[0].ToAssociatedScaledVector4(); + expectedUnassociatedNative[0] = pixels[0].ToUnassociatedVector4(); + expectedUnassociatedScaled[0] = pixels[0].ToUnassociatedScaledVector4(); + } + + operations.ToVector4(Configuration.Default, pixels, actualNative, PixelConversionModifiers.None); + operations.ToVector4(Configuration.Default, pixels, actualAssociatedNative, PixelConversionModifiers.Premultiply); + operations.ToVector4(Configuration.Default, pixels, actualAssociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + operations.ToVector4(Configuration.Default, pixels, actualUnassociatedNative, PixelConversionModifiers.UnPremultiply); + operations.ToVector4(Configuration.Default, pixels, actualUnassociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Assert.Equal(expectedNative, actualNative); + Assert.Equal(expectedAssociatedNative, actualAssociatedNative); + Assert.Equal(expectedAssociatedScaled, actualAssociatedScaled); + Assert.Equal(expectedUnassociatedNative, actualUnassociatedNative); + Assert.Equal(expectedUnassociatedScaled, actualUnassociatedScaled); + + Vector4[] unassociatedScaled = new Vector4[length]; + Vector4[] associatedScaled = new Vector4[length]; + Vector4[] unassociatedNative = new Vector4[length]; + Vector4[] associatedNative = new Vector4[length]; + NormalizedByte4P[] expectedFromUnassociatedNative = new NormalizedByte4P[length]; + NormalizedByte4P[] expectedFromAssociatedNative = new NormalizedByte4P[length]; + NormalizedByte4P[] expectedFromUnassociatedScaled = new NormalizedByte4P[length]; + NormalizedByte4P[] expectedFromAssociatedScaled = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromUnassociatedNative = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromAssociatedNative = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromUnassociatedScaled = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromAssociatedScaled = new NormalizedByte4P[length]; + + for (int i = 0; i < length; i++) + { + float alpha = ((i * 151) % 1021) / 1020F; + unassociatedScaled[i] = new Vector4(((i * 37) % 1021) / 1020F, ((i * 73) % 1021) / 1020F, ((i * 109) % 1021) / 1020F, alpha); + associatedScaled[i] = new Vector4(unassociatedScaled[i].X * alpha, unassociatedScaled[i].Y * alpha, unassociatedScaled[i].Z * alpha, alpha); + + // Signed-native formats encode the common scaled domain over [-1, 1], so native inputs must be derived before invoking the native entry points. + unassociatedNative[i] = (unassociatedScaled[i] * 2F) - Vector4.One; + associatedNative[i] = (associatedScaled[i] * 2F) - Vector4.One; + expectedFromUnassociatedNative[i] = NormalizedByte4P.FromUnassociatedVector4(unassociatedNative[i]); + expectedFromAssociatedNative[i] = NormalizedByte4P.FromAssociatedVector4(associatedNative[i]); + expectedFromUnassociatedScaled[i] = NormalizedByte4P.FromUnassociatedScaledVector4(unassociatedScaled[i]); + expectedFromAssociatedScaled[i] = NormalizedByte4P.FromAssociatedScaledVector4(associatedScaled[i]); + } + + Vector4[] unassociatedNativeSource = [.. unassociatedNative]; + Vector4[] associatedNativeSource = [.. associatedNative]; + Vector4[] unassociatedScaledSource = [.. unassociatedScaled]; + Vector4[] associatedScaledSource = [.. associatedScaled]; + + operations.FromVector4Destructive(Configuration.Default, unassociatedNativeSource, actualFromUnassociatedNative, PixelConversionModifiers.UnPremultiply); + operations.FromVector4Destructive(Configuration.Default, associatedNativeSource, actualFromAssociatedNative, PixelConversionModifiers.Premultiply); + operations.FromVector4Destructive(Configuration.Default, unassociatedScaledSource, actualFromUnassociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + operations.FromVector4Destructive(Configuration.Default, associatedScaledSource, actualFromAssociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + + Assert.Equal(expectedFromUnassociatedNative, actualFromUnassociatedNative); + Assert.Equal(expectedFromAssociatedNative, actualFromAssociatedNative); + Assert.Equal(expectedFromUnassociatedScaled, actualFromUnassociatedScaled); + Assert.Equal(expectedFromAssociatedScaled, actualFromAssociatedScaled); + + NormalizedByte4P[] expectedFromNative = new NormalizedByte4P[length]; + NormalizedByte4P[] expectedFromScaled = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromNative = new NormalizedByte4P[length]; + NormalizedByte4P[] actualFromScaled = new NormalizedByte4P[length]; + + for (int i = 0; i < length; i++) + { + expectedFromNative[i] = NormalizedByte4P.FromVector4(expectedNative[i]); + expectedFromScaled[i] = NormalizedByte4P.FromScaledVector4(expectedAssociatedScaled[i]); + } + + Vector4[] actualNativeSource = [.. expectedNative]; + Vector4[] actualScaledSource = [.. expectedAssociatedScaled]; + + operations.FromVector4Destructive(Configuration.Default, actualNativeSource, actualFromNative, PixelConversionModifiers.None); + operations.FromVector4Destructive(Configuration.Default, actualScaledSource, actualFromScaled, PixelConversionModifiers.Scale); + + Assert.Equal(expectedFromNative, actualFromNative); + Assert.Equal(expectedFromScaled, actualFromScaled); + } + } +} + +/// +/// Tests the pixel format. +/// +public class HalfVector4PTests : AssociatedAlphaPixelTests +{ + [Fact] + public void PackedValueMatchesHalfVector4ForAssociatedVector() + { + Vector4 associated = new(.1F, -.3F, .5F, -.7F); + + Assert.Equal(new HalfVector4(associated).PackedValue, new HalfVector4P(associated).PackedValue); + } + + [Fact] + public void ColorRoundTripDoesNotIntroduceAdditionalAssociationLoss() + { + HalfVector4P source = HalfVector4P.FromAssociatedScaledVector4(new Vector4(.125F, .25F, .375F, .5F)); + HalfVector4P expected = HalfVector4P.FromScaledVector4(source.ToScaledVector4()); + Color color = Color.FromPixel(source); + Color[] bulkColors = new Color[1]; + + Color.FromPixel(new[] { source }, bulkColors); + + Assert.Equal(source.ToScaledVector4(), color.ToScaledVector4()); + Assert.Equal(expected, color.ToPixel()); + Assert.Equal(source.ToScaledVector4(), bulkColors[0].ToScaledVector4()); + Assert.Equal(expected, bulkColors[0].ToPixel()); + } + + [Fact] + public void BulkConversionsMatchScalarAcrossHardwareWidths() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertBulkConversionsMatchScalar, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertBulkConversionsMatchScalar() + { + AssertEveryHalfBitPatternUnpacksExactly(); + AssertHalfPackingMatchesScalar(); + AssertAlphaConversionsMatchScalar(); + } + + private static void AssertEveryHalfBitPatternUnpacksExactly() + { + HalfVector4P[] pixels = new HalfVector4P[(ushort.MaxValue + 1) / 4]; + Vector4[] expectedNative = new Vector4[pixels.Length]; + Vector4[] expectedScaled = new Vector4[pixels.Length]; + Vector4[] actualNative = new Vector4[pixels.Length]; + Vector4[] actualScaled = new Vector4[pixels.Length]; + + for (int i = 0; i < pixels.Length; i++) + { + ulong x = (ushort)(i * 4); + ulong y = (ushort)((i * 4) + 1); + ulong z = (ushort)((i * 4) + 2); + ulong w = (ushort)((i * 4) + 3); + pixels[i].PackedValue = x | (y << 16) | (z << 32) | (w << 48); + expectedNative[i] = pixels[i].ToVector4(); + expectedScaled[i] = pixels[i].ToScaledVector4(); + } + + PixelOperations.Instance.ToVector4(Configuration.Default, pixels, actualNative, PixelConversionModifiers.None); + PixelOperations.Instance.ToVector4(Configuration.Default, pixels, actualScaled, PixelConversionModifiers.Scale); + + AssertVectorBitsEqual(expectedNative, actualNative); + AssertVectorBitsEqual(expectedScaled, actualScaled); + } + + private static void AssertHalfPackingMatchesScalar() + { + const int finitePositiveHalfCount = 0x7BFF; + const int distributedFloatCount = ushort.MaxValue + 1; + const int componentCount = (ushort.MaxValue + 1) + (finitePositiveHalfCount * 2) + distributedFloatCount + 2; + Vector4[] source = new Vector4[componentCount / 4]; + Span components = MemoryMarshal.Cast(source.AsSpan()); + int index = 0; + + for (int bits = 0; bits <= ushort.MaxValue; bits++) + { + components[index++] = (float)BitConverter.UInt16BitsToHalf((ushort)bits); + } + + for (int bits = 0; bits < finitePositiveHalfCount; bits++) + { + float lower = (float)BitConverter.UInt16BitsToHalf((ushort)bits); + float upper = (float)BitConverter.UInt16BitsToHalf((ushort)(bits + 1)); + + // Every midpoint exercises binary16 round-to-nearest-even; negating it covers the symmetric sign path. + float midpoint = (lower + upper) * .5F; + components[index++] = midpoint; + components[index++] = -midpoint; + } + + // Equal high and low words distribute samples across the complete binary32 sign, exponent, and fraction space. + for (uint bits = 0; bits <= ushort.MaxValue; bits++) + { + components[index++] = BitConverter.UInt32BitsToSingle(bits * 0x0001_0001U); + } + + // The two padding components also verify finite overflow in both directions. + components[index++] = float.MaxValue; + components[index] = float.MinValue; + + HalfVector4P[] expected = new HalfVector4P[source.Length]; + HalfVector4P[] actual = new HalfVector4P[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + expected[i] = HalfVector4P.FromVector4(source[i]); + } + + Vector4[] destructiveSource = [.. source]; + + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actual, PixelConversionModifiers.None); + + for (int i = 0; i < expected.Length; i++) + { + for (int component = 0; component < 4; component++) + { + int shift = component * 16; + ushort expectedBits = (ushort)(expected[i].PackedValue >> shift); + ushort actualBits = (ushort)(actual[i].PackedValue >> shift); + + // Association performs floating-point arithmetic before packing. IEEE 754 requires a NaN result but does not specify which operand's payload survives that arithmetic. + if (Half.IsNaN(BitConverter.UInt16BitsToHalf(expectedBits))) + { + Assert.True(Half.IsNaN(BitConverter.UInt16BitsToHalf(actualBits)), $"Pixel {i}, component {component} should be NaN."); + } + else + { + Assert.Equal(expectedBits, actualBits); + } + } + } + } + + private static void AssertAlphaConversionsMatchScalar() + { + // Values immediately below, at, and above each vector width force every hardware regime through its vector body and scalar tail. + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + foreach (int length in lengths) + { + Vector4[] unassociatedScaled = new Vector4[length]; + Vector4[] associatedScaled = new Vector4[length]; + Vector4[] unassociatedNative = new Vector4[length]; + Vector4[] associatedNative = new Vector4[length]; + HalfVector4P[] expectedFromUnassociatedNative = new HalfVector4P[length]; + HalfVector4P[] expectedFromAssociatedNative = new HalfVector4P[length]; + HalfVector4P[] expectedFromUnassociatedScaled = new HalfVector4P[length]; + HalfVector4P[] expectedFromAssociatedScaled = new HalfVector4P[length]; + HalfVector4P[] actualFromUnassociatedNative = new HalfVector4P[length]; + HalfVector4P[] actualFromAssociatedNative = new HalfVector4P[length]; + HalfVector4P[] actualFromUnassociatedScaled = new HalfVector4P[length]; + HalfVector4P[] actualFromAssociatedScaled = new HalfVector4P[length]; + + for (int i = 0; i < length; i++) + { + float alpha = ((i * 151) % 4093) / 4092F; + unassociatedScaled[i] = new Vector4(((i * 37) % 4093) / 4092F, ((i * 73) % 4093) / 4092F, ((i * 109) % 4093) / 4092F, alpha); + associatedScaled[i] = new Vector4(unassociatedScaled[i].X * alpha, unassociatedScaled[i].Y * alpha, unassociatedScaled[i].Z * alpha, alpha); + + unassociatedNative[i] = (unassociatedScaled[i] * 131008F) - new Vector4(65504F); + associatedNative[i] = (associatedScaled[i] * 131008F) - new Vector4(65504F); + expectedFromUnassociatedNative[i] = HalfVector4P.FromUnassociatedVector4(unassociatedNative[i]); + expectedFromAssociatedNative[i] = HalfVector4P.FromAssociatedVector4(associatedNative[i]); + expectedFromUnassociatedScaled[i] = HalfVector4P.FromUnassociatedScaledVector4(unassociatedScaled[i]); + expectedFromAssociatedScaled[i] = HalfVector4P.FromAssociatedScaledVector4(associatedScaled[i]); + } + + Vector4[] unassociatedNativeSource = [.. unassociatedNative]; + Vector4[] associatedNativeSource = [.. associatedNative]; + Vector4[] unassociatedScaledSource = [.. unassociatedScaled]; + Vector4[] associatedScaledSource = [.. associatedScaled]; + + operations.FromVector4Destructive(Configuration.Default, unassociatedNativeSource, actualFromUnassociatedNative, PixelConversionModifiers.UnPremultiply); + operations.FromVector4Destructive(Configuration.Default, associatedNativeSource, actualFromAssociatedNative, PixelConversionModifiers.Premultiply); + operations.FromVector4Destructive(Configuration.Default, unassociatedScaledSource, actualFromUnassociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + operations.FromVector4Destructive(Configuration.Default, associatedScaledSource, actualFromAssociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + + Assert.Equal(expectedFromUnassociatedNative, actualFromUnassociatedNative); + Assert.Equal(expectedFromAssociatedNative, actualFromAssociatedNative); + Assert.Equal(expectedFromUnassociatedScaled, actualFromUnassociatedScaled); + Assert.Equal(expectedFromAssociatedScaled, actualFromAssociatedScaled); + + Vector4[] expectedAssociatedNative = new Vector4[length]; + Vector4[] expectedAssociatedScaled = new Vector4[length]; + Vector4[] expectedUnassociatedNative = new Vector4[length]; + Vector4[] expectedUnassociatedScaled = new Vector4[length]; + Vector4[] actualAssociatedNative = new Vector4[length]; + Vector4[] actualAssociatedScaled = new Vector4[length]; + Vector4[] actualUnassociatedNative = new Vector4[length]; + Vector4[] actualUnassociatedScaled = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + expectedAssociatedNative[i] = expectedFromAssociatedScaled[i].ToAssociatedVector4(); + expectedAssociatedScaled[i] = expectedFromAssociatedScaled[i].ToAssociatedScaledVector4(); + expectedUnassociatedNative[i] = expectedFromAssociatedScaled[i].ToUnassociatedVector4(); + expectedUnassociatedScaled[i] = expectedFromAssociatedScaled[i].ToUnassociatedScaledVector4(); + } + + operations.ToVector4(Configuration.Default, expectedFromAssociatedScaled, actualAssociatedNative, PixelConversionModifiers.Premultiply); + operations.ToVector4(Configuration.Default, expectedFromAssociatedScaled, actualAssociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + operations.ToVector4(Configuration.Default, expectedFromAssociatedScaled, actualUnassociatedNative, PixelConversionModifiers.UnPremultiply); + operations.ToVector4(Configuration.Default, expectedFromAssociatedScaled, actualUnassociatedScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + AssertVectorBitsEqual(expectedAssociatedNative, actualAssociatedNative); + AssertVectorBitsEqual(expectedAssociatedScaled, actualAssociatedScaled); + AssertVectorBitsEqual(expectedUnassociatedNative, actualUnassociatedNative); + AssertVectorBitsEqual(expectedUnassociatedScaled, actualUnassociatedScaled); + } + } + + private static void AssertVectorBitsEqual(ReadOnlySpan expected, ReadOnlySpan actual) + { + ReadOnlySpan expectedBits = MemoryMarshal.Cast(expected); + ReadOnlySpan actualBits = MemoryMarshal.Cast(actual); + + Assert.Equal(expectedBits.ToArray(), actualBits.ToArray()); + } +} + +/// +/// Tests conversion between associated-alpha packed byte layouts. +/// +public class AssociatedAlphaPackedPixelConversionTests +{ + [Fact] + public void Rgba32PToBgra32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PToArgb32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PToAbgr32PRoundTripIsLossless() => AssertLosslessRoundTrip(); + + [Fact] + public void Rgba32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Rgba32P(r, g, b, a)); + + [Fact] + public void Bgra32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Bgra32P(r, g, b, a)); + + [Fact] + public void Argb32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Argb32P(r, g, b, a)); + + [Fact] + public void Abgr32PScalarAndBulkAssociatedVectorsAreEqual() => AssertScalarAndBulkAssociatedVectorsAreEqual((r, g, b, a) => new Abgr32P(r, g, b, a)); + + [Fact] + public void Rgba32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Bgra32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Argb32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Abgr32PScalarAndBulkFromAssociatedVectorsAreEqual() => AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + + [Fact] + public void Rgba32PScalarAndBulkUnassociatedVectorsAreEqual() => AssertScalarAndBulkUnassociatedVectorsAreEqual((r, g, b, a) => new Rgba32P(r, g, b, a)); + + [Fact] + public void Bgra32PScalarAndBulkUnassociatedVectorsAreEqual() => AssertScalarAndBulkUnassociatedVectorsAreEqual((r, g, b, a) => new Bgra32P(r, g, b, a)); + + [Fact] + public void Argb32PScalarAndBulkUnassociatedVectorsAreEqual() => AssertScalarAndBulkUnassociatedVectorsAreEqual((r, g, b, a) => new Argb32P(r, g, b, a)); + + [Fact] + public void Abgr32PScalarAndBulkUnassociatedVectorsAreEqual() => AssertScalarAndBulkUnassociatedVectorsAreEqual((r, g, b, a) => new Abgr32P(r, g, b, a)); + + [Fact] + public void Rgba32PScalarAndBulkFromUnassociatedVectorsAreEqual() => AssertScalarAndBulkFromUnassociatedVectorsAreEqual(); + + [Fact] + public void Bgra32PScalarAndBulkFromUnassociatedVectorsAreEqual() => AssertScalarAndBulkFromUnassociatedVectorsAreEqual(); + + [Fact] + public void Argb32PScalarAndBulkFromUnassociatedVectorsAreEqual() => AssertScalarAndBulkFromUnassociatedVectorsAreEqual(); + + [Fact] + public void Abgr32PScalarAndBulkFromUnassociatedVectorsAreEqual() => AssertScalarAndBulkFromUnassociatedVectorsAreEqual(); + + [Fact] + public void BulkConversionsMatchScalarAcrossHardwareWidths() + { + // RemoteExecutor resolves the delegate by method name alone, so its entry point must not share the generic worker's name. + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertPackedBulkConversionsMatchScalar, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + } + + private static void AssertPackedBulkConversionsMatchScalar() + { + AssertBulkConversionsMatchScalar((r, g, b, a) => new Rgba32P(r, g, b, a)); + AssertBulkConversionsMatchScalar((r, g, b, a) => new Bgra32P(r, g, b, a)); + AssertBulkConversionsMatchScalar((r, g, b, a) => new Argb32P(r, g, b, a)); + AssertBulkConversionsMatchScalar((r, g, b, a) => new Abgr32P(r, g, b, a)); + } + + private static void AssertBulkConversionsMatchScalar(Func createPixel) + where TPixel : unmanaged, IPixel + { + AssertScalarAndBulkAssociatedVectorsAreEqual(createPixel); + AssertScalarAndBulkFromAssociatedVectorsAreEqual(); + AssertScalarAndBulkUnassociatedVectorsAreEqual(createPixel); + AssertScalarAndBulkFromUnassociatedVectorsAreEqual(); + } + + private static void AssertLosslessRoundTrip() + where TIntermediate : unmanaged, IPixel + { + const int componentAlphaPairCount = ((byte.MaxValue + 1) * (byte.MaxValue + 2)) / 2; + const int colorChannelCount = 3; + Rgba32P[] expected = new Rgba32P[componentAlphaPairCount * colorChannelCount]; + TIntermediate[] intermediate = new TIntermediate[expected.Length]; + Rgba32P[] actual = new Rgba32P[expected.Length]; + int index = 0; + + // Every component/alpha pair in the valid associated-byte domain must survive a layout + // conversion exactly. Exercising each color lane also catches an incorrect channel map. + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + for (int component = 0; component <= alpha; component++) + { + expected[index++] = new Rgba32P((byte)component, 0, 0, (byte)alpha); + expected[index++] = new Rgba32P(0, (byte)component, 0, (byte)alpha); + expected[index++] = new Rgba32P(0, 0, (byte)component, (byte)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, expected, intermediate); + PixelOperations.Instance.From(Configuration.Default, intermediate, actual); + + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i].ToScaledVector4(), intermediate[i].ToScaledVector4()); + } + + Assert.Equal(expected, actual); + } + + private static void AssertScalarAndBulkAssociatedVectorsAreEqual(Func createPixel) + where TPixel : unmanaged, IPixel + { + // Prefixes straddle every vector width; the final length also preserves exhaustive byte-component coverage and adds a scalar tail. + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259, byte.MaxValue * 4 + 5]; + TPixel[] source = new TPixel[lengths[^1]]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + for (int component = 0; component <= byte.MaxValue; component++) + { + int index = component * 4; + source[index] = createPixel((byte)component, 0, 0, byte.MaxValue); + source[index + 1] = createPixel(0, (byte)component, 0, byte.MaxValue); + source[index + 2] = createPixel(0, 0, (byte)component, byte.MaxValue); + source[index + 3] = createPixel(0, 0, 0, (byte)component); + } + + source[^1] = createPixel(37, 73, 109, 151); + + foreach (int length in lengths) + { + ReadOnlySpan pixels = source.AsSpan(0, length); + Vector4[] expectedNative = new Vector4[length]; + Vector4[] expectedScaled = new Vector4[length]; + Vector4[] actualNative = new Vector4[length]; + Vector4[] actualScaled = new Vector4[length]; + + for (int i = 0; i < pixels.Length; i++) + { + expectedNative[i] = pixels[i].ToAssociatedVector4(); + expectedScaled[i] = pixels[i].ToAssociatedScaledVector4(); + } + + operations.ToVector4(Configuration.Default, pixels, actualNative, PixelConversionModifiers.Premultiply); + operations.ToVector4(Configuration.Default, pixels, actualScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + + Assert.Equal(expectedNative, actualNative); + Assert.Equal(expectedScaled, actualScaled); + } + } + + private static void AssertScalarAndBulkFromAssociatedVectorsAreEqual() + where TPixel : unmanaged, IPixel + { + // Values immediately below, at, and above each vector width force every hardware regime through its vector body and scalar tail. + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + Vector4[] source = new Vector4[lengths[^1]]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + for (int i = 0; i < source.Length; i++) + { + // Alternating exact byte alpha values with fractional values covers both reassociation branches. + float alpha = (i & 1) == 0 ? (i % 256) / 255F : ((i % 255) + .375F) / 255F; + source[i] = new Vector4(((i * 37) % 256) / 255F, ((i * 73) % 256) / 255F, ((i * 109) % 256) / 255F, 1F) * alpha; + source[i].W = alpha; + } + + foreach (int length in lengths) + { + Vector4[] nativeSource = source.AsSpan(0, length).ToArray(); + Vector4[] scaledSource = source.AsSpan(0, length).ToArray(); + TPixel[] expectedNative = new TPixel[length]; + TPixel[] expectedScaled = new TPixel[length]; + TPixel[] actualNative = new TPixel[length]; + TPixel[] actualScaled = new TPixel[length]; + + for (int i = 0; i < length; i++) + { + expectedNative[i] = TPixel.FromAssociatedVector4(nativeSource[i]); + expectedScaled[i] = TPixel.FromAssociatedScaledVector4(scaledSource[i]); + } + + operations.FromVector4Destructive(Configuration.Default, nativeSource, actualNative, PixelConversionModifiers.Premultiply); + operations.FromVector4Destructive(Configuration.Default, scaledSource, actualScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + + Assert.Equal(expectedNative, actualNative); + Assert.Equal(expectedScaled, actualScaled); + } + } + + private static void AssertScalarAndBulkUnassociatedVectorsAreEqual(Func createPixel) + where TPixel : unmanaged, IPixel + { + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + foreach (int length in lengths) + { + TPixel[] pixels = new TPixel[length]; + Vector4[] expectedNative = new Vector4[length]; + Vector4[] expectedScaled = new Vector4[length]; + Vector4[] actualNative = new Vector4[length]; + Vector4[] actualScaled = new Vector4[length]; + + for (int i = 0; i < pixels.Length; i++) + { + // Independent component sequences exercise every packed layout across each SIMD width and remainder. + pixels[i] = createPixel((byte)((i * 37) % 256), (byte)((i * 73) % 256), (byte)((i * 109) % 256), (byte)((i * 151) % 256)); + expectedNative[i] = pixels[i].ToUnassociatedVector4(); + expectedScaled[i] = pixels[i].ToUnassociatedScaledVector4(); + } + + if (length > 0) + { + // A zero-alpha pixel with stored RGB verifies the converter's defined zero-divisor behavior outside the lossless straight-color round-trip domain. + pixels[0] = createPixel(37, 73, 109, 0); + expectedNative[0] = pixels[0].ToUnassociatedVector4(); + expectedScaled[0] = pixels[0].ToUnassociatedScaledVector4(); + } + + operations.ToVector4(Configuration.Default, pixels, actualNative, PixelConversionModifiers.UnPremultiply); + operations.ToVector4(Configuration.Default, pixels, actualScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Assert.Equal(expectedNative, actualNative); + Assert.Equal(expectedScaled, actualScaled); + } + } + + private static void AssertScalarAndBulkFromUnassociatedVectorsAreEqual() + where TPixel : unmanaged, IPixel + { + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + AssociatedAlphaPixelOperations operations = (AssociatedAlphaPixelOperations)PixelOperations.Instance; + + foreach (int length in lengths) + { + Vector4[] vectors = new Vector4[length]; + TPixel[] expectedNative = new TPixel[length]; + TPixel[] expectedScaled = new TPixel[length]; + TPixel[] actualNative = new TPixel[length]; + TPixel[] actualScaled = new TPixel[length]; + + for (int i = 0; i < vectors.Length; i++) + { + // Fractional alpha values verify destination-alpha quantization across each SIMD width and remainder. + vectors[i] = new Vector4(((i * 37) % 1021) / 1020F, ((i * 73) % 1021) / 1020F, ((i * 109) % 1021) / 1020F, ((i * 151) % 1021) / 1020F); + expectedNative[i] = TPixel.FromUnassociatedVector4(vectors[i]); + expectedScaled[i] = TPixel.FromUnassociatedScaledVector4(vectors[i]); + } + + Vector4[] nativeSource = [.. vectors]; + Vector4[] scaledSource = [.. vectors]; + + operations.FromVector4Destructive(Configuration.Default, nativeSource, actualNative, PixelConversionModifiers.UnPremultiply); + operations.FromVector4Destructive(Configuration.Default, scaledSource, actualScaled, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Assert.Equal(expectedNative, actualNative); + Assert.Equal(expectedScaled, actualScaled); + } + } +} + +/// +/// Tests conversion between associated and unassociated packed byte formats. +/// +public class AssociatedToUnassociatedPackedPixelConversionTests +{ + [Fact] + public void Rgba32ToRgba32PMatchesExactAssociationForEveryComponentAndAlpha() + => AssertUnsignedByteAssociation((red, green, blue, alpha) => new Rgba32P(red, green, blue, alpha)); + + [Fact] + public void Rgba32ToBgra32PMatchesExactAssociationForEveryComponentAndAlpha() + => AssertUnsignedByteAssociation((red, green, blue, alpha) => new Bgra32P(red, green, blue, alpha)); + + [Fact] + public void Rgba32ToArgb32PMatchesExactAssociationForEveryComponentAndAlpha() + => AssertUnsignedByteAssociation((red, green, blue, alpha) => new Argb32P(red, green, blue, alpha)); + + [Fact] + public void Rgba32ToAbgr32PMatchesExactAssociationForEveryComponentAndAlpha() + => AssertUnsignedByteAssociation((red, green, blue, alpha) => new Abgr32P(red, green, blue, alpha)); + + [Fact] + public void Rgba32ToNormalizedByte4PMatchesExactAssociationForEveryComponentAndAlpha() + { + const int pairCount = 65536; + const int channelCount = 3; + Rgba32[] source = new Rgba32[pairCount * channelCount]; + NormalizedByte4P[] expected = new NormalizedByte4P[source.Length]; + NormalizedByte4P[] actualBulk = new NormalizedByte4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // NormalizedByte4 has 254 intervals from -1 through 1, so alpha is first rounded to that destination grid. + int destinationAlpha = ((alpha * 254) + 127) / byte.MaxValue; + + for (int unassociated = 0; unassociated <= byte.MaxValue; unassociated++) + { + // Association uses the alpha representable by the destination, then rounds the result to the same 254-interval grid. + int associated = ((unassociated * destinationAlpha) + 127) / byte.MaxValue; + + source[index] = new Rgba32((byte)unassociated, 0, 0, (byte)alpha); + expected[index++] = CreateNormalizedByte4P(associated, 0, 0, destinationAlpha); + source[index] = new Rgba32(0, (byte)unassociated, 0, (byte)alpha); + expected[index++] = CreateNormalizedByte4P(0, associated, 0, destinationAlpha); + source[index] = new Rgba32(0, 0, (byte)unassociated, (byte)alpha); + expected[index++] = CreateNormalizedByte4P(0, 0, associated, destinationAlpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + + Assert.Equal(expected, actualBulk); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(expected[i], NormalizedByte4P.FromRgba32(source[i])); + Assert.Equal(expected[i], Color.FromPixel(source[i]).ToPixel()); + } + } + + [Fact] + public void Rgba32ToHalfVector4PMatchesExactAssociationForEveryComponentAndAlpha() + { + const int pairCount = 65536; + const int channelCount = 3; + + const float finiteMinimum = -65504F; + const float finiteRange = 131008F; + const float inverseFiniteRange = (float)(1D / finiteRange); + const ulong zeroComponentBits = 0xFBFF; + Rgba32[] source = new Rgba32[pairCount * channelCount]; + HalfVector4P[] expected = new HalfVector4P[source.Length]; + HalfVector4P[] actualBulk = new HalfVector4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // Association must use the alpha value recovered from the destination half, rather than the higher-precision source alpha. + float normalizedAlpha = (float)(alpha / (double)byte.MaxValue); + ushort alphaBits = BitConverter.HalfToUInt16Bits((Half)((normalizedAlpha * finiteRange) + finiteMinimum)); + float destinationAlpha = ((float)BitConverter.UInt16BitsToHalf(alphaBits) * inverseFiniteRange) + .5F; + + for (int unassociated = 0; unassociated <= byte.MaxValue; unassociated++) + { + float normalizedComponent = (float)(unassociated / (double)byte.MaxValue); + float associated = normalizedComponent * destinationAlpha; + ushort associatedBits = BitConverter.HalfToUInt16Bits((Half)((associated * finiteRange) + finiteMinimum)); + ulong alphaPacked = (ulong)alphaBits << 48; + + source[index] = new Rgba32((byte)unassociated, 0, 0, (byte)alpha); + expected[index++] = new HalfVector4P { PackedValue = associatedBits | (zeroComponentBits << 16) | (zeroComponentBits << 32) | alphaPacked }; + source[index] = new Rgba32(0, (byte)unassociated, 0, (byte)alpha); + expected[index++] = new HalfVector4P { PackedValue = zeroComponentBits | ((ulong)associatedBits << 16) | (zeroComponentBits << 32) | alphaPacked }; + source[index] = new Rgba32(0, 0, (byte)unassociated, (byte)alpha); + expected[index++] = new HalfVector4P { PackedValue = zeroComponentBits | (zeroComponentBits << 16) | ((ulong)associatedBits << 32) | alphaPacked }; + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(expected[i], HalfVector4P.FromRgba32(source[i])); + Assert.Equal(expected[i], Color.FromPixel(source[i]).ToPixel()); + Assert.Equal(expected[i], actualBulk[i]); + } + } + + [Fact] + public void Rgba32PToRgba32ScalarRoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + { + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // Components greater than alpha are valid for additive blending but cannot round-trip losslessly through an 8-bit unassociated pixel. + // The exhaustive lossless domain is therefore triangular rather than 256 squared. + for (int associated = 0; associated <= alpha; associated++) + { + // This integer expression is the exact nearest 8-bit unassociated value for associated * 255 / alpha. + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + Rgba32P source = new((byte)associated, 0, 0, (byte)alpha); + Rgba32 expectedUnassociated = new(unassociated, 0, 0, (byte)alpha); + + Rgba32 actualUnassociated = source.ToRgba32(); + Rgba32P actualRoundTrip = Rgba32P.FromRgba32(actualUnassociated); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + } + } + + [Fact] + public void ColorFromRgba32PPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + { + const int pairCount = 32896; + Rgba32P[] sourcePixels = new Rgba32P[pairCount]; + Color[] bulkColors = new Color[pairCount]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + Rgba32P source = new((byte)associated, 0, 0, (byte)alpha); + Color color = Color.FromPixel(source); + sourcePixels[index++] = source; + + Assert.Equal(new Rgba32(unassociated, 0, 0, (byte)alpha), color.ToPixel()); + Assert.Equal(source, color.ToPixel()); + } + } + + Color.FromPixel(sourcePixels, bulkColors); + + for (int i = 0; i < sourcePixels.Length; i++) + { + Assert.Equal(sourcePixels[i], bulkColors[i].ToPixel()); + } + } + + [Fact] + public void Rgba32PToBgra32RoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Rgba32P(red, green, blue, alpha)); + + [Fact] + public void Bgra32PToBgra32RoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Bgra32P(red, green, blue, alpha)); + + [Fact] + public void Argb32PToBgra32RoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Argb32P(red, green, blue, alpha)); + + [Fact] + public void Abgr32PToBgra32RoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + => AssertUnsignedByteBulkRoundTrip((red, green, blue, alpha) => new Abgr32P(red, green, blue, alpha)); + + [Fact] + public void NormalizedByte4PToRgba32ScalarRoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + { + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + NormalizedByte4P source = CreateNormalizedByte4P(associated, 0, 0, alpha); + + Rgba32 actualUnassociated = source.ToRgba32(); + NormalizedByte4P actualRoundTrip = NormalizedByte4P.FromRgba32(actualUnassociated); + + Assert.Equal(new Rgba32(unassociated, 0, 0, unassociatedAlpha), actualUnassociated); + Assert.Equal(source, actualRoundTrip); + } + } + } + + [Fact] + public void ColorFromNormalizedByte4PPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + { + const int pairCount = 32640; + NormalizedByte4P[] sourcePixels = new NormalizedByte4P[pairCount]; + Color[] bulkColors = new Color[pairCount]; + int index = 0; + + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + NormalizedByte4P source = CreateNormalizedByte4P(associated, 0, 0, alpha); + Color color = Color.FromPixel(source); + sourcePixels[index++] = source; + + Assert.Equal(new Rgba32(unassociated, 0, 0, unassociatedAlpha), color.ToPixel()); + Assert.Equal(source, color.ToPixel()); + } + } + + Color.FromPixel(sourcePixels, bulkColors); + + for (int i = 0; i < sourcePixels.Length; i++) + { + Assert.Equal(sourcePixels[i], bulkColors[i].ToPixel()); + } + } + + [Fact] + public void NormalizedByte4PToBgra32RoundTripPreservesEveryLosslesslyRoundTrippableAssociatedComponent() + { + const int pairCount = 32640; + const int channelCount = 3; + NormalizedByte4P[] source = new NormalizedByte4P[pairCount * channelCount]; + Bgra32[] expectedUnassociated = new Bgra32[source.Length]; + Bgra32[] actualUnassociated = new Bgra32[source.Length]; + NormalizedByte4P[] actualRoundTrip = new NormalizedByte4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha < byte.MaxValue; alpha++) + { + for (int associated = 0; associated <= alpha; associated++) + { + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + byte unassociatedAlpha = (byte)(((alpha * byte.MaxValue) + 127) / 254); + + source[index] = CreateNormalizedByte4P(associated, 0, 0, alpha); + expectedUnassociated[index++] = new Bgra32(unassociated, 0, 0, unassociatedAlpha); + source[index] = CreateNormalizedByte4P(0, associated, 0, alpha); + expectedUnassociated[index++] = new Bgra32(0, unassociated, 0, unassociatedAlpha); + source[index] = CreateNormalizedByte4P(0, 0, associated, alpha); + expectedUnassociated[index++] = new Bgra32(0, 0, unassociated, unassociatedAlpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualUnassociated); + PixelOperations.Instance.From(Configuration.Default, actualUnassociated, actualRoundTrip); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + + for (int i = 0; i < source.Length; i++) + { + Bgra32 expected = expectedUnassociated[i]; + + // Scalar conversion and Color must preserve the same exact canonical value proven for the bulk path. + Assert.Equal(new Rgba32(expected.R, expected.G, expected.B, expected.A), source[i].ToRgba32()); + Assert.Equal(expected, Color.FromPixel(source[i]).ToPixel()); + } + } + + private static void AssertUnsignedByteBulkRoundTrip(Func createPixel) + where TPixel : unmanaged, IPixel + { + const int pairCount = 32896; + const int channelCount = 3; + TPixel[] source = new TPixel[pairCount * channelCount]; + Bgra32[] expectedUnassociated = new Bgra32[source.Length]; + Bgra32[] actualUnassociated = new Bgra32[source.Length]; + TPixel[] actualRoundTrip = new TPixel[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + // Components greater than alpha are valid for additive blending but cannot round-trip losslessly through an 8-bit unassociated pixel. + // The exhaustive lossless domain is therefore triangular rather than 256 squared. + for (int associated = 0; associated <= alpha; associated++) + { + // This integer expression is the exact nearest 8-bit unassociated value for associated * 255 / alpha. + byte unassociated = alpha == 0 ? (byte)0 : (byte)(((associated * byte.MaxValue) + (alpha / 2)) / alpha); + + source[index] = createPixel((byte)associated, 0, 0, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(unassociated, 0, 0, (byte)alpha); + source[index] = createPixel(0, (byte)associated, 0, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(0, unassociated, 0, (byte)alpha); + source[index] = createPixel(0, 0, (byte)associated, (byte)alpha); + expectedUnassociated[index++] = new Bgra32(0, 0, unassociated, (byte)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualUnassociated); + PixelOperations.Instance.From(Configuration.Default, actualUnassociated, actualRoundTrip); + + Assert.Equal(expectedUnassociated, actualUnassociated); + Assert.Equal(source, actualRoundTrip); + + for (int i = 0; i < source.Length; i++) + { + Bgra32 expected = expectedUnassociated[i]; + + // Scalar conversion and Color must use the same exact canonical byte as the independently calculated bulk oracle. + Assert.Equal(new Rgba32(expected.R, expected.G, expected.B, expected.A), source[i].ToRgba32()); + Assert.Equal(expected, Color.FromPixel(source[i]).ToPixel()); + } + } + + private static void AssertUnsignedByteAssociation(Func createPixel) + where TPixel : unmanaged, IPixel + { + const int pairCount = 65536; + const int channelCount = 3; + Rgba32[] source = new Rgba32[pairCount * channelCount]; + TPixel[] expected = new TPixel[source.Length]; + TPixel[] actualBulk = new TPixel[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= byte.MaxValue; alpha++) + { + for (int unassociated = 0; unassociated <= byte.MaxValue; unassociated++) + { + // The destination stores round(unassociated * alpha / 255). The integer numerator adds half + // the divisor so the expected value is independent of the floating-point implementation. + byte associated = (byte)(((unassociated * alpha) + 127) / byte.MaxValue); + + source[index] = new Rgba32((byte)unassociated, 0, 0, (byte)alpha); + expected[index++] = createPixel(associated, 0, 0, (byte)alpha); + source[index] = new Rgba32(0, (byte)unassociated, 0, (byte)alpha); + expected[index++] = createPixel(0, associated, 0, (byte)alpha); + source[index] = new Rgba32(0, 0, (byte)unassociated, (byte)alpha); + expected[index++] = createPixel(0, 0, associated, (byte)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + + Assert.Equal(expected, actualBulk); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(expected[i], TPixel.FromRgba32(source[i])); + Assert.Equal(expected[i], Color.FromPixel(source[i]).ToPixel()); + } + } + + private static NormalizedByte4P CreateNormalizedByte4P(int red, int green, int blue, int alpha) + { + uint packed = (byte)(red - 127) + | ((uint)(byte)(green - 127) << 8) + | ((uint)(byte)(blue - 127) << 16) + | ((uint)(byte)(alpha - 127) << 24); + + return new NormalizedByte4P { PackedValue = packed }; + } +} + +/// +/// Tests that associated destinations use their stored alpha value when associating color components. +/// +public class AssociatedDestinationAlphaQuantizationTests +{ + [Fact] + public void Rgba32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Bgra32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Argb32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void Abgr32PQuantizesDestinationAlphaBeforeAssociation() => AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation(); + + [Fact] + public void NormalizedByte4PQuantizesDestinationAlphaBeforeAssociation() + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + NormalizedByte4P[] actualBulk = new NormalizedByte4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + int expectedAlpha = ((alpha * 254) + 32767) / ushort.MaxValue; + + foreach (byte component in components) + { + int expectedRed = ((component * expectedAlpha) + 127) / byte.MaxValue; + NormalizedByte4P actualScalar = NormalizedByte4P.FromRgba64(source[index]); + int scalarRed = (sbyte)actualScalar.PackedValue + 127; + int scalarAlpha = (sbyte)(actualScalar.PackedValue >> 24) + 127; + int bulkRed = (sbyte)actualBulk[index].PackedValue + 127; + int bulkAlpha = (sbyte)(actualBulk[index].PackedValue >> 24) + 127; + + Assert.Equal(expectedRed, scalarRed); + Assert.Equal(expectedAlpha, scalarAlpha); + Assert.Equal(expectedRed, bulkRed); + Assert.Equal(expectedAlpha, bulkAlpha); + index++; + } + } + } + + [Fact] + public void HalfVector4PQuantizesDestinationAlphaBeforeAssociation() + { + const float finiteMinimum = -65504F; + const float finiteRange = 131008F; + const float inverseFiniteRange = (float)(1D / finiteRange); + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + HalfVector4P[] actualBulk = new HalfVector4P[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + float normalizedAlpha = alpha / (float)ushort.MaxValue; + ushort expectedAlpha = BitConverter.HalfToUInt16Bits((Half)((normalizedAlpha * finiteRange) + finiteMinimum)); + float storedAlpha = ((float)BitConverter.UInt16BitsToHalf(expectedAlpha) * inverseFiniteRange) + .5F; + + foreach (byte component in components) + { + float associatedRed = (component / (float)byte.MaxValue) * storedAlpha; + ushort expectedRed = BitConverter.HalfToUInt16Bits((Half)((associatedRed * finiteRange) + finiteMinimum)); + HalfVector4P actualScalar = HalfVector4P.FromRgba64(source[index]); + + Assert.Equal(expectedRed, (ushort)actualScalar.PackedValue); + Assert.Equal(expectedAlpha, (ushort)(actualScalar.PackedValue >> 48)); + Assert.Equal(expectedRed, (ushort)actualBulk[index].PackedValue); + Assert.Equal(expectedAlpha, (ushort)(actualBulk[index].PackedValue >> 48)); + index++; + } + } + } + + [Fact] + public void RgbaHalfPQuantizesDestinationAlphaBeforeAssociation() + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + RgbaHalfP[] actualBulk = new RgbaHalfP[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + float normalizedAlpha = alpha / (float)ushort.MaxValue; + Half expectedAlpha = (Half)normalizedAlpha; + float storedAlpha = (float)expectedAlpha; + + foreach (byte component in components) + { + Half expectedRed = (Half)((component / (float)byte.MaxValue) * storedAlpha); + RgbaHalfP actualScalar = RgbaHalfP.FromRgba64(source[index]); + + Assert.Equal(expectedRed, actualScalar.R); + Assert.Equal(expectedAlpha, actualScalar.A); + Assert.Equal(expectedRed, actualBulk[index].R); + Assert.Equal(expectedAlpha, actualBulk[index].A); + index++; + } + } + } + + [Fact] + public void ColorToRgba32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToBgra32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToArgb32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToAbgr32PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToNormalizedByte4PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToHalfVector4PUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + [Fact] + public void ColorToRgbaHalfPUsesDestinationAlphaRepresentation() => AssertColorUsesDestinationAlphaRepresentation(); + + /// + /// Verifies the unsigned-byte destination grid through scalar and bulk conversion entry points. + /// + /// The associated unsigned-byte pixel format. + private static void AssertUnsignedByteDestinationQuantizesAlphaBeforeAssociation() + where TPixel : unmanaged, IPixel + { + ReadOnlySpan components = [64, 127, 191]; + Rgba64[] source = new Rgba64[(ushort.MaxValue + 1) * components.Length]; + TPixel[] actualBulk = new TPixel[source.Length]; + int index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + foreach (byte component in components) + { + source[index++] = new Rgba64((ushort)(component * 257), 0, 0, (ushort)alpha); + } + } + + PixelOperations.Instance.From(Configuration.Default, source, actualBulk); + index = 0; + + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + int expectedAlpha = ((alpha * byte.MaxValue) + 32767) / ushort.MaxValue; + + foreach (byte component in components) + { + int expectedRed = ((component * expectedAlpha) + 127) / byte.MaxValue; + Vector4 expected = new Vector4(expectedRed, 0, 0, expectedAlpha) / byte.MaxValue; + + Assert.Equal(expected, TPixel.FromRgba64(source[index]).ToScaledVector4()); + Assert.Equal(expected, actualBulk[index].ToScaledVector4()); + index++; + } + } + } + + /// + /// Verifies that delegates association to the destination pixel operations. + /// + /// The associated destination pixel format. + private static void AssertColorUsesDestinationAlphaRepresentation() + where TPixel : unmanaged, IPixel + { + for (int alpha = 0; alpha <= ushort.MaxValue; alpha++) + { + ushort component = (ushort)((byte)alpha * 257); + Rgba64 source = new(component, 0, 0, (ushort)alpha); + TPixel expected = TPixel.FromRgba64(source); + TPixel actual = Color.FromPixel(source).ToPixel(); + + Assert.Equal(expected, actual); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 730e3996d9..362e20bbae 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +13,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 +22,8 @@ public void AreEqual() [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 +34,7 @@ public void AreNotEqual() [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 +44,7 @@ public void Constructor(byte r, byte g, byte b) [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 +56,8 @@ public unsafe void ByteLayoutIsSequentialBgr() [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 +70,8 @@ public void Equals_WhenTrue(byte r, byte g, byte b) [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,15 +80,14 @@ public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b [Fact] public void FromRgba32() { - var rgb = default(Bgr24); - 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); 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,8 +96,7 @@ public void FromRgba32() [Fact] public void FromVector4() { - var rgb = default(Bgr24); - 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); @@ -107,7 +106,7 @@ public void FromVector4() [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()); } @@ -115,15 +114,29 @@ public void ToVector4() [Fact] public void Bgr24_FromBgra5551() { - // arrange - var bgr = default(Bgr24); - // 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); 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR, info.ColorType); + + 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 8245a578f9..3c4a104233 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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 +31,10 @@ public void AreEqual() [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 +67,7 @@ public void Bgr565_ToVector3() public void Bgr565_ToScaledVector4() { // arrange - var bgr = new Bgr565(Vector3.One); + Bgr565 bgr = new(Vector3.One); // act Vector4 actual = bgr.ToScaledVector4(); @@ -83,11 +84,10 @@ public void Bgr565_FromScaledVector4() { // arrange Vector4 scaled = new Bgr565(Vector3.One).ToScaledVector4(); - int expected = 0xFFFF; - var pixel = default(Bgr565); + const int expected = 0xFFFF; // act - pixel.FromScaledVector4(scaled); + Bgr565 pixel = Bgr565.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -98,11 +98,10 @@ public void Bgr565_FromScaledVector4() public void Bgr565_FromBgra5551() { // arrange - var bgr = default(Bgr565); - 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); @@ -112,14 +111,12 @@ public void Bgr565_FromBgra5551() public void Bgr565_FromArgb32() { // arrange - var bgr1 = default(Bgr565); - var bgr2 = default(Bgr565); - 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); @@ -130,14 +127,12 @@ public void Bgr565_FromArgb32() public void Bgr565_FromRgba32() { // arrange - var bgr1 = default(Bgr565); - var bgr2 = default(Bgr565); - 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); @@ -148,12 +143,11 @@ public void Bgr565_FromRgba32() public void Bgr565_ToRgba32() { // arrange - var bgra = new Bgr565(Vector3.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgr565 pixel = new(Vector3.One); + Rgba32 expected = new(Vector4.One); // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -162,11 +156,10 @@ public void Bgr565_ToRgba32() public void Bgra565_FromRgb48() { // arrange - var bgr = default(Bgr565); - 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); @@ -176,11 +169,10 @@ public void Bgra565_FromRgb48() public void Bgra565_FromRgba64() { // arrange - var bgr = default(Bgr565); - 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); @@ -190,11 +182,10 @@ public void Bgra565_FromRgba64() public void Bgr565_FromBgr24() { // arrange - var bgr = default(Bgr565); - 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); @@ -204,11 +195,10 @@ public void Bgr565_FromBgr24() public void Bgr565_FromRgb24() { // arrange - var bgr = default(Bgr565); - 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); @@ -218,11 +208,10 @@ public void Bgr565_FromRgb24() public void Bgr565_FromGrey8() { // arrange - var bgr = default(Bgr565); - 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); @@ -232,11 +221,10 @@ public void Bgr565_FromGrey8() public void Bgr565_FromGrey16() { // arrange - var bgr = default(Bgr565); - 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); @@ -248,4 +236,21 @@ public void Bgr565_Clamping() 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR, info.ColorType); + + 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 04c8813d58..277975896e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,8 +16,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 +28,8 @@ public void AreEqual() [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 +47,7 @@ public void AreNotEqual() [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 +58,7 @@ public void Constructor(byte b, byte g, byte r, byte a) [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 +71,8 @@ public unsafe void ByteLayoutIsSequentialBgra() [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 +86,8 @@ public void Equality_WhenTrue(byte b, byte g, byte r, byte a) [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,8 +96,7 @@ public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte [Fact] public void FromRgba32() { - var bgra = default(Bgra32); - 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); @@ -104,7 +104,7 @@ public void FromRgba32() 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,8 +113,7 @@ public void FromRgba32() [Fact] public void FromVector4() { - var c = default(Bgra32); - 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); @@ -125,7 +124,7 @@ public void FromVector4() [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,13 +133,30 @@ public void ToVector4() public void Bgra32_FromBgra5551() { // arrange - var bgra = default(Bgra32); - 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); } + + [Fact] + public void Bgra32_PixelInformation() + { + 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); + 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 543b5814f9..5d20b5cf12 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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 +31,10 @@ public void AreEqual() [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,10 +67,10 @@ public void Bgra4444_ToVector4() public void Bgra4444_ToScaledVector4() { // arrange - var bgra = new Bgra4444(Vector4.One); + Bgra4444 pixel = new(Vector4.One); // act - Vector4 actual = bgra.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -82,12 +83,11 @@ public void Bgra4444_ToScaledVector4() public void Bgra4444_ToRgba32() { // arrange - var bgra = new Bgra4444(Vector4.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgra4444 pixel = new(Vector4.One); + Rgba32 expected = new(Vector4.One); // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -97,12 +97,11 @@ public void Bgra4444_FromScaledVector4() { // arrange Vector4 scaled = new Bgra4444(Vector4.One).ToScaledVector4(); - int expected = 0xFFFF; - var bgra = default(Bgra4444); + 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); @@ -112,42 +111,38 @@ public void Bgra4444_FromScaledVector4() public void Bgra4444_FromBgra5551() { // arrange - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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 - var bgra1 = default(Bgra4444); - var bgra2 = default(Bgra4444); - 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); @@ -158,84 +153,78 @@ public void Bgra4444_FromRgba32() public void Bgra4444_FromRgb48() { // arrange - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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 - var bgra = default(Bgra4444); - 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] @@ -244,4 +233,22 @@ public void Bgra4444_Clamping() 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(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR | PixelColorType.Alpha, info.ColorType); + + 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 ec54173101..38f809e49f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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(1f, 0.0f, 0.0f, 1f)); + Bgra5551 color4 = new(1f, 0.0f, 0.0f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +31,10 @@ public void AreEqual() [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(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); @@ -42,10 +43,10 @@ public void AreNotEqual() [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); @@ -71,10 +72,10 @@ public void Bgra5551_ToVector4() public void Bgra5551_ToScaledVector4() { // arrange - var bgra = new Bgra5551(Vector4.One); + Bgra5551 pixel = new(Vector4.One); // act - Vector4 actual = bgra.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -87,12 +88,11 @@ public void Bgra5551_ToScaledVector4() public void Bgra5551_ToRgba32() { // arrange - var bgra = new Bgra5551(Vector4.One); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + Bgra5551 pixel = new(Vector4.One); + Rgba32 expected = new(Vector4.One); // act - bgra.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -102,11 +102,10 @@ public void Bgra5551_FromScaledVector4() { // arrange Vector4 scaled = new Bgra5551(Vector4.One).ToScaledVector4(); - int expected = 0xFFFF; - var pixel = default(Bgra5551); + const int expected = 0xFFFF; // act - pixel.FromScaledVector4(scaled); + Bgra5551 pixel = Bgra5551.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -117,13 +116,11 @@ public void Bgra5551_FromScaledVector4() 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 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); @@ -133,14 +130,12 @@ public void Bgra5551_FromBgra5551() public void Bgra5551_FromRgba32() { // arrange - var bgra1 = default(Bgra5551); - var bgra2 = default(Bgra5551); - 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); @@ -151,14 +146,12 @@ public void Bgra5551_FromRgba32() public void Bgra5551_FromBgra32() { // arrange - var bgra1 = default(Bgra5551); - var bgra2 = default(Bgra5551); - 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); @@ -169,98 +162,91 @@ public void Bgra5551_FromBgra32() public void Bgra5551_FromArgb32() { // arrange - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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 - var bgra = default(Bgra5551); - 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] @@ -269,4 +255,22 @@ public void Bgra5551_Clamping() 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(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.BGR | PixelColorType.Alpha, info.ColorType); + + 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 435f073915..e73d646408 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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(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); @@ -30,10 +31,10 @@ public void AreEqual() [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(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); @@ -63,10 +64,10 @@ public void Byte4_ToVector4() public void Byte4_ToScaledVector4() { // arrange - var byte4 = new Byte4(Vector4.One * 255); + Byte4 pixel = new(Vector4.One * 255); // act - Vector4 actual = byte4.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(1, actual.X); @@ -79,12 +80,11 @@ public void Byte4_ToScaledVector4() 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 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -94,11 +94,10 @@ public void Byte4_FromScaledVector4() { // arrange Vector4 scaled = new Byte4(Vector4.One * 255).ToScaledVector4(); - var pixel = default(Byte4); - uint expected = 0xFFFFFFFF; + const uint expected = 0xFFFFFFFF; // act - pixel.FromScaledVector4(scaled); + Byte4 pixel = Byte4.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -109,126 +108,117 @@ public void Byte4_FromScaledVector4() public void Byte4_FromArgb32() { // arrange - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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 - var byte4 = default(Byte4); - 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] @@ -237,4 +227,22 @@ public void Byte4_Clamping() 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(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); + + 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/FloatingPointPixelNormalizationTests.cs b/tests/ImageSharp.Tests/PixelFormats/FloatingPointPixelNormalizationTests.cs new file mode 100644 index 0000000000..5718da5b03 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/FloatingPointPixelNormalizationTests.cs @@ -0,0 +1,183 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +[Trait("Category", "PixelFormats")] +public class FloatingPointPixelNormalizationTests +{ + /// + /// HalfSingle normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void HalfSingle_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// HalfVector2 normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void HalfVector2_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// HalfVector4 normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void HalfVector4_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// HalfVector4P normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void HalfVector4P_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// RgbaVector normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void RgbaVector_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// RgbaHalf normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void RgbaHalf_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// RgbaHalfP normalizes scaled input identically in scalar and bulk conversions. + /// + [Fact] + public void RgbaHalfP_ScaledInputIsNormalized() => AssertScaledInputIsNormalized(); + + /// + /// Raw half storage preserves IEEE special values while its scaled representation remains finite. + /// + [Fact] + public void HalfVector4_NativeSpecialValuesHaveNormalizedScaledOutput() => AssertNativeSpecialValuesHaveNormalizedScaledOutput(); + + /// + /// Associated half-vector conversion uses the stored alpha ratio before normalizing RGB. + /// + [Fact] + public void HalfVector4P_AssociatedScaledInputIsNormalized() => AssertAssociatedScaledInputIsNormalized(); + + /// + /// Associated half-RGBA conversion uses the stored alpha ratio before normalizing RGB. + /// + [Fact] + public void RgbaHalfP_AssociatedScaledInputIsNormalized() => AssertAssociatedScaledInputIsNormalized(); + + /// + /// Checks saturation and NaN handling without deriving expectations from the invalid-input path. + /// + /// The destination pixel format. + private static void AssertScaledInputIsNormalized() + where TPixel : unmanaged, IPixel + { + Vector4[] inputs = + [ + new(float.PositiveInfinity, float.NegativeInfinity, float.NaN, 1F), + new(2F, -2F, .5F, 1F), + new(.25F, .5F, .75F, .5F), + new(.25F, .5F, .75F, float.NaN), + new(.25F, .5F, .75F, float.PositiveInfinity) + ]; + + Vector4[] normalized = + [ + new(1F, 0F, 0F, 1F), + new(1F, 0F, .5F, 1F), + new(.25F, .5F, .75F, .5F), + new(.25F, .5F, .75F, 0F), + new(.25F, .5F, .75F, 1F) + ]; + + // Seventeen pixels exercise wide registers and the narrower remainder paths. + Vector4[] source = new Vector4[17]; + TPixel[] expected = new TPixel[source.Length]; + TPixel[] actual = new TPixel[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + int sample = i % inputs.Length; + source[i] = inputs[sample]; + expected[i] = TPixel.FromUnassociatedScaledVector4(normalized[sample]); + Assert.Equal(expected[i], TPixel.FromUnassociatedScaledVector4(source[i])); + } + + // Associated formats otherwise interpret the vectors using their native alpha representation. + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, source, actual, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + Assert.Equal(expected, actual); + } + + /// + /// Checks associated input against finite control values with the same represented color. + /// + /// The associated destination pixel format. + private static void AssertAssociatedScaledInputIsNormalized() + where TPixel : unmanaged, IPixel + { + Vector4[] inputs = + [ + new(float.PositiveInfinity, float.NegativeInfinity, float.NaN, 1F), + new(1F, .5F, 1.5F, 2F), + new(.125F, .25F, .375F, .5F), + new(.25F, .5F, .75F, float.NaN), + new(.25F, .5F, .75F, float.PositiveInfinity) + ]; + + Vector4[] normalized = + [ + new(1F, 0F, 0F, 1F), + new(.5F, .25F, .75F, 1F), + new(.125F, .25F, .375F, .5F), + Vector4.Zero, + new(0F, 0F, 0F, 1F) + ]; + + Vector4[] source = new Vector4[17]; + TPixel[] expected = new TPixel[source.Length]; + TPixel[] actual = new TPixel[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + int sample = i % inputs.Length; + source[i] = inputs[sample]; + expected[i] = TPixel.FromAssociatedScaledVector4(normalized[sample]); + Assert.Equal(expected[i], TPixel.FromAssociatedScaledVector4(source[i])); + } + + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, source, actual, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); + Assert.Equal(expected, actual); + } + + /// + /// Checks native storage and every scaled output lane independently of integer conversion semantics. + /// + private static void AssertNativeSpecialValuesHaveNormalizedScaledOutput() + { + Vector4 native = new(float.PositiveInfinity, float.NegativeInfinity, float.NaN, 65504F); + HalfVector4 pixel = HalfVector4.FromVector4(native); + Assert.True(float.IsPositiveInfinity(pixel.ToVector4().X)); + Assert.True(float.IsNegativeInfinity(pixel.ToVector4().Y)); + Assert.True(float.IsNaN(pixel.ToVector4().Z)); + + Vector4 expected = new(1F, 0F, 0F, 1F); + Assert.Equal(expected, pixel.ToScaledVector4()); + Assert.Equal(1F, new HalfSingle(float.PositiveInfinity).ToScaledVector4().X); + Assert.Equal(0F, new HalfSingle(float.NaN).ToScaledVector4().X); + Assert.Equal(new Vector4(1F, 0F, 0F, 1F), new HalfVector2(new Vector2(float.PositiveInfinity, float.NaN)).ToScaledVector4()); + + HalfVector4[] source = new HalfVector4[17]; + Vector4[] nativeSource = new Vector4[source.Length]; + Array.Fill(nativeSource, native); + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, nativeSource, source, PixelConversionModifiers.None); + Assert.All(source, value => Assert.Equal(pixel.PackedValue, value.PackedValue)); + + Vector4[] actual = new Vector4[source.Length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actual, PixelConversionModifiers.Scale); + Assert.All(actual, value => Assert.Equal(expected, value)); + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index 9ffaf3e21c..3e06f04714 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -25,45 +27,76 @@ public void HalfSingle_PackedValue() public void HalfSingle_ToVector4() { // arrange - var halfSingle = new HalfSingle(0.5f); - var expected = new Vector4(0.5f, 0, 0, 1); + HalfSingle pixel = new(-2F); + Vector4 expected = new(-2F, 0, 0, 1); // act - var actual = halfSingle.ToVector4(); + Vector4 actual = pixel.ToVector4(); // assert Assert.Equal(expected, actual); + Assert.Equal((float)Half.MinValue, new HalfSingle((float)Half.MinValue).ToSingle()); + Assert.Equal((float)Half.MaxValue, new HalfSingle((float)Half.MaxValue).ToSingle()); } [Fact] public void HalfSingle_ToScaledVector4() { - // arrange - var halfSingle = new HalfSingle(-1F); - - // act - Vector4 actual = halfSingle.ToScaledVector4(); - - // assert - Assert.Equal(0, actual.X); - Assert.Equal(0, actual.Y); - Assert.Equal(0, actual.Z); - Assert.Equal(1, actual.W); + Assert.Equal(new Vector4(0F, 0F, 0F, 1F), new HalfSingle((float)Half.MinValue).ToScaledVector4()); + Assert.Equal(new Vector4(.5F, 0F, 0F, 1F), new HalfSingle(0F).ToScaledVector4()); + Assert.Equal(new Vector4(1F, 0F, 0F, 1F), new HalfSingle((float)Half.MaxValue).ToScaledVector4()); } [Fact] public void HalfSingle_FromScaledVector4() { - // arrange - Vector4 scaled = new HalfSingle(-1F).ToScaledVector4(); - int expected = 48128; - var halfSingle = default(HalfSingle); + Assert.Equal((ushort)0xFBFF, HalfSingle.FromScaledVector4(new Vector4(0F, 0F, 0F, 1F)).PackedValue); + Assert.Equal((ushort)0, HalfSingle.FromScaledVector4(new Vector4(.5F, 0F, 0F, 1F)).PackedValue); + Assert.Equal((ushort)0x7BFF, HalfSingle.FromScaledVector4(new Vector4(1F, 0F, 0F, 1F)).PackedValue); + } - // act - halfSingle.FromScaledVector4(scaled); - ushort actual = halfSingle.PackedValue; + [Fact] + public void HalfSingle_BulkScaledConversionsCoverFiniteRange() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertHalfSingleBulkScaledConversionsCoverFiniteRange, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); - // assert - Assert.Equal(expected, actual); + private static void AssertHalfSingleBulkScaledConversionsCoverFiniteRange() + { + ushort[] packedValues = [0xFBFF, 0, 0x7BFF]; + Vector4[] scaledValues = [new(0F, 0F, 0F, 1F), new(.5F, 0F, 0F, 1F), new(1F, 0F, 0F, 1F)]; + HalfSingle[] source = new HalfSingle[17]; + Vector4[] expectedVectors = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + int valueIndex = i % packedValues.Length; + source[i].PackedValue = packedValues[valueIndex]; + expectedVectors[i] = scaledValues[valueIndex]; + } + + Vector4[] actualVectors = new Vector4[source.Length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actualVectors, PixelConversionModifiers.Scale); + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] destructiveSource = [.. expectedVectors]; + HalfSingle[] actualPixels = new HalfSingle[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.Equal(source, actualPixels); + } + + [Fact] + public void HalfSingle_PixelInformation() + { + 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); + 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 eda9315363..1fde34a826 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -24,19 +26,21 @@ public void HalfVector2_ToVector2() Assert.Equal(Vector2.One, new HalfVector2(Vector2.One).ToVector2()); Assert.Equal(Vector2.UnitX, new HalfVector2(Vector2.UnitX).ToVector2()); Assert.Equal(Vector2.UnitY, new HalfVector2(Vector2.UnitY).ToVector2()); + Assert.Equal(new Vector2(-2F, 4F), new HalfVector2(-2F, 4F).ToVector2()); + Assert.Equal(new Vector2((float)Half.MinValue, (float)Half.MaxValue), new HalfVector2((float)Half.MinValue, (float)Half.MaxValue).ToVector2()); } [Fact] public void HalfVector2_ToScaledVector4() { // arrange - var halfVector = new HalfVector2(Vector2.One); + HalfVector2 halfVector = new((float)Half.MinValue, (float)Half.MaxValue); // act Vector4 actual = halfVector.ToScaledVector4(); // assert - Assert.Equal(1F, actual.X); + Assert.Equal(0F, actual.X); Assert.Equal(1F, actual.Y); Assert.Equal(0, actual.Z); Assert.Equal(1, actual.W); @@ -46,27 +50,50 @@ public void HalfVector2_ToScaledVector4() public void HalfVector2_FromScaledVector4() { // arrange - Vector4 scaled = new HalfVector2(Vector2.One).ToScaledVector4(); - uint expected = 1006648320u; - var halfVector = default(HalfVector2); + Vector4 scaled = new(0F, 1F, 0F, 1F); + const uint expected = 0x7BFF_FBFF; // act - halfVector.FromScaledVector4(scaled); + HalfVector2 halfVector = HalfVector2.FromScaledVector4(scaled); uint actual = halfVector.PackedValue; // assert Assert.Equal(expected, actual); } + [Fact] + public void HalfVector2_BulkScaledConversionsCoverFiniteRange() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertHalfVector2BulkScaledConversionsCoverFiniteRange, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertHalfVector2BulkScaledConversionsCoverFiniteRange() + { + HalfVector2 pixel = new() { PackedValue = 0x7BFF_FBFF }; + HalfVector2[] source = new HalfVector2[17]; + Vector4[] expectedVectors = new Vector4[source.Length]; + Array.Fill(source, pixel); + Array.Fill(expectedVectors, new Vector4(0F, 1F, 0F, 1F)); + + Vector4[] actualVectors = new Vector4[source.Length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actualVectors, PixelConversionModifiers.Scale); + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] destructiveSource = [.. expectedVectors]; + HalfVector2[] actualPixels = new HalfVector2[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.Equal(source, actualPixels); + } + [Fact] 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); @@ -75,17 +102,30 @@ public void HalfVector2_ToVector4() [Fact] public void HalfVector2_FromBgra5551() { - // arrange - var halfVector2 = default(HalfVector2); - // 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); Assert.Equal(1, actual.W); } + + [Fact] + public void HalfVector2_PixelInformation() + { + 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); + 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 77120192bb..75130c144c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -32,51 +34,94 @@ public void HalfVector4_ToVector4() Assert.Equal(Vector4.UnitY, new HalfVector4(Vector4.UnitY).ToVector4()); Assert.Equal(Vector4.UnitZ, new HalfVector4(Vector4.UnitZ).ToVector4()); Assert.Equal(Vector4.UnitW, new HalfVector4(Vector4.UnitW).ToVector4()); + Assert.Equal(new Vector4(-2F, 4F, .5F, 8F), new HalfVector4(-2F, 4F, .5F, 8F).ToVector4()); + Assert.Equal( + new Vector4((float)Half.MinValue, (float)Half.MaxValue, (float)Half.MinValue, (float)Half.MaxValue), + new HalfVector4((float)Half.MinValue, (float)Half.MaxValue, (float)Half.MinValue, (float)Half.MaxValue).ToVector4()); } [Fact] public void HalfVector4_ToScaledVector4() { // arrange - var halfVector4 = new HalfVector4(-Vector4.One); + Vector4 expected = new(0F, .5F, 1F, 0F); + HalfVector4 pixel = new((float)Half.MinValue, 0F, (float)Half.MaxValue, (float)Half.MinValue); // act - Vector4 actual = halfVector4.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert - Assert.Equal(0, actual.X); - Assert.Equal(0, actual.Y); - Assert.Equal(0, actual.Z); - Assert.Equal(0, actual.W); + Assert.Equal(expected, actual); } [Fact] public void HalfVector4_FromScaledVector4() { // arrange - var halfVector4 = default(HalfVector4); - Vector4 scaled = new HalfVector4(-Vector4.One).ToScaledVector4(); - ulong expected = 13547034390470638592uL; + Vector4 scaled = new(0F, .25F, .5F, 1F); + ulong expected = new HalfVector4((float)Half.MinValue, -32752F, 0F, (float)Half.MaxValue).PackedValue; // act - halfVector4.FromScaledVector4(scaled); - ulong actual = halfVector4.PackedValue; + HalfVector4 pixel = HalfVector4.FromScaledVector4(scaled); + ulong actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); } + [Fact] + public void HalfVector4_BulkScaledConversionsCoverFiniteRange() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertHalfVector4BulkScaledConversionsCoverFiniteRange, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertHalfVector4BulkScaledConversionsCoverFiniteRange() + { + const ulong packedValue = 0xFBFF_7BFF_0000_FBFF; + HalfVector4 pixel = new() { PackedValue = packedValue }; + HalfVector4[] source = new HalfVector4[17]; + Vector4[] expectedVectors = new Vector4[source.Length]; + Array.Fill(source, pixel); + Array.Fill(expectedVectors, new Vector4(0F, .5F, 1F, 0F)); + + Vector4[] actualVectors = new Vector4[source.Length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actualVectors, PixelConversionModifiers.Scale); + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] destructiveSource = [.. expectedVectors]; + HalfVector4[] actualPixels = new HalfVector4[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.Equal(source, actualPixels); + } + [Fact] public void HalfVector4_FromBgra5551() { // arrange - var halfVector4 = default(HalfVector4); 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] + public void HalfVector4_PixelInformation() + { + 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); + 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 685da2ddf8..7f0a4217c1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +13,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 +22,8 @@ public void AreEqual() [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); } @@ -39,13 +40,12 @@ public void L16_PackedValue_EqualsInput(ushort input) 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); @@ -58,10 +58,10 @@ public void L16_FromScaledVector4() public void L16_ToScaledVector4(ushort input) { // Arrange - var gray = new L16(input); + L16 pixel = new(input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float vectorInput = input / 65535F; @@ -75,13 +75,12 @@ public void L16_ToScaledVector4(ushort input) public void L16_FromVector4() { // Arrange - L16 gray = default; const ushort expected = 32767; - var vector = new L16(expected).ToVector4(); + 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); @@ -94,10 +93,10 @@ public void L16_FromVector4() public void L16_ToVector4(ushort input) { // Arrange - var gray = new L16(input); + L16 pixel = new(input); // Act - var actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -111,14 +110,13 @@ public void L16_ToVector4(ushort input) public void L16_FromRgba32() { // 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 - 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); @@ -131,12 +129,11 @@ public void L16_FromRgba32() public void L16_ToRgba32(ushort input) { // Arrange - ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); - var gray = new L16(input); + ushort expected = ColorNumerics.From16BitTo8Bit(input); + L16 pixel = new(input); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(expected, actual.R); @@ -149,13 +146,27 @@ public void L16_ToRgba32(ushort input) public void L16_FromBgra5551() { // arrange - var gray = default(L16); - 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] + public void L16_PixelInformation() + { + PixelTypeInfo info = L16.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Luminance, info.ColorType); + + 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 afdc039a28..1ca865ef41 100644 --- a/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -24,8 +25,8 @@ public void L8_PackedValue_EqualsInput(byte input) [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 +34,8 @@ public void AreEqual() [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); } @@ -43,13 +44,12 @@ public void AreNotEqual() 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); @@ -60,10 +60,10 @@ public void L8_FromScaledVector4() public void L8_ToScaledVector4(byte input) { // Arrange - var gray = new L8(input); + L8 pixel = new(input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float scaledInput = input / 255F; @@ -78,12 +78,11 @@ public void L8_ToScaledVector4(byte input) public void L8_FromVector4(byte luminance) { // Arrange - L8 gray = default; - var vector = new L8(luminance).ToVector4(); + 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); @@ -94,10 +93,10 @@ public void L8_FromVector4(byte luminance) public void L8_ToVector4(byte input) { // Arrange - var gray = new L8(input); + L8 pixel = new(input); // Act - var actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float scaledInput = input / 255F; @@ -112,12 +111,11 @@ public void L8_ToVector4(byte input) 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); @@ -128,11 +126,10 @@ public void L8_FromRgba32(byte rgb) public void L8_ToRgba32(byte luminance) { // Arrange - var gray = new L8(luminance); + L8 pixel = new(luminance); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(luminance, actual.R); @@ -145,11 +142,10 @@ public void L8_ToRgba32(byte luminance) public void L8_FromBgra5551() { // arrange - var grey = default(L8); - 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); @@ -164,13 +160,11 @@ public class Rgba32Compatibility [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); + Rgba32 rgba = original.ToRgba32(); - L8 mirror = default; - mirror.FromRgba32(rgba); + L8 mirror = L8.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -179,13 +173,10 @@ public void L8_FromRgba32_IsInverseOf_ToRgba32(byte luminance) [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); - - L8 mirror = default; - mirror.FromRgba32(rgba); + Rgba32 rgba = original.ToRgba32(); + L8 mirror = L8.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -194,13 +185,12 @@ public void Rgba32_ToL8_IsInverseOf_L8_ToRgba32(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); - var l8Vector = original.ToVector4(); - var rgbaVector = original.ToVector4(); + Vector4 l8Vector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); Assert.Equal(l8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -209,15 +199,13 @@ public void ToVector4_IsRgba32Compatible(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); - var rgbaVector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); - L8 mirror = default; - mirror.FromVector4(rgbaVector); + L8 mirror = L8.FromVector4(rgbaVector); Assert.Equal(original, mirror); } @@ -226,10 +214,9 @@ public void FromVector4_IsRgba32Compatible(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); Vector4 l8Vector = original.ToScaledVector4(); Vector4 rgbaVector = original.ToScaledVector4(); @@ -241,17 +228,30 @@ public void ToScaledVector4_IsRgba32Compatible(byte luminance) [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); + 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); } + + [Fact] + public void L8_PixelInformation() + { + PixelTypeInfo info = L8.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Luminance, info.ColorType); + + 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 92b4a4e1a1..f6cbfc4426 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming @@ -24,8 +25,8 @@ public void La16_PackedValue_EqualsPackedInput(byte input, ushort packed) [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 +34,8 @@ public void AreEqual() [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); } @@ -43,12 +44,11 @@ public void AreNotEqual() 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 @@ -60,7 +60,7 @@ public void La16_FromScaledVector4() public void La16_ToScaledVector4(byte input) { // Arrange - var gray = new La16(input, input); + La16 gray = new(input, input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -78,11 +78,10 @@ public void La16_ToScaledVector4(byte input) public void La16_FromVector4(byte luminance) { // Arrange - La16 gray = default; - var vector = new La16(luminance, luminance).ToVector4(); + Vector4 vector = new La16(luminance, luminance).ToVector4(); // Act - gray.FromVector4(vector); + La16 gray = La16.FromVector4(vector); byte actualL = gray.L; byte actualA = gray.A; @@ -96,10 +95,10 @@ public void La16_FromVector4(byte luminance) 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; @@ -114,11 +113,10 @@ public void La16_ToVector4(byte input) 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 @@ -131,11 +129,10 @@ public void La16_FromRgba32(byte rgb) public void La16_ToRgba32(byte luminance) { // Arrange - var gray = new La16(luminance, luminance); + La16 gray = new(luminance, luminance); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = gray.ToRgba32(); // Assert Assert.Equal(luminance, actual.R); @@ -148,11 +145,10 @@ public void La16_ToRgba32(byte luminance) public void La16_FromBgra5551() { // arrange - var grey = default(La16); - 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); @@ -168,13 +164,11 @@ public class Rgba32Compatibility [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); + Rgba32 rgba = original.ToRgba32(); - La16 mirror = default; - mirror.FromRgba32(rgba); + La16 mirror = La16.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -183,13 +177,11 @@ public void La16_FromRgba32_IsInverseOf_ToRgba32(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); - La16 mirror = default; - mirror.FromRgba32(rgba); + La16 mirror = La16.FromRgba32(rgba); Assert.Equal(original, mirror); } @@ -198,13 +190,12 @@ public void Rgba32_ToLa16_IsInverseOf_La16_ToRgba32(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); - var la16Vector = original.ToVector4(); - var rgbaVector = original.ToVector4(); + Vector4 la16Vector = original.ToVector4(); + Vector4 rgbaVector = rgba.ToVector4(); Assert.Equal(la16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -213,15 +204,12 @@ public void ToVector4_IsRgba32Compatible(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); + Vector4 rgbaVector = rgba.ToVector4(); - var rgbaVector = original.ToVector4(); - - La16 mirror = default; - mirror.FromVector4(rgbaVector); + La16 mirror = La16.FromVector4(rgbaVector); Assert.Equal(original, mirror); } @@ -230,13 +218,12 @@ public void FromVector4_IsRgba32Compatible(byte luminance) [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); + Rgba32 rgba = original.ToRgba32(); Vector4 la16Vector = original.ToScaledVector4(); - Vector4 rgbaVector = original.ToScaledVector4(); + Vector4 rgbaVector = rgba.ToScaledVector4(); Assert.Equal(la16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } @@ -245,17 +232,30 @@ public void ToScaledVector4_IsRgba32Compatible(byte luminance) [MemberData(nameof(LuminanceData))] public void FromScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new La16(luminance, luminance); - - Rgba32 rgba = default; - original.ToRgba32(ref rgba); + La16 original = new(luminance, luminance); - 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); } + + [Fact] + public void La16_PixelInformation() + { + PixelTypeInfo info = La16.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Luminance | PixelColorType.Alpha, info.ColorType); + + 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 d333818c78..fd5556d3bc 100644 --- a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,8 +13,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 +22,8 @@ public void AreEqual() [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); } @@ -39,14 +40,13 @@ public void La32_PackedValue_EqualsInput(ushort input, uint packed) 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); @@ -60,10 +60,10 @@ public void La32_FromScaledVector4() public void La32_ToScaledVector4(ushort input) { // Arrange - var gray = new La32(input, input); + La32 pixel = new(input, input); // Act - Vector4 actual = gray.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // Assert float vectorInput = input / 65535F; @@ -77,14 +77,13 @@ public void La32_ToScaledVector4(ushort input) public void La32_FromVector4() { // 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); - 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); @@ -98,10 +97,10 @@ public void La32_FromVector4() public void La32_ToVector4(ushort input) { // Arrange - var gray = new La32(input, input); + La32 pixel = new(input, input); // Act - var actual = gray.ToVector4(); + Vector4 actual = pixel.ToVector4(); // Assert float vectorInput = input / 65535F; @@ -115,18 +114,17 @@ public void La32_ToVector4(ushort input) public void La32_FromRgba32() { // 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 - 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] @@ -136,12 +134,11 @@ public void La32_FromRgba32() public void La32_ToRgba32(ushort input) { // Arrange - ushort expected = ColorNumerics.DownScaleFrom16BitTo8Bit(input); - var gray = new La32(input, ushort.MaxValue); + ushort expected = ColorNumerics.From16BitTo8Bit(input); + La32 pixel = new(input, ushort.MaxValue); // Act - Rgba32 actual = default; - gray.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); // Assert Assert.Equal(expected, actual.R); @@ -154,14 +151,29 @@ public void La32_ToRgba32(ushort input) public void La32_FromBgra5551() { // arrange - var gray = default(La32); - 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] + public void La32_PixelInformation() + { + PixelTypeInfo info = La32.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Luminance | PixelColorType.Alpha, info.ColorType); + + 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 14c590d0b5..e6be539c9a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -28,6 +30,42 @@ public void NormalizedByte2_ToVector2() Assert.Equal(-Vector2.One, new NormalizedByte2(Vector2.One * -1234.0f).ToVector2()); } + [Fact] + public void NormalizedByte2_MinimumStorageCodeDecodesAsNegativeOne() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertNormalizedByte2MinimumStorageCodeDecodesAsNegativeOne, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertNormalizedByte2MinimumStorageCodeDecodesAsNegativeOne() + { + NormalizedByte2 pixel = new() { PackedValue = 0x8080 }; + Vector4 expectedNative = new(-1F, -1F, 0F, 1F); + Vector4 expectedScaled = new(0F, 0F, 0F, 1F); + + Assert.Equal(expectedNative, pixel.ToVector4()); + Assert.Equal(expectedScaled, pixel.ToScaledVector4()); + + NormalizedByte2[] source = new NormalizedByte2[17]; + Vector4[] native = new Vector4[source.Length]; + Vector4[] scaled = new Vector4[source.Length]; + Array.Fill(source, pixel); + + PixelOperations.Instance.ToVector4(Configuration.Default, source, native); + PixelOperations.Instance.ToVector4(Configuration.Default, source, scaled, PixelConversionModifiers.Scale); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(expectedNative, native[i]); + Assert.Equal(expectedScaled, scaled[i]); + } + + Vector4[] destructiveSource = new Vector4[source.Length]; + Array.Fill(destructiveSource, expectedScaled); + NormalizedByte2[] actualPixels = new NormalizedByte2[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.All(actualPixels, actual => Assert.Equal((ushort)0x8181, actual.PackedValue)); + } + [Fact] public void NormalizedByte2_ToVector4() { @@ -39,10 +77,10 @@ public void NormalizedByte2_ToVector4() public void NormalizedByte2_ToScaledVector4() { // arrange - var byte2 = new NormalizedByte2(-Vector2.One); + NormalizedByte2 pixel = new(-Vector2.One); // act - Vector4 actual = byte2.ToScaledVector4(); + Vector4 actual = pixel.ToScaledVector4(); // assert Assert.Equal(0, actual.X); @@ -56,12 +94,11 @@ public void NormalizedByte2_FromScaledVector4() { // arrange Vector4 scaled = new NormalizedByte2(-Vector2.One).ToScaledVector4(); - var byte2 = default(NormalizedByte2); - 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); @@ -71,13 +108,28 @@ public void NormalizedByte2_FromScaledVector4() public void NormalizedByte2_FromBgra5551() { // arrange - var normalizedByte2 = default(NormalizedByte2); - var expected = new Vector4(1, 1, 0, 1); + 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] + public void NormalizedByte2_PixelInformation() + { + 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); + 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 ca73a6c1f4..c2a0047d23 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; 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(1f, 0.0f, 1f, 1f)); + NormalizedByte4 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public void AreEqual() [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(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); @@ -59,11 +61,44 @@ public void NormalizedByte4_ToVector4() Assert.Equal(-Vector4.One, new NormalizedByte4(Vector4.One * -1234.0f).ToVector4()); } + [Fact] + public void NormalizedByte4_MinimumStorageCodeDecodesAsNegativeOne() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertNormalizedByte4MinimumStorageCodeDecodesAsNegativeOne, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertNormalizedByte4MinimumStorageCodeDecodesAsNegativeOne() + { + NormalizedByte4 pixel = new() { PackedValue = 0x80808080 }; + + Assert.Equal(-Vector4.One, pixel.ToVector4()); + Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); + + NormalizedByte4[] source = new NormalizedByte4[17]; + Vector4[] native = new Vector4[source.Length]; + Vector4[] scaled = new Vector4[source.Length]; + Array.Fill(source, pixel); + + PixelOperations.Instance.ToVector4(Configuration.Default, source, native); + PixelOperations.Instance.ToVector4(Configuration.Default, source, scaled, PixelConversionModifiers.Scale); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(-Vector4.One, native[i]); + Assert.Equal(Vector4.Zero, scaled[i]); + } + + Vector4[] destructiveSource = new Vector4[source.Length]; + NormalizedByte4[] actualPixels = new NormalizedByte4[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.All(actualPixels, actual => Assert.Equal(0x81818181U, actual.PackedValue)); + } + [Fact] public void NormalizedByte4_ToScaledVector4() { // arrange - var short4 = new NormalizedByte4(-Vector4.One); + NormalizedByte4 short4 = new(-Vector4.One); // act Vector4 actual = short4.ToScaledVector4(); @@ -79,12 +114,11 @@ public void NormalizedByte4_ToScaledVector4() public void NormalizedByte4_FromScaledVector4() { // arrange - var pixel = default(NormalizedByte4); 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 @@ -95,139 +129,147 @@ public void NormalizedByte4_FromScaledVector4() public void NormalizedByte4_FromArgb32() { // arrange - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var normalizedByte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = default(NormalizedByte4); 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 - var byte4 = new NormalizedByte4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + NormalizedByte4 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } + + [Fact] + public void NormalizedByte4_PixelInformation() + { + 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); + 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 70bfa1be71..9cb3aee508 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -32,6 +34,42 @@ public void NormalizedShort2_ToVector2() Assert.Equal(-Vector2.One, new NormalizedShort2(Vector2.One * -1234.0f).ToVector2()); } + [Fact] + public void NormalizedShort2_MinimumStorageCodeDecodesAsNegativeOne() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertNormalizedShort2MinimumStorageCodeDecodesAsNegativeOne, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertNormalizedShort2MinimumStorageCodeDecodesAsNegativeOne() + { + NormalizedShort2 pixel = new() { PackedValue = 0x80008000 }; + Vector4 expectedNative = new(-1F, -1F, 0F, 1F); + Vector4 expectedScaled = new(0F, 0F, 0F, 1F); + + Assert.Equal(expectedNative, pixel.ToVector4()); + Assert.Equal(expectedScaled, pixel.ToScaledVector4()); + + NormalizedShort2[] source = new NormalizedShort2[17]; + Vector4[] native = new Vector4[source.Length]; + Vector4[] scaled = new Vector4[source.Length]; + Array.Fill(source, pixel); + + PixelOperations.Instance.ToVector4(Configuration.Default, source, native); + PixelOperations.Instance.ToVector4(Configuration.Default, source, scaled, PixelConversionModifiers.Scale); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(expectedNative, native[i]); + Assert.Equal(expectedScaled, scaled[i]); + } + + Vector4[] destructiveSource = new Vector4[source.Length]; + Array.Fill(destructiveSource, expectedScaled); + NormalizedShort2[] actualPixels = new NormalizedShort2[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.All(actualPixels, actual => Assert.Equal(0x80018001U, actual.PackedValue)); + } + [Fact] public void NormalizedShort2_ToVector4() { @@ -43,7 +81,7 @@ public void NormalizedShort2_ToVector4() public void NormalizedShort2_ToScaledVector4() { // arrange - var short2 = new NormalizedShort2(-Vector2.One); + NormalizedShort2 short2 = new(-Vector2.One); // act Vector4 actual = short2.ToScaledVector4(); @@ -60,11 +98,10 @@ public void NormalizedShort2_FromScaledVector4() { // arrange Vector4 scaled = new NormalizedShort2(-Vector2.One).ToScaledVector4(); - var short2 = default(NormalizedShort2); - uint expected = 0x80018001; + const uint expected = 0x80018001; // act - short2.FromScaledVector4(scaled); + NormalizedShort2 short2 = NormalizedShort2.FromScaledVector4(scaled); uint actual = short2.PackedValue; // assert @@ -75,13 +112,28 @@ public void NormalizedShort2_FromScaledVector4() public void NormalizedShort2_FromBgra5551() { // arrange - var normalizedShort2 = default(NormalizedShort2); - var expected = new Vector4(1, 1, 0, 1); + 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()); } + + [Fact] + public void NormalizedShort2_PixelInformation() + { + 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); + 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 997c6df82b..c317586e48 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; 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(1f, 0.0f, 1f, 1f)); + NormalizedShort4 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +32,10 @@ public void AreEqual() [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(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); @@ -60,11 +62,44 @@ public void NormalizedShort4_ToVector4() Assert.Equal(-Vector4.One, new NormalizedShort4(Vector4.One * -1234.0f).ToVector4()); } + [Fact] + public void NormalizedShort4_MinimumStorageCodeDecodesAsNegativeOne() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertNormalizedShort4MinimumStorageCodeDecodesAsNegativeOne, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertNormalizedShort4MinimumStorageCodeDecodesAsNegativeOne() + { + NormalizedShort4 pixel = new() { PackedValue = 0x8000800080008000 }; + + Assert.Equal(-Vector4.One, pixel.ToVector4()); + Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); + + NormalizedShort4[] source = new NormalizedShort4[17]; + Vector4[] native = new Vector4[source.Length]; + Vector4[] scaled = new Vector4[source.Length]; + Array.Fill(source, pixel); + + PixelOperations.Instance.ToVector4(Configuration.Default, source, native); + PixelOperations.Instance.ToVector4(Configuration.Default, source, scaled, PixelConversionModifiers.Scale); + + for (int i = 0; i < source.Length; i++) + { + Assert.Equal(-Vector4.One, native[i]); + Assert.Equal(Vector4.Zero, scaled[i]); + } + + Vector4[] destructiveSource = new Vector4[source.Length]; + NormalizedShort4[] actualPixels = new NormalizedShort4[source.Length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.All(actualPixels, actual => Assert.Equal(0x8001800180018001UL, actual.PackedValue)); + } + [Fact] public void NormalizedShort4_ToScaledVector4() { // arrange - var short4 = new NormalizedShort4(Vector4.One); + NormalizedShort4 short4 = new(Vector4.One); // act Vector4 actual = short4.ToScaledVector4(); @@ -80,12 +115,11 @@ public void NormalizedShort4_ToScaledVector4() public void NormalizedShort4_FromScaledVector4() { // arrange - var pixel = default(NormalizedShort4); 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 @@ -96,124 +130,115 @@ public void NormalizedShort4_FromScaledVector4() public void NormalizedShort4_FromArgb32() { // arrange - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = default(NormalizedShort4); 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 - var byte4 = new NormalizedShort4(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - var expected = new Rgba32(Vector4.One); - var actual = default(Rgba32); + NormalizedShort4 pixel = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + Rgba32 expected = new(Vector4.One); // act - byte4.ToRgba32(ref actual); + Rgba32 actual = pixel.ToRgba32(); Assert.Equal(expected, actual); } @@ -222,13 +247,30 @@ public void NormalizedShort4_ToRgba32() public void NormalizedShort4_FromBgra5551() { // arrange - var normalizedShort4 = default(NormalizedShort4); 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()); } + + [Fact] + public void NormalizedShort4_PixelInformation() + { + 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); + 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/PixelAlphaRepresentationTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelAlphaRepresentationTests.cs new file mode 100644 index 0000000000..2cc26e76ca --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelAlphaRepresentationTests.cs @@ -0,0 +1,548 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +/// +/// Verifies scalar and bulk alpha-representation conversions for a pixel format. +/// +/// The pixel format. +[Trait("Category", "PixelFormats")] +public abstract class PixelAlphaRepresentationTests + where TPixel : unmanaged, IPixel +{ + private static readonly Vector4[] UnassociatedScaledVectors = + [ + new(.8F, .4F, .2F, .5F), + new(.15F, .65F, .35F, .25F), + new(.9F, .1F, .7F, 1F), + Vector4.Zero + ]; + + private static readonly Vector4[] OutOfRangeUnassociatedScaledVectors = + [ + new(-.25F, 1.25F, .5F, .75F), + new(1.5F, -.5F, 2F, 1F) + ]; + + [Fact] + public void ScalarScaledConversionsDescribeTheSameLogicalColors() + { + foreach (Vector4 unassociated in UnassociatedScaledVectors) + { + Vector4 associated = Associate(unassociated); + TPixel fromUnassociated = TPixel.FromUnassociatedScaledVector4(unassociated); + TPixel fromAssociated = TPixel.FromAssociatedScaledVector4(associated); + + Assert.Equal(fromUnassociated, fromAssociated); + } + } + + [Fact] + public void ScalarAssociatedVectorsContainPremultipliedRgb() + { + foreach (Vector4 source in UnassociatedScaledVectors) + { + TPixel pixel = TPixel.FromUnassociatedScaledVector4(source); + Vector4 unassociatedScaled = pixel.ToUnassociatedScaledVector4(); + Vector4 expectedAssociatedScaled = Associate(unassociatedScaled); + Vector4 actualAssociatedScaled = pixel.ToAssociatedScaledVector4(); + + if (typeof(TPixel) == typeof(NormalizedByte4P)) + { + // Exhaustive valid-component coverage proves that signed-byte unassociation followed by reassociation can differ by at most two ULP. + AlphaRepresentationTestAssertions.EqualWithinTwoUlps(expectedAssociatedScaled, actualAssociatedScaled); + } + else + { + // Reversing association can introduce one final float rounding step, but this still rejects an unassociated no-op by several orders of magnitude. + AlphaRepresentationTestAssertions.EqualWithinOneUlp(expectedAssociatedScaled, actualAssociatedScaled); + } + + Assert.Equal(BitConverter.SingleToInt32Bits(expectedAssociatedScaled.W), BitConverter.SingleToInt32Bits(actualAssociatedScaled.W)); + + Vector4 unassociatedNative = pixel.ToUnassociatedVector4(); + + if (unassociatedNative == unassociatedScaled) + { + // Native-equals-scaled formats use the same independent oracle; affine-native formats are verified by AffineNativeAlphaRepresentationTests. + AlphaRepresentationTestAssertions.EqualWithinOneUlp(expectedAssociatedScaled, pixel.ToAssociatedVector4()); + } + } + } + + [Fact] + public void ScalarScaledConversionsClampHighAlphaConsistently() + { + Vector4 unassociated = new(.8F, .4F, .2F, 1.5F); + Vector4 associated = Associate(unassociated); + + // Both entry points describe the same logical color, so clamping opacity at the storage boundary must not change RGB differently between them. + Assert.Equal(TPixel.FromUnassociatedScaledVector4(unassociated), TPixel.FromAssociatedScaledVector4(associated)); + } + + [Fact] + public void ScalarNativeConversionsRoundTripTheirDeclaredRepresentations() + { + foreach (Vector4 source in UnassociatedScaledVectors) + { + TPixel pixel = TPixel.FromUnassociatedScaledVector4(source); + TPixel fromUnassociated = TPixel.FromUnassociatedVector4(pixel.ToUnassociatedVector4()); + TPixel fromAssociated = TPixel.FromAssociatedVector4(pixel.ToAssociatedVector4()); + + Assert.Equal(pixel, fromUnassociated); + + if (pixel.ToUnassociatedScaledVector4().W == 0F) + { + // Associated color cannot encode hidden RGB at zero alpha. The round trip must therefore produce the format's transparent black value. + Assert.Equal(TPixel.FromUnassociatedScaledVector4(Vector4.Zero), fromAssociated); + } + else + { + Assert.Equal(pixel, fromAssociated); + } + } + } + + [Fact] + public void ParameterlessVectorConversionsUseTheDeclaredAlphaRepresentation() + { + PixelAlphaRepresentation alphaRepresentation = TPixel.GetPixelTypeInfo().AlphaRepresentation; + + foreach (Vector4 source in UnassociatedScaledVectors) + { + TPixel pixel = TPixel.FromUnassociatedScaledVector4(source); + Vector4 expectedNative = alphaRepresentation == PixelAlphaRepresentation.Associated + ? pixel.ToAssociatedVector4() + : pixel.ToUnassociatedVector4(); + + Vector4 expectedScaled = alphaRepresentation == PixelAlphaRepresentation.Associated + ? pixel.ToAssociatedScaledVector4() + : pixel.ToUnassociatedScaledVector4(); + + TPixel expectedFromNative = alphaRepresentation == PixelAlphaRepresentation.Associated + ? TPixel.FromAssociatedVector4(expectedNative) + : TPixel.FromUnassociatedVector4(expectedNative); + + TPixel expectedFromScaled = alphaRepresentation == PixelAlphaRepresentation.Associated + ? TPixel.FromAssociatedScaledVector4(expectedScaled) + : TPixel.FromUnassociatedScaledVector4(expectedScaled); + + Assert.Equal(expectedNative, pixel.ToVector4()); + Assert.Equal(expectedScaled, pixel.ToScaledVector4()); + Assert.Equal(expectedFromNative, TPixel.FromVector4(expectedNative)); + Assert.Equal(expectedFromScaled, TPixel.FromScaledVector4(expectedScaled)); + } + } + + [Fact] + public void BulkConversionsMatchScalarConversions() + { + const int length = 259; + TPixel[] pixels = new TPixel[length]; + + for (int i = 0; i < pixels.Length; i++) + { + Vector4 source = UnassociatedScaledVectors[i % UnassociatedScaledVectors.Length]; + pixels[i] = TPixel.FromUnassociatedScaledVector4(source); + } + + AssertBulkToMatchesScalar(pixels, static pixel => pixel.ToUnassociatedVector4(), static (operations, source, destination) => operations.ToVector4(Configuration.Default, source, destination, PixelConversionModifiers.UnPremultiply)); + AssertBulkToMatchesScalar(pixels, static pixel => pixel.ToAssociatedVector4(), static (operations, source, destination) => operations.ToVector4(Configuration.Default, source, destination, PixelConversionModifiers.Premultiply)); + AssertBulkToMatchesScalar(pixels, static pixel => pixel.ToUnassociatedScaledVector4(), static (operations, source, destination) => operations.ToVector4(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply)); + AssertBulkToMatchesScalar(pixels, static pixel => pixel.ToAssociatedScaledVector4(), static (operations, source, destination) => operations.ToVector4(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + + AssertBulkFromMatchesScalar(pixels, static pixel => pixel.ToUnassociatedVector4(), static vector => TPixel.FromUnassociatedVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.UnPremultiply)); + AssertBulkFromMatchesScalar(pixels, static pixel => pixel.ToAssociatedVector4(), static vector => TPixel.FromAssociatedVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Premultiply)); + AssertBulkFromMatchesScalar(pixels, static pixel => pixel.ToUnassociatedScaledVector4(), static vector => TPixel.FromUnassociatedScaledVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply)); + AssertBulkFromMatchesScalar(pixels, static pixel => pixel.ToAssociatedScaledVector4(), static vector => TPixel.FromAssociatedScaledVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + } + + [Fact] + public void BulkScaledConversionsClampLikeScalarConversions() + { + Vector4[] associated = new Vector4[OutOfRangeUnassociatedScaledVectors.Length]; + + for (int i = 0; i < associated.Length; i++) + { + associated[i] = Associate(OutOfRangeUnassociatedScaledVectors[i]); + } + + AssertBulkFromMatchesScalar(OutOfRangeUnassociatedScaledVectors, static vector => TPixel.FromUnassociatedScaledVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply)); + AssertBulkFromMatchesScalar(associated, static vector => TPixel.FromAssociatedScaledVector4(vector), static (operations, source, destination) => operations.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void BulkCompandingHonorsRequestedAlphaRepresentationAndRange(bool scaled, bool associated) + { + const int length = 259; + TPixel[] pixels = new TPixel[length]; + + for (int i = 0; i < pixels.Length; i++) + { + pixels[i] = TPixel.FromUnassociatedScaledVector4(UnassociatedScaledVectors[i % UnassociatedScaledVectors.Length]); + } + + PixelConversionModifiers modifiers = PixelConversionModifiers.SRgbCompand + | (scaled ? PixelConversionModifiers.Scale : PixelConversionModifiers.None) + | (associated ? PixelConversionModifiers.Premultiply : PixelConversionModifiers.UnPremultiply); + + Vector4[] expectedVectors = new Vector4[length]; + + for (int i = 0; i < pixels.Length; i++) + { + expectedVectors[i] = scaled ? pixels[i].ToUnassociatedScaledVector4() : pixels[i].ToUnassociatedVector4(); + } + + // Transfer functions operate on straight RGB. Association is therefore applied after expansion on the outbound path. + SRgbCompanding.Expand(expectedVectors); + + if (associated) + { + Numerics.Premultiply(expectedVectors); + } + + Vector4[] actualVectors = new Vector4[length]; + PixelOperations.Instance.ToVector4(Configuration.Default, pixels, actualVectors, modifiers); + + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] expectedPixelSource = [.. expectedVectors]; + + if (associated) + { + Numerics.UnPremultiply(expectedPixelSource); + } + + // Reverse the transfer only after restoring straight RGB, matching the observable modifier order for inbound vectors. + SRgbCompanding.Compress(expectedPixelSource); + + TPixel[] expectedPixels = new TPixel[length]; + + for (int i = 0; i < expectedPixels.Length; i++) + { + expectedPixels[i] = scaled + ? TPixel.FromUnassociatedScaledVector4(expectedPixelSource[i]) + : TPixel.FromUnassociatedVector4(expectedPixelSource[i]); + } + + Vector4[] actualPixelSource = [.. expectedVectors]; + TPixel[] actualPixels = new TPixel[length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, actualPixelSource, actualPixels, modifiers); + + Assert.Equal(expectedPixels, actualPixels); + } + + private static void AssertBulkToMatchesScalar(TPixel[] pixels, Func scalar, BulkToVector4 bulk) + { + Vector4[] expected = new Vector4[pixels.Length]; + Vector4[] actual = new Vector4[pixels.Length + 3]; + Vector4 sentinel = new(.125F, .25F, .5F, .75F); + + for (int i = 0; i < pixels.Length; i++) + { + expected[i] = scalar(pixels[i]); + } + + actual.AsSpan(pixels.Length).Fill(sentinel); + bulk(PixelOperations.Instance, pixels, actual); + + // Bulk conversion is source-length driven and must leave excess destination capacity untouched. + Assert.Equal(expected, actual[..pixels.Length]); + Assert.All(actual[pixels.Length..], vector => Assert.Equal(sentinel, vector)); + } + + private static void AssertBulkFromMatchesScalar(TPixel[] pixels, Func createSource, Func scalar, BulkFromVector4 bulk) + { + Vector4[] source = new Vector4[pixels.Length]; + + for (int i = 0; i < pixels.Length; i++) + { + source[i] = createSource(pixels[i]); + } + + AssertBulkFromMatchesScalar(source, scalar, bulk); + } + + private static void AssertBulkFromMatchesScalar(Vector4[] source, Func scalar, BulkFromVector4 bulk) + { + TPixel[] expected = new TPixel[source.Length]; + TPixel[] actual = new TPixel[source.Length + 3]; + TPixel sentinel = TPixel.FromUnassociatedScaledVector4(new Vector4(.125F, .25F, .5F, .75F)); + + for (int i = 0; i < source.Length; i++) + { + expected[i] = scalar(source[i]); + } + + // From-vector bulk operations are destructive, so preserve the scalar source for diagnostics and future assertions. + Vector4[] destructiveSource = [.. source]; + actual.AsSpan(source.Length).Fill(sentinel); + bulk(PixelOperations.Instance, destructiveSource, actual); + + // A destination may expose spare capacity; the source span still defines how many pixels are written. + Assert.Equal(expected, actual[..source.Length]); + Assert.All(actual[source.Length..], pixel => Assert.Equal(sentinel, pixel)); + } + + private static Vector4 Associate(Vector4 vector) + { + vector.X *= vector.W; + vector.Y *= vector.W; + vector.Z *= vector.W; + return vector; + } + + private delegate void BulkToVector4(PixelOperations operations, ReadOnlySpan source, Span destination); + + private delegate void BulkFromVector4(PixelOperations operations, Span source, Span destination); +} + +/// +/// Verifies the shared bulk vector conversion used by RGB byte formats. +/// +[Trait("Category", "PixelFormats")] +public class RgbaCompatiblePixelOperationsTests +{ + [Fact] + public void BulkConversionsMatchScalarAcrossHardwareWidths() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertBulkConversionsMatchScalar, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void AssertBulkConversionsMatchScalar() + { + AssertBulkConversionsMatchScalarForPixel(); + AssertBulkConversionsMatchScalarForPixel(); + } + + private static void AssertBulkConversionsMatchScalarForPixel() + where TPixel : unmanaged, IPixel + { + const int length = 259; + Vector4 sourceVector = new(.5F, .25F, .125F, 1F); + TPixel expectedPixel = TPixel.FromUnassociatedScaledVector4(sourceVector); + TPixel[] pixels = new TPixel[length]; + Array.Fill(pixels, expectedPixel); + + Vector4 expectedVector = expectedPixel.ToUnassociatedScaledVector4(); + Vector4 vectorSentinel = new(.125F, .5F, .75F, 1F); + Vector4[] vectors = new Vector4[length + 3]; + vectors.AsSpan(length).Fill(vectorSentinel); + + PixelOperations.Instance.ToVector4(Configuration.Default, pixels, vectors, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + Assert.All(vectors[..length], vector => Assert.Equal(expectedVector, vector)); + Assert.All(vectors[length..], vector => Assert.Equal(vectorSentinel, vector)); + + Vector4[] source = new Vector4[length]; + source.AsSpan().Fill(sourceVector); + + TPixel pixelSentinel = TPixel.FromUnassociatedScaledVector4(vectorSentinel); + TPixel[] destination = new TPixel[length + 3]; + destination.AsSpan(length).Fill(pixelSentinel); + + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + + // A 259-pixel source crosses the 128- and 256-bit optimized thresholds while leaving spare destination capacity to detect writes beyond the source length. + Assert.All(destination[..length], pixel => Assert.Equal(expectedPixel, pixel)); + Assert.All(destination[length..], pixel => Assert.Equal(pixelSentinel, pixel)); + } +} + +/// +/// Verifies alpha conversion for formats with distinct native vector contracts. +/// +[Trait("Category", "PixelFormats")] +public class AffineNativeAlphaRepresentationTests +{ + private static readonly Vector4 UnassociatedScaled = new(.8F, .4F, .2F, .5F); + + [Fact] + public void Byte4NativeConversionsUseScaledAlpha() + => AssertNativeConversions(static vector => vector * byte.MaxValue); + + [Fact] + public void Short4NativeConversionsUseScaledAlpha() + => AssertNativeConversions(static vector => (vector * ushort.MaxValue) - new Vector4(32768F)); + + [Fact] + public void NormalizedByte4NativeConversionsUseScaledAlpha() + => AssertSignedNormalizedNativeConversions(static vector => (vector * 2F) - Vector4.One); + + [Fact] + public void NormalizedByte4PNativeConversionsUseScaledAlpha() + => AssertSignedNormalizedNativeConversions(static vector => (vector * 2F) - Vector4.One); + + [Fact] + public void NormalizedShort4NativeConversionsUseScaledAlpha() + => AssertSignedNormalizedNativeConversions(static vector => (vector * 2F) - Vector4.One); + + [Fact] + public void HalfVector4NativeConversionsUseScaledAlpha() + => AssertNativeConversions(static vector => (vector * 131008F) - new Vector4(65504F)); + + [Fact] + public void HalfVector4PNativeConversionsUseScaledAlpha() + => AssertNativeConversions(static vector => (vector * 131008F) - new Vector4(65504F)); + + [Fact] + public void HalfSingleFromAssociatedNativeVectorUsesScaledAlpha() + => AssertAlphaLessNativeFrom(static vector => new Vector4((vector.X * 131008F) - 65504F, 0F, 0F, vector.W)); + + [Fact] + public void HalfVector2FromAssociatedNativeVectorUsesScaledAlpha() + => AssertAlphaLessNativeFrom(static vector => new Vector4((vector.X * 131008F) - 65504F, (vector.Y * 131008F) - 65504F, 0F, vector.W)); + + [Fact] + public void NormalizedByte2FromAssociatedNativeVectorUsesScaledAlpha() + => AssertAlphaLessNativeFrom(static vector => new Vector4((vector.X * 2F) - 1F, (vector.Y * 2F) - 1F, 0F, vector.W)); + + [Fact] + public void NormalizedShort2FromAssociatedNativeVectorUsesScaledAlpha() + => AssertAlphaLessNativeFrom(static vector => new Vector4((vector.X * 2F) - 1F, (vector.Y * 2F) - 1F, 0F, vector.W)); + + [Fact] + public void Short2FromAssociatedNativeVectorUsesScaledAlpha() + => AssertAlphaLessNativeFrom(static vector => new Vector4((vector.X * ushort.MaxValue) - 32768F, (vector.Y * ushort.MaxValue) - 32768F, 0F, vector.W)); + + private static void AssertNativeConversions(Func encodeNative) + where TPixel : unmanaged, IPixel + { + TPixel pixel = TPixel.FromUnassociatedScaledVector4(UnassociatedScaled); + Vector4 unassociatedScaled = pixel.ToUnassociatedScaledVector4(); + Vector4 associatedScaled = Associate(unassociatedScaled); + + // Native alpha can be signed or use an integer component range, so multiplying native RGB by native W would not represent opacity. + AlphaRepresentationTestAssertions.EqualWithinOneUlp(encodeNative(unassociatedScaled), pixel.ToUnassociatedVector4()); + AlphaRepresentationTestAssertions.EqualWithinOneUlp(encodeNative(associatedScaled), pixel.ToAssociatedVector4()); + Assert.Equal(pixel, TPixel.FromUnassociatedVector4(encodeNative(unassociatedScaled))); + Assert.Equal(pixel, TPixel.FromAssociatedVector4(encodeNative(associatedScaled))); + } + + private static void AssertSignedNormalizedNativeConversions(Func encodeNative) + where TPixel : unmanaged, IPixel + { + TPixel pixel = TPixel.FromUnassociatedScaledVector4(UnassociatedScaled); + Vector4 unassociatedScaled = pixel.ToUnassociatedScaledVector4(); + Vector4 associatedScaled = Associate(unassociatedScaled); + + // The 2*x-1 affine map is ill-conditioned around native zero, so a native-space ULP tolerance would not measure conversion accuracy. + // Verify the stored native representation exactly, then verify both inverse paths using independently encoded scaled vectors. + if (TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Associated) + { + Assert.Equal(pixel.ToVector4(), pixel.ToAssociatedVector4()); + } + else + { + Assert.Equal(pixel.ToVector4(), pixel.ToUnassociatedVector4()); + } + + Assert.Equal(pixel, TPixel.FromUnassociatedVector4(encodeNative(unassociatedScaled))); + Assert.Equal(pixel, TPixel.FromAssociatedVector4(encodeNative(associatedScaled))); + } + + private static void AssertAlphaLessNativeFrom(Func encodeNative) + where TPixel : unmanaged, IPixel + { + TPixel expected = TPixel.FromUnassociatedScaledVector4(UnassociatedScaled); + Vector4 associatedScaled = Associate(UnassociatedScaled); + + // Formats with implicit alpha still need to unassociate an associated source before discarding its W component. + Assert.Equal(expected, TPixel.FromAssociatedVector4(encodeNative(associatedScaled))); + } + + private static Vector4 Associate(Vector4 vector) + { + vector.X *= vector.W; + vector.Y *= vector.W; + vector.Z *= vector.W; + return vector; + } +} + +/// +/// Provides exact floating-point bounds for independently ordered alpha-association calculations. +/// +internal static class AlphaRepresentationTestAssertions +{ + /// + /// Verifies that corresponding components differ by no more than one binary32 value. + /// + /// The independently calculated expected vector. + /// The vector produced by the pixel implementation. + internal static void EqualWithinOneUlp(Vector4 expected, Vector4 actual) + { + // One neighboring value is the strict bound for a single independently ordered, correctly rounded binary32 operation. + Assert.InRange(actual.X, MathF.BitDecrement(expected.X), MathF.BitIncrement(expected.X)); + Assert.InRange(actual.Y, MathF.BitDecrement(expected.Y), MathF.BitIncrement(expected.Y)); + Assert.InRange(actual.Z, MathF.BitDecrement(expected.Z), MathF.BitIncrement(expected.Z)); + Assert.InRange(actual.W, MathF.BitDecrement(expected.W), MathF.BitIncrement(expected.W)); + } + + /// + /// Verifies that corresponding components differ by no more than two binary32 values. + /// + /// The independently calculated expected vector. + /// The vector produced by the pixel implementation. + internal static void EqualWithinTwoUlps(Vector4 expected, Vector4 actual) + { + // Two neighboring values are the exhaustive bound for NormalizedByte4P unassociation followed by reassociation. + // Bit increments express that exact bound without introducing a loose decimal tolerance. + Assert.InRange(actual.X, MathF.BitDecrement(MathF.BitDecrement(expected.X)), MathF.BitIncrement(MathF.BitIncrement(expected.X))); + Assert.InRange(actual.Y, MathF.BitDecrement(MathF.BitDecrement(expected.Y)), MathF.BitIncrement(MathF.BitIncrement(expected.Y))); + Assert.InRange(actual.Z, MathF.BitDecrement(MathF.BitDecrement(expected.Z)), MathF.BitIncrement(MathF.BitIncrement(expected.Z))); + Assert.InRange(actual.W, MathF.BitDecrement(MathF.BitDecrement(expected.W)), MathF.BitIncrement(MathF.BitIncrement(expected.W))); + } +} + +public class A8AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Abgr32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Abgr32PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Argb32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Argb32PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgr24AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgr565AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgra32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgra32PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgra4444AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Bgra5551AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Byte4AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class HalfSingleAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class HalfVector2AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class HalfVector4AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class HalfVector4PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class L16AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class L8AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class La16AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class La32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class NormalizedByte2AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class NormalizedByte4AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class NormalizedByte4PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class NormalizedShort2AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class NormalizedShort4AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rg32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgb24AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgb48AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgb96AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgba1010102AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgba128AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgba32AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgba32PAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class RgbaHalfAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class RgbaHalfPAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Rgba64AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class RgbaVectorAlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Short2AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class Short4AlphaRepresentationTests : PixelAlphaRepresentationTests { } +public class RgbaDoubleAlphaRepresentationTests : PixelAlphaRepresentationTests { } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs index 68cdf2d633..7562d8ff6e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -12,26 +13,322 @@ public class PixelBlenderTests { public static TheoryData BlenderMappings = new() { - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelColorBlendingMode.Normal }, - { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelColorBlendingMode.Screen }, - { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelColorBlendingMode.HardLight }, - { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelColorBlendingMode.Overlay }, - { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelColorBlendingMode.Darken }, - { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelColorBlendingMode.Lighten }, - { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelColorBlendingMode.Add }, - { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelColorBlendingMode.Subtract }, - { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelColorBlendingMode.Multiply }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelColorBlendingMode.Normal }, - { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelColorBlendingMode.Screen }, - { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelColorBlendingMode.HardLight }, - { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelColorBlendingMode.Overlay }, - { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelColorBlendingMode.Darken }, - { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelColorBlendingMode.Lighten }, - { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelColorBlendingMode.Add }, - { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelColorBlendingMode.Subtract }, - { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelColorBlendingMode.Multiply }, + { new TestPixel(), DefaultPixelBlenders.NormalSrcOver.GetType(), PixelColorBlendingMode.Normal }, + { new TestPixel(), DefaultPixelBlenders.ScreenSrcOver.GetType(), PixelColorBlendingMode.Screen }, + { new TestPixel(), DefaultPixelBlenders.HardLightSrcOver.GetType(), PixelColorBlendingMode.HardLight }, + { new TestPixel(), DefaultPixelBlenders.OverlaySrcOver.GetType(), PixelColorBlendingMode.Overlay }, + { new TestPixel(), DefaultPixelBlenders.DarkenSrcOver.GetType(), PixelColorBlendingMode.Darken }, + { new TestPixel(), DefaultPixelBlenders.LightenSrcOver.GetType(), PixelColorBlendingMode.Lighten }, + { new TestPixel(), DefaultPixelBlenders.AddSrcOver.GetType(), PixelColorBlendingMode.Add }, + { new TestPixel(), DefaultPixelBlenders.SubtractSrcOver.GetType(), PixelColorBlendingMode.Subtract }, + { new TestPixel(), DefaultPixelBlenders.MultiplySrcOver.GetType(), PixelColorBlendingMode.Multiply }, + { new TestPixel(), DefaultPixelBlenders.NormalSrcOver.GetType(), PixelColorBlendingMode.Normal }, + { new TestPixel(), DefaultPixelBlenders.ScreenSrcOver.GetType(), PixelColorBlendingMode.Screen }, + { new TestPixel(), DefaultPixelBlenders.HardLightSrcOver.GetType(), PixelColorBlendingMode.HardLight }, + { new TestPixel(), DefaultPixelBlenders.OverlaySrcOver.GetType(), PixelColorBlendingMode.Overlay }, + { new TestPixel(), DefaultPixelBlenders.DarkenSrcOver.GetType(), PixelColorBlendingMode.Darken }, + { new TestPixel(), DefaultPixelBlenders.LightenSrcOver.GetType(), PixelColorBlendingMode.Lighten }, + { new TestPixel(), DefaultPixelBlenders.AddSrcOver.GetType(), PixelColorBlendingMode.Add }, + { new TestPixel(), DefaultPixelBlenders.SubtractSrcOver.GetType(), PixelColorBlendingMode.Subtract }, + { new TestPixel(), DefaultPixelBlenders.MultiplySrcOver.GetType(), PixelColorBlendingMode.Multiply }, }; + public static TheoryData BlenderModeMappings + { + get + { + TheoryData data = new(); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Clear, + DefaultPixelBlenders.NormalClear.GetType(), + DefaultPixelBlenders.MultiplyClear.GetType(), + DefaultPixelBlenders.AddClear.GetType(), + DefaultPixelBlenders.SubtractClear.GetType(), + DefaultPixelBlenders.ScreenClear.GetType(), + DefaultPixelBlenders.DarkenClear.GetType(), + DefaultPixelBlenders.LightenClear.GetType(), + DefaultPixelBlenders.OverlayClear.GetType(), + DefaultPixelBlenders.HardLightClear.GetType(), + DefaultPixelBlenders.ColorDodgeClear.GetType(), + DefaultPixelBlenders.ColorBurnClear.GetType(), + DefaultPixelBlenders.SoftLightClear.GetType(), + DefaultPixelBlenders.DifferenceClear.GetType(), + DefaultPixelBlenders.ExclusionClear.GetType(), + DefaultPixelBlenders.HueClear.GetType(), + DefaultPixelBlenders.SaturationClear.GetType(), + DefaultPixelBlenders.ColorClear.GetType(), + DefaultPixelBlenders.LuminosityClear.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Xor, + DefaultPixelBlenders.NormalXor.GetType(), + DefaultPixelBlenders.MultiplyXor.GetType(), + DefaultPixelBlenders.AddXor.GetType(), + DefaultPixelBlenders.SubtractXor.GetType(), + DefaultPixelBlenders.ScreenXor.GetType(), + DefaultPixelBlenders.DarkenXor.GetType(), + DefaultPixelBlenders.LightenXor.GetType(), + DefaultPixelBlenders.OverlayXor.GetType(), + DefaultPixelBlenders.HardLightXor.GetType(), + DefaultPixelBlenders.ColorDodgeXor.GetType(), + DefaultPixelBlenders.ColorBurnXor.GetType(), + DefaultPixelBlenders.SoftLightXor.GetType(), + DefaultPixelBlenders.DifferenceXor.GetType(), + DefaultPixelBlenders.ExclusionXor.GetType(), + DefaultPixelBlenders.HueXor.GetType(), + DefaultPixelBlenders.SaturationXor.GetType(), + DefaultPixelBlenders.ColorXor.GetType(), + DefaultPixelBlenders.LuminosityXor.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Plus, + DefaultPixelBlenders.NormalPlus.GetType(), + DefaultPixelBlenders.MultiplyPlus.GetType(), + DefaultPixelBlenders.AddPlus.GetType(), + DefaultPixelBlenders.SubtractPlus.GetType(), + DefaultPixelBlenders.ScreenPlus.GetType(), + DefaultPixelBlenders.DarkenPlus.GetType(), + DefaultPixelBlenders.LightenPlus.GetType(), + DefaultPixelBlenders.OverlayPlus.GetType(), + DefaultPixelBlenders.HardLightPlus.GetType(), + DefaultPixelBlenders.ColorDodgePlus.GetType(), + DefaultPixelBlenders.ColorBurnPlus.GetType(), + DefaultPixelBlenders.SoftLightPlus.GetType(), + DefaultPixelBlenders.DifferencePlus.GetType(), + DefaultPixelBlenders.ExclusionPlus.GetType(), + DefaultPixelBlenders.HuePlus.GetType(), + DefaultPixelBlenders.SaturationPlus.GetType(), + DefaultPixelBlenders.ColorPlus.GetType(), + DefaultPixelBlenders.LuminosityPlus.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Src, + DefaultPixelBlenders.NormalSrc.GetType(), + DefaultPixelBlenders.MultiplySrc.GetType(), + DefaultPixelBlenders.AddSrc.GetType(), + DefaultPixelBlenders.SubtractSrc.GetType(), + DefaultPixelBlenders.ScreenSrc.GetType(), + DefaultPixelBlenders.DarkenSrc.GetType(), + DefaultPixelBlenders.LightenSrc.GetType(), + DefaultPixelBlenders.OverlaySrc.GetType(), + DefaultPixelBlenders.HardLightSrc.GetType(), + DefaultPixelBlenders.ColorDodgeSrc.GetType(), + DefaultPixelBlenders.ColorBurnSrc.GetType(), + DefaultPixelBlenders.SoftLightSrc.GetType(), + DefaultPixelBlenders.DifferenceSrc.GetType(), + DefaultPixelBlenders.ExclusionSrc.GetType(), + DefaultPixelBlenders.HueSrc.GetType(), + DefaultPixelBlenders.SaturationSrc.GetType(), + DefaultPixelBlenders.ColorSrc.GetType(), + DefaultPixelBlenders.LuminositySrc.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcAtop, + DefaultPixelBlenders.NormalSrcAtop.GetType(), + DefaultPixelBlenders.MultiplySrcAtop.GetType(), + DefaultPixelBlenders.AddSrcAtop.GetType(), + DefaultPixelBlenders.SubtractSrcAtop.GetType(), + DefaultPixelBlenders.ScreenSrcAtop.GetType(), + DefaultPixelBlenders.DarkenSrcAtop.GetType(), + DefaultPixelBlenders.LightenSrcAtop.GetType(), + DefaultPixelBlenders.OverlaySrcAtop.GetType(), + DefaultPixelBlenders.HardLightSrcAtop.GetType(), + DefaultPixelBlenders.ColorDodgeSrcAtop.GetType(), + DefaultPixelBlenders.ColorBurnSrcAtop.GetType(), + DefaultPixelBlenders.SoftLightSrcAtop.GetType(), + DefaultPixelBlenders.DifferenceSrcAtop.GetType(), + DefaultPixelBlenders.ExclusionSrcAtop.GetType(), + DefaultPixelBlenders.HueSrcAtop.GetType(), + DefaultPixelBlenders.SaturationSrcAtop.GetType(), + DefaultPixelBlenders.ColorSrcAtop.GetType(), + DefaultPixelBlenders.LuminositySrcAtop.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcIn, + DefaultPixelBlenders.NormalSrcIn.GetType(), + DefaultPixelBlenders.MultiplySrcIn.GetType(), + DefaultPixelBlenders.AddSrcIn.GetType(), + DefaultPixelBlenders.SubtractSrcIn.GetType(), + DefaultPixelBlenders.ScreenSrcIn.GetType(), + DefaultPixelBlenders.DarkenSrcIn.GetType(), + DefaultPixelBlenders.LightenSrcIn.GetType(), + DefaultPixelBlenders.OverlaySrcIn.GetType(), + DefaultPixelBlenders.HardLightSrcIn.GetType(), + DefaultPixelBlenders.ColorDodgeSrcIn.GetType(), + DefaultPixelBlenders.ColorBurnSrcIn.GetType(), + DefaultPixelBlenders.SoftLightSrcIn.GetType(), + DefaultPixelBlenders.DifferenceSrcIn.GetType(), + DefaultPixelBlenders.ExclusionSrcIn.GetType(), + DefaultPixelBlenders.HueSrcIn.GetType(), + DefaultPixelBlenders.SaturationSrcIn.GetType(), + DefaultPixelBlenders.ColorSrcIn.GetType(), + DefaultPixelBlenders.LuminositySrcIn.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcOut, + DefaultPixelBlenders.NormalSrcOut.GetType(), + DefaultPixelBlenders.MultiplySrcOut.GetType(), + DefaultPixelBlenders.AddSrcOut.GetType(), + DefaultPixelBlenders.SubtractSrcOut.GetType(), + DefaultPixelBlenders.ScreenSrcOut.GetType(), + DefaultPixelBlenders.DarkenSrcOut.GetType(), + DefaultPixelBlenders.LightenSrcOut.GetType(), + DefaultPixelBlenders.OverlaySrcOut.GetType(), + DefaultPixelBlenders.HardLightSrcOut.GetType(), + DefaultPixelBlenders.ColorDodgeSrcOut.GetType(), + DefaultPixelBlenders.ColorBurnSrcOut.GetType(), + DefaultPixelBlenders.SoftLightSrcOut.GetType(), + DefaultPixelBlenders.DifferenceSrcOut.GetType(), + DefaultPixelBlenders.ExclusionSrcOut.GetType(), + DefaultPixelBlenders.HueSrcOut.GetType(), + DefaultPixelBlenders.SaturationSrcOut.GetType(), + DefaultPixelBlenders.ColorSrcOut.GetType(), + DefaultPixelBlenders.LuminositySrcOut.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.Dest, + DefaultPixelBlenders.NormalDest.GetType(), + DefaultPixelBlenders.MultiplyDest.GetType(), + DefaultPixelBlenders.AddDest.GetType(), + DefaultPixelBlenders.SubtractDest.GetType(), + DefaultPixelBlenders.ScreenDest.GetType(), + DefaultPixelBlenders.DarkenDest.GetType(), + DefaultPixelBlenders.LightenDest.GetType(), + DefaultPixelBlenders.OverlayDest.GetType(), + DefaultPixelBlenders.HardLightDest.GetType(), + DefaultPixelBlenders.ColorDodgeDest.GetType(), + DefaultPixelBlenders.ColorBurnDest.GetType(), + DefaultPixelBlenders.SoftLightDest.GetType(), + DefaultPixelBlenders.DifferenceDest.GetType(), + DefaultPixelBlenders.ExclusionDest.GetType(), + DefaultPixelBlenders.HueDest.GetType(), + DefaultPixelBlenders.SaturationDest.GetType(), + DefaultPixelBlenders.ColorDest.GetType(), + DefaultPixelBlenders.LuminosityDest.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestAtop, + DefaultPixelBlenders.NormalDestAtop.GetType(), + DefaultPixelBlenders.MultiplyDestAtop.GetType(), + DefaultPixelBlenders.AddDestAtop.GetType(), + DefaultPixelBlenders.SubtractDestAtop.GetType(), + DefaultPixelBlenders.ScreenDestAtop.GetType(), + DefaultPixelBlenders.DarkenDestAtop.GetType(), + DefaultPixelBlenders.LightenDestAtop.GetType(), + DefaultPixelBlenders.OverlayDestAtop.GetType(), + DefaultPixelBlenders.HardLightDestAtop.GetType(), + DefaultPixelBlenders.ColorDodgeDestAtop.GetType(), + DefaultPixelBlenders.ColorBurnDestAtop.GetType(), + DefaultPixelBlenders.SoftLightDestAtop.GetType(), + DefaultPixelBlenders.DifferenceDestAtop.GetType(), + DefaultPixelBlenders.ExclusionDestAtop.GetType(), + DefaultPixelBlenders.HueDestAtop.GetType(), + DefaultPixelBlenders.SaturationDestAtop.GetType(), + DefaultPixelBlenders.ColorDestAtop.GetType(), + DefaultPixelBlenders.LuminosityDestAtop.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestIn, + DefaultPixelBlenders.NormalDestIn.GetType(), + DefaultPixelBlenders.MultiplyDestIn.GetType(), + DefaultPixelBlenders.AddDestIn.GetType(), + DefaultPixelBlenders.SubtractDestIn.GetType(), + DefaultPixelBlenders.ScreenDestIn.GetType(), + DefaultPixelBlenders.DarkenDestIn.GetType(), + DefaultPixelBlenders.LightenDestIn.GetType(), + DefaultPixelBlenders.OverlayDestIn.GetType(), + DefaultPixelBlenders.HardLightDestIn.GetType(), + DefaultPixelBlenders.ColorDodgeDestIn.GetType(), + DefaultPixelBlenders.ColorBurnDestIn.GetType(), + DefaultPixelBlenders.SoftLightDestIn.GetType(), + DefaultPixelBlenders.DifferenceDestIn.GetType(), + DefaultPixelBlenders.ExclusionDestIn.GetType(), + DefaultPixelBlenders.HueDestIn.GetType(), + DefaultPixelBlenders.SaturationDestIn.GetType(), + DefaultPixelBlenders.ColorDestIn.GetType(), + DefaultPixelBlenders.LuminosityDestIn.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestOut, + DefaultPixelBlenders.NormalDestOut.GetType(), + DefaultPixelBlenders.MultiplyDestOut.GetType(), + DefaultPixelBlenders.AddDestOut.GetType(), + DefaultPixelBlenders.SubtractDestOut.GetType(), + DefaultPixelBlenders.ScreenDestOut.GetType(), + DefaultPixelBlenders.DarkenDestOut.GetType(), + DefaultPixelBlenders.LightenDestOut.GetType(), + DefaultPixelBlenders.OverlayDestOut.GetType(), + DefaultPixelBlenders.HardLightDestOut.GetType(), + DefaultPixelBlenders.ColorDodgeDestOut.GetType(), + DefaultPixelBlenders.ColorBurnDestOut.GetType(), + DefaultPixelBlenders.SoftLightDestOut.GetType(), + DefaultPixelBlenders.DifferenceDestOut.GetType(), + DefaultPixelBlenders.ExclusionDestOut.GetType(), + DefaultPixelBlenders.HueDestOut.GetType(), + DefaultPixelBlenders.SaturationDestOut.GetType(), + DefaultPixelBlenders.ColorDestOut.GetType(), + DefaultPixelBlenders.LuminosityDestOut.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.DestOver, + DefaultPixelBlenders.NormalDestOver.GetType(), + DefaultPixelBlenders.MultiplyDestOver.GetType(), + DefaultPixelBlenders.AddDestOver.GetType(), + DefaultPixelBlenders.SubtractDestOver.GetType(), + DefaultPixelBlenders.ScreenDestOver.GetType(), + DefaultPixelBlenders.DarkenDestOver.GetType(), + DefaultPixelBlenders.LightenDestOver.GetType(), + DefaultPixelBlenders.OverlayDestOver.GetType(), + DefaultPixelBlenders.HardLightDestOver.GetType(), + DefaultPixelBlenders.ColorDodgeDestOver.GetType(), + DefaultPixelBlenders.ColorBurnDestOver.GetType(), + DefaultPixelBlenders.SoftLightDestOver.GetType(), + DefaultPixelBlenders.DifferenceDestOver.GetType(), + DefaultPixelBlenders.ExclusionDestOver.GetType(), + DefaultPixelBlenders.HueDestOver.GetType(), + DefaultPixelBlenders.SaturationDestOver.GetType(), + DefaultPixelBlenders.ColorDestOver.GetType(), + DefaultPixelBlenders.LuminosityDestOver.GetType()); + + AddBlenderModeMappings( + data, + PixelAlphaCompositionMode.SrcOver, + DefaultPixelBlenders.NormalSrcOver.GetType(), + DefaultPixelBlenders.MultiplySrcOver.GetType(), + DefaultPixelBlenders.AddSrcOver.GetType(), + DefaultPixelBlenders.SubtractSrcOver.GetType(), + DefaultPixelBlenders.ScreenSrcOver.GetType(), + DefaultPixelBlenders.DarkenSrcOver.GetType(), + DefaultPixelBlenders.LightenSrcOver.GetType(), + DefaultPixelBlenders.OverlaySrcOver.GetType(), + DefaultPixelBlenders.HardLightSrcOver.GetType(), + DefaultPixelBlenders.ColorDodgeSrcOver.GetType(), + DefaultPixelBlenders.ColorBurnSrcOver.GetType(), + DefaultPixelBlenders.SoftLightSrcOver.GetType(), + DefaultPixelBlenders.DifferenceSrcOver.GetType(), + DefaultPixelBlenders.ExclusionSrcOver.GetType(), + DefaultPixelBlenders.HueSrcOver.GetType(), + DefaultPixelBlenders.SaturationSrcOver.GetType(), + DefaultPixelBlenders.ColorSrcOver.GetType(), + DefaultPixelBlenders.LuminositySrcOver.GetType()); + + return data; + } + } + [Theory] [MemberData(nameof(BlenderMappings))] public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelColorBlendingMode mode) @@ -41,17 +338,507 @@ public void ReturnsCorrectBlender(TestPixel pixel, Type type, Pi Assert.IsType(type, blender); } + [Theory] + [MemberData(nameof(BlenderModeMappings))] + public void ReturnsCorrectBlenderForAllModeCombinations(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode, Type type) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + Assert.IsType(type, blender); + } + + [Fact] + public void BlendFunctionsAreCalledForAllModeCombinations() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + ExerciseAllBlenderModeCombinations, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + [Fact] + public void AssociatedAlphaBlendFunctionsAreCalledForAllModeCombinations() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + ExerciseAllAssociatedAlphaBlenderModeCombinations, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + [Theory] + [InlineData(PixelColorBlendingMode.ColorDodge)] + [InlineData(PixelColorBlendingMode.ColorBurn)] + [InlineData(PixelColorBlendingMode.SoftLight)] + [InlineData(PixelColorBlendingMode.Difference)] + [InlineData(PixelColorBlendingMode.Exclusion)] + [InlineData(PixelColorBlendingMode.Hue)] + [InlineData(PixelColorBlendingMode.Saturation)] + [InlineData(PixelColorBlendingMode.Color)] + [InlineData(PixelColorBlendingMode.Luminosity)] + public void NewSrcOverModesMatchAcrossAlphaRepresentations(PixelColorBlendingMode colorMode) + { + Rgba32 background = new(220, 80, 40, 160); + Rgba32 source = new(20, 180, 120, 96); + PixelBlender straightBlender = PixelOperations.Instance.GetPixelBlender(colorMode, PixelAlphaCompositionMode.SrcOver); + PixelBlender associatedBlender = PixelOperations.Instance.GetPixelBlender(colorMode, PixelAlphaCompositionMode.SrcOver); + + Rgba32 straightResult = straightBlender.Blend(background, source, .625F); + Rgba32P expected = Rgba32P.FromRgba32(straightResult); + Rgba32P actual = associatedBlender.Blend(Rgba32P.FromRgba32(background), Rgba32P.FromRgba32(source), .625F); + + // Associated storage introduces one extra byte quantization, but both paths must describe the same composite. + Assert.True(Math.Abs(expected.R - actual.R) <= 1, $"{colorMode}: expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.G - actual.G) <= 1, $"{colorMode}: expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.B - actual.B) <= 1, $"{colorMode}: expected {expected}, actual {actual}"); + Assert.Equal(expected.A, actual.A); + } + + [Fact] + public void PlusMatchesAcrossAlphaRepresentations() + { + Rgba32 background = new(220, 80, 40, 160); + Rgba32 source = new(20, 180, 120, 96); + PixelBlender straightBlender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Plus); + PixelBlender associatedBlender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Plus); + + Rgba32P expected = Rgba32P.FromRgba32(straightBlender.Blend(background, source, .625F)); + Rgba32P actual = associatedBlender.Blend(Rgba32P.FromRgba32(background), Rgba32P.FromRgba32(source), .625F); + + Assert.True(Math.Abs(expected.R - actual.R) <= 1, $"expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.G - actual.G) <= 1, $"expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.B - actual.B) <= 1, $"expected {expected}, actual {actual}"); + Assert.Equal(expected.A, actual.A); + } + + [Fact] + public void AssociatedHardLightDestAtopRoundsExactMidpointAwayFromZero() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunAssociatedHardLightDestAtopRoundsExactMidpointAwayFromZero, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void RunAssociatedHardLightDestAtopRoundsExactMidpointAwayFromZero() + { + Rgba32P background = Rgba32P.FromRgba32(new Rgba32(220, 80, 40, 160)); + Rgba32P source = Rgba32P.FromRgba32(new Rgba32(20, 180, 120, 96)); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.HardLight, PixelAlphaCompositionMode.DestAtop); + + Assert.Equal(new Rgba32P(30, 33, 16, 60), blender.Blend(background, source, .625F)); + } + + [Fact] + public void AssociatedBlendWithCoverageAppliesCoverageToColorAndAlpha() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunAssociatedBlendWithCoverageAppliesCoverageToColorAndAlpha, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + [Fact] + public void BlendWithCoverageMatchesAcrossAlphaRepresentations() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunBlendWithCoverageMatchesAcrossAlphaRepresentations, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + private static void RunAssociatedBlendWithCoverageAppliesCoverageToColorAndAlpha() + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); + Rgba32P source = Rgba32P.FromRgba32(new Rgba32(37, 211, 89, 173)); + Vector4 sourceScaled = source.ToScaledVector4(); + + // Seven pixels exercise one AVX-512 batch plus three tails, or three AVX2 batches plus one tail. + float[] coverage = [1F, .8F, .6F, .4F, .2F, 0F, .5F]; + float[] amounts = [1F, 1F, 1F, 1F, 1F, 1F, 1F]; + Rgba32P[] background = new Rgba32P[7]; + Rgba32P[] sourceSpan = [source, source, source, source, source, source, source]; + Rgba32P[] destination = new Rgba32P[7]; + Rgba32P[] expected = new Rgba32P[7]; + Vector4[] sourceSpanBuffer = new Vector4[destination.Length * 3]; + Vector4[] constantSourceBuffer = new Vector4[destination.Length * 2]; + + // Compositing over transparency reduces source-over to the source itself, so coverage must + // scale the associated color and alpha lanes together: the premultiplied source times coverage. + // An unassociated coverage lerp instead premultiplies and unassociates around the mix, which + // cancels coverage out of the color lanes entirely at zero backdrop alpha. + for (int i = 0; i < expected.Length; i++) + { + expected[i] = Rgba32P.FromScaledVector4(sourceScaled * coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, destination, background, sourceSpan, 1F, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage, constantSourceBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, background, sourceSpan, amounts, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amounts, coverage, constantSourceBuffer); + Assert.Equal(expected, destination); + } + + private static void RunBlendWithCoverageMatchesAcrossAlphaRepresentations() + { + PixelBlender straightBlender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); + PixelBlender associatedBlender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); + + Rgba32[] background = + [ + new(0, 0, 0, 0), + new(29, 83, 137, 107), + new(255, 255, 255, 255), + new(220, 80, 40, 160), + new(10, 20, 30, 40), + new(40, 200, 100, 192), + new(180, 160, 20, 128), + ]; + + Rgba32 source = new(37, 211, 89, 173); + float[] coverage = [1F, .8F, .6F, .4F, .2F, 0F, .5F]; + + Rgba32[] straightDestination = new Rgba32[background.Length]; + Rgba32P[] associatedDestination = new Rgba32P[background.Length]; + Rgba32P[] associatedBackground = new Rgba32P[background.Length]; + Vector4[] workingBuffer = new Vector4[background.Length * 2]; + + for (int i = 0; i < background.Length; i++) + { + associatedBackground[i] = Rgba32P.FromRgba32(background[i]); + } + + straightBlender.BlendWithCoverage(Configuration.Default, straightDestination, background, source, 1F, coverage, workingBuffer); + associatedBlender.BlendWithCoverage(Configuration.Default, associatedDestination, associatedBackground, Rgba32P.FromRgba32(source), 1F, coverage, workingBuffer); + + // Both destinations must describe the same composite: canonical associated bytes may differ + // by one quantization step per channel while alpha is representation-independent. + for (int i = 0; i < background.Length; i++) + { + Rgba32P expected = Rgba32P.FromRgba32(straightDestination[i]); + Rgba32P actual = associatedDestination[i]; + + Assert.True(Math.Abs(expected.R - actual.R) <= 1, $"[{i}] expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.G - actual.G) <= 1, $"[{i}] expected {expected}, actual {actual}"); + Assert.True(Math.Abs(expected.B - actual.B) <= 1, $"[{i}] expected {expected}, actual {actual}"); + Assert.Equal(expected.A, actual.A); + } + } + + [Fact] + public void Blend_WithConstantSourceAndSingleAmount() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrcOver; + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + + blender.Blend(Configuration.Default, destination, background, source, 1F); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceSingleAmountAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrcOver; + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.Blend(Configuration.Default, destination, background, source, 1F, workingBuffer); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceAndAmountSpan() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrcOver; + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + float[] amount = [1F, 1F]; + + blender.Blend(Configuration.Default, destination, background, source, amount); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithConstantSourceAmountSpanAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrcOver; + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32 source = Color.Blue.ToPixel(); + float[] amount = [1F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.Blend(Configuration.Default, destination, background, source, amount, workingBuffer); + + Assert.Equal(source, destination[0]); + Assert.Equal(source, destination[1]); + } + + [Fact] + public void Blend_WithSourceSpanAmountSpanAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrcOver; + Rgba32[] destination = new Rgba32[2]; + Rgba32[] background = + [ + Color.Red.ToPixel(), + Color.Green.ToPixel() + ]; + + Rgba32[] source = + [ + Color.Blue.ToPixel(), + Color.Yellow.ToPixel() + ]; + + float[] amount = [1F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.Blend(Configuration.Default, destination, background, source, amount, workingBuffer); + + Assert.Equal(source[0], destination[0]); + Assert.Equal(source[1], destination[1]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceAndSingleAmount() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceSingleAmountAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAndSingleAmount() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanSingleAmountAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, 1F, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceAndAmountSpan() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithConstantSourceAmountSpanAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(255, 0, 0), + new(255, 0, 0) + ]; + + Rgba32 source = new(0, 0, 255); + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 2]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 0, 128), destination[1]); + Assert.Equal(source, destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAndAmountSpan() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + + [Fact] + public void BlendWithCoverage_WithSourceSpanAmountSpanAndWorkingBuffer() + { + PixelBlender blender = DefaultPixelBlenders.NormalSrc; + Rgba32[] destination = new Rgba32[3]; + Rgba32[] background = + [ + new(255, 0, 0), + new(0, 255, 0), + new(0, 0, 255) + ]; + + Rgba32[] source = + [ + new(0, 0, 255), + new(255, 0, 0), + new(0, 255, 0) + ]; + + float[] amount = [1F, 1F, 1F]; + float[] coverage = [0F, .5F, 1F]; + Vector4[] workingBuffer = new Vector4[destination.Length * 3]; + + blender.BlendWithCoverage(Configuration.Default, destination, background, source, amount, coverage, workingBuffer); + + Assert.Equal(background[0], destination[0]); + Assert.Equal(new Rgba32(128, 128, 0), destination[1]); + Assert.Equal(source[2], destination[2]); + } + 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 +854,20 @@ public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity 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() }, + { new Rgba32(51, 102, 153, 77), new Rgba32(204, 51, 26, 102), .5F, PixelAlphaCompositionMode.Plus, new Rgba32(112, 82, 102, 128) }, + { new Rgba32(230, 51, 26, 191), new Rgba32(204, 230, 77, 191), 1F, PixelAlphaCompositionMode.Plus, new Rgba32(255, 210, 77, 255) }, }; [Theory] @@ -92,4 +881,293 @@ public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opac // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); } + + private static void AddBlenderModeMappings( + TheoryData data, + PixelAlphaCompositionMode alphaMode, + Type normal, + Type multiply, + Type add, + Type subtract, + Type screen, + Type darken, + Type lighten, + Type overlay, + Type hardLight, + Type colorDodge, + Type colorBurn, + Type softLight, + Type difference, + Type exclusion, + Type hue, + Type saturation, + Type color, + Type luminosity) + { + data.Add(PixelColorBlendingMode.Normal, alphaMode, normal); + data.Add(PixelColorBlendingMode.Multiply, alphaMode, multiply); + data.Add(PixelColorBlendingMode.Add, alphaMode, add); + data.Add(PixelColorBlendingMode.Subtract, alphaMode, subtract); + data.Add(PixelColorBlendingMode.Screen, alphaMode, screen); + data.Add(PixelColorBlendingMode.Darken, alphaMode, darken); + data.Add(PixelColorBlendingMode.Lighten, alphaMode, lighten); + data.Add(PixelColorBlendingMode.Overlay, alphaMode, overlay); + data.Add(PixelColorBlendingMode.HardLight, alphaMode, hardLight); + data.Add(PixelColorBlendingMode.ColorDodge, alphaMode, colorDodge); + data.Add(PixelColorBlendingMode.ColorBurn, alphaMode, colorBurn); + data.Add(PixelColorBlendingMode.SoftLight, alphaMode, softLight); + data.Add(PixelColorBlendingMode.Difference, alphaMode, difference); + data.Add(PixelColorBlendingMode.Exclusion, alphaMode, exclusion); + data.Add(PixelColorBlendingMode.Hue, alphaMode, hue); + data.Add(PixelColorBlendingMode.Saturation, alphaMode, saturation); + data.Add(PixelColorBlendingMode.Color, alphaMode, color); + data.Add(PixelColorBlendingMode.Luminosity, alphaMode, luminosity); + } + + private static void ExerciseAllBlenderModeCombinations() + { + foreach (PixelAlphaCompositionMode alphaMode in Enum.GetValues()) + { + foreach (PixelColorBlendingMode colorMode in Enum.GetValues()) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + ExerciseBlender(blender); + } + } + } + + private static void ExerciseAllAssociatedAlphaBlenderModeCombinations() + { + Rgba32P[] background = + [ + Rgba32P.FromRgba32(new Rgba32(220, 80, 40, 160)), + Rgba32P.FromRgba32(new Rgba32(40, 200, 100, 192)), + Rgba32P.FromRgba32(new Rgba32(120, 60, 230, 224)), + Rgba32P.FromRgba32(new Rgba32(180, 160, 20, 128)), + Rgba32P.FromRgba32(new Rgba32(30, 140, 210, 96)), + ]; + + Rgba32P[] source = + [ + Rgba32P.FromRgba32(new Rgba32(20, 180, 120, 96)), + Rgba32P.FromRgba32(new Rgba32(210, 30, 150, 144)), + Rgba32P.FromRgba32(new Rgba32(80, 220, 40, 176)), + Rgba32P.FromRgba32(new Rgba32(240, 110, 60, 208)), + Rgba32P.FromRgba32(new Rgba32(100, 50, 200, 112)), + ]; + + foreach (PixelAlphaCompositionMode alphaMode in Enum.GetValues()) + { + foreach (PixelColorBlendingMode colorMode in Enum.GetValues()) + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(colorMode, alphaMode); + AssertAssociatedBlenderMatchesScalar(blender, background, source, colorMode, alphaMode); + } + } + } + + private static void AssertAssociatedBlenderMatchesScalar( + PixelBlender blender, + Rgba32P[] associatedBackground, + Rgba32P[] associatedSource, + PixelColorBlendingMode colorMode, + PixelAlphaCompositionMode alphaMode) + { + const float amount = .625F; + float[] amounts = [.125F, .375F, .625F, .875F, 1F]; + float[] coverage = [.2F, .4F, .6F, .8F, 1F]; + Rgba32P constantAssociatedSource = associatedSource[2]; + + Rgba32P[] expected = new Rgba32P[associatedBackground.Length]; + Rgba32P[] actual = new Rgba32P[associatedBackground.Length]; + Vector4[] actualSourceSpanBuffer = new Vector4[associatedBackground.Length * 3]; + Vector4[] actualConstantSourceBuffer = new Vector4[associatedBackground.Length * 2]; + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], associatedSource[i], amount); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, associatedSource, amount, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanSingleAmount"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], constantAssociatedSource, amount); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amount, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceSingleAmount"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], associatedSource[i], amounts[i]); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, associatedSource, amounts, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanAmountSpan"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(associatedBackground[i], constantAssociatedSource, amounts[i]); + } + + blender.Blend(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amounts, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceAmountSpan"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], associatedSource[i], amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, associatedSource, amount, coverage, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanSingleAmountCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], constantAssociatedSource, amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amount, coverage, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceSingleAmountCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], associatedSource[i], amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, associatedSource, amounts, coverage, actualSourceSpanBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "SourceSpanAmountSpanCoverage"); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, associatedBackground[i], constantAssociatedSource, amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, actual, associatedBackground, constantAssociatedSource, amounts, coverage, actualConstantSourceBuffer); + AssertRgba32PEqual(expected, actual, colorMode, alphaMode, "ConstantSourceAmountSpanCoverage"); + } + + private static Rgba32P BlendWithCoverageScalar(PixelBlender blender, Rgba32P background, Rgba32P source, float amount, float coverage) + { + Span destination = stackalloc Rgba32P[1]; + Span backgroundSpan = stackalloc Rgba32P[1] { background }; + Span sourceSpan = stackalloc Rgba32P[1] { source }; + Span coverageSpan = stackalloc float[1] { coverage }; + Span buffer = stackalloc Vector4[3]; + + // A one-pixel span takes the scalar remainder path, providing an exact oracle for each SIMD coverage result. + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverageSpan, buffer); + return destination[0]; + } + + private static void AssertRgba32PEqual( + ReadOnlySpan expected, + ReadOnlySpan actual, + PixelColorBlendingMode colorMode, + PixelAlphaCompositionMode alphaMode, + string scenario) + { + for (int i = 0; i < expected.Length; i++) + { + Assert.True(expected[i] == actual[i], $"{colorMode}/{alphaMode}/{scenario}[{i}]: expected {expected[i]}, actual {actual[i]}"); + } + } + + private static void ExerciseBlender(PixelBlender blender) + { + Rgba32 background = Color.MistyRose.ToPixel(); + Rgba32 source = Color.MidnightBlue.ToPixel(); + + ExerciseBlender(blender, background, source); + } + + /// + /// Exercises every bulk overload across vector-width boundaries and compares it with single-pixel composition. + /// + /// The pixel format. + /// The blender under test. + /// The background pixel. + /// The source pixel. + private static void ExerciseBlender(PixelBlender blender, TPixel background, TPixel source) + where TPixel : unmanaged, IPixel + { + const float amount = .625F; + + float[] amounts = [0F, .125F, .375F, .625F, .875F, 1F, .5F]; + float[] coverage = [1F, .8F, .6F, .4F, .2F, 0F, .5F]; + + // Seven pixels exercise one AVX-512 batch plus three tails, or three AVX2 batches plus one tail. + TPixel[] destination = new TPixel[7]; + TPixel[] expected = new TPixel[7]; + TPixel[] backgroundSpan = [background, background, background, background, background, background, background]; + TPixel[] sourceSpan = [source, source, source, source, source, source, source]; + Vector4[] sourceSpanBuffer = new Vector4[destination.Length * 3]; + Vector4[] constantSourceBuffer = new Vector4[destination.Length * 2]; + + Array.Fill(expected, blender.Blend(background, source, amount)); + + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.Blend(Configuration.Default, destination, backgroundSpan, source, amount, constantSourceBuffer); + Assert.Equal(expected, destination); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = blender.Blend(background, source, amounts[i]); + } + + blender.Blend(Configuration.Default, destination, backgroundSpan, sourceSpan, amounts, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.Blend(Configuration.Default, destination, backgroundSpan, source, amounts, constantSourceBuffer); + Assert.Equal(expected, destination); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, background, source, amount, coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, amount, coverage, constantSourceBuffer); + Assert.Equal(expected, destination); + + for (int i = 0; i < expected.Length; i++) + { + expected[i] = BlendWithCoverageScalar(blender, background, source, amounts[i], coverage[i]); + } + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amounts, coverage, sourceSpanBuffer); + Assert.Equal(expected, destination); + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, source, amounts, coverage, constantSourceBuffer); + Assert.Equal(expected, destination); + } + + /// + /// Computes a one-pixel coverage result through the scalar remainder path. + /// + /// The pixel format. + /// The blender under test. + /// The background pixel. + /// The source pixel. + /// The source opacity. + /// The pixel coverage. + /// The blended pixel. + private static TPixel BlendWithCoverageScalar(PixelBlender blender, TPixel background, TPixel source, float amount, float coverage) + where TPixel : unmanaged, IPixel + { + Span destination = stackalloc TPixel[1]; + Span backgroundSpan = stackalloc TPixel[1] { background }; + Span sourceSpan = stackalloc TPixel[1] { source }; + Span coverageSpan = stackalloc float[1] { coverage }; + Span buffer = stackalloc Vector4[3]; + + blender.BlendWithCoverage(Configuration.Default, destination, backgroundSpan, sourceSpan, amount, coverageSpan, buffer); + + return destination[0]; + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 1086afe76d..f8c296846b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -59,7 +59,7 @@ TestImageProvider provider FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX, provider, mode.ToString()); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 976a272ebf..d2ca3f12c4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -4,7 +4,7 @@ using System.Numerics; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -using Castle.Components.DictionaryAdapter; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -45,6 +45,22 @@ public void NormalBlendFunction256(TestVector4 back, TestVector4 source, float a Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.NormalSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData MultiplyFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -77,6 +93,22 @@ public void MultiplyFunction256(TestVector4 back, TestVector4 source, float amou Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.MultiplySrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData AddFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -109,6 +141,22 @@ public void AddFunction256(TestVector4 back, TestVector4 source, float amount, T Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.AddSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData SubtractFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(0, 0, 0, 1) }, @@ -141,6 +189,22 @@ public void SubtractFunction256(TestVector4 back, TestVector4 source, float amou Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(SubtractFunctionData))] + public void SubtractFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.SubtractSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData ScreenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -173,6 +237,22 @@ public void ScreenFunction256(TestVector4 back, TestVector4 source, float amount Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.ScreenSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData DarkenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -205,6 +285,22 @@ public void DarkenFunction256(TestVector4 back, TestVector4 source, float amount Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.DarkenSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData LightenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -237,6 +333,22 @@ public void LightenFunction256(TestVector4 back, TestVector4 source, float amoun Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.LightenSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData OverlayFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -269,6 +381,22 @@ public void OverlayFunction256(TestVector4 back, TestVector4 source, float amoun Assert.Equal(expected256, actual, FloatComparer); } + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.OverlaySrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + public static TheoryData HardLightFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, @@ -300,4 +428,154 @@ public void HardLightFunction256(TestVector4 back, TestVector4 source, float amo Vector256 actual = PorterDuffFunctions.HardLightSrcOver(back256, source256, Vector256.Create(amount)); Assert.Equal(expected256, actual, FloatComparer); } + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction512(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 back512 = CreateVector512(back); + Vector512 source512 = CreateVector512(source); + Vector512 expected512 = CreateVector512(expected); + Vector512 actual = PorterDuffFunctions.HardLightSrcOver(back512, source512, Vector512.Create(amount)); + Assert.Equal(expected512, actual, FloatComparer); + } + + public static TheoryData ExtendedBlendFunctionData { get; } = new() + { + { PixelColorBlendingMode.ColorDodge, CreateTestVector(0F, .25F, .8F, 1F), CreateTestVector(1F, .5F, .5F, 1F), CreateTestVector(0F, .5F, 1F, 1F) }, + { PixelColorBlendingMode.ColorBurn, CreateTestVector(1F, .4F, .75F, 1F), CreateTestVector(0F, 0F, .5F, 1F), CreateTestVector(1F, 0F, .5F, 1F) }, + { PixelColorBlendingMode.SoftLight, CreateTestVector(.25F, .64F, .16F, 1F), CreateTestVector(.75F, .75F, .25F, 1F), CreateTestVector(.375F, .72F, .0928F, 1F) }, + { PixelColorBlendingMode.SoftLight, CreateTestVector(.25F, .36F, .81F, 1F), CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.25F, .36F, .81F, 1F) }, + { PixelColorBlendingMode.Difference, CreateTestVector(.1F, .8F, .4F, 1F), CreateTestVector(.7F, .2F, .4F, 1F), CreateTestVector(.6F, .6F, 0F, 1F) }, + { PixelColorBlendingMode.Exclusion, CreateTestVector(.1F, .8F, .4F, 1F), CreateTestVector(.7F, .2F, .4F, 1F), CreateTestVector(.66F, .68F, .48F, 1F) }, + { PixelColorBlendingMode.Hue, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.832625F, .332625F, .645125F, 1F) }, + { PixelColorBlendingMode.Saturation, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.0098F, .8098F, .3298F, 1F) }, + { PixelColorBlendingMode.Color, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(1F, .234851485F, .713069307F, 1F) }, + { PixelColorBlendingMode.Luminosity, CreateTestVector(.2F, .7F, .4F, 1F), CreateTestVector(.9F, .1F, .6F, 1F), CreateTestVector(.078F, .578F, .278F, 1F) }, + { PixelColorBlendingMode.Hue, CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.9F, .2F, .6F, 1F), CreateTestVector(.5F, .5F, .5F, 1F) }, + { PixelColorBlendingMode.Saturation, CreateTestVector(.5F, .5F, .5F, 1F), CreateTestVector(.9F, .2F, .6F, 1F), CreateTestVector(.5F, .5F, .5F, 1F) }, + }; + + [Theory] + [MemberData(nameof(ExtendedBlendFunctionData))] + public void ExtendedBlendFunction(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected) + { + Vector4 actual = InvokeExtendedBlend(mode, back, source, 1F); + VectorAssert.Equal(expected, actual, 4); + } + + [Theory] + [MemberData(nameof(ExtendedBlendFunctionData))] + public void ExtendedBlendFunction256(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 actual = InvokeExtendedBlend(mode, Vector256.Create(back.AsVector().AsVector128(), back.AsVector().AsVector128()), Vector256.Create(source.AsVector().AsVector128(), source.AsVector().AsVector128()), Vector256.Create(1F)); + Vector256 expectedVector = Vector256.Create(expected.AsVector().AsVector128(), expected.AsVector().AsVector128()); + Assert.Equal(expectedVector, actual, FloatComparer); + } + + [Theory] + [MemberData(nameof(ExtendedBlendFunctionData))] + public void ExtendedBlendFunction512(PixelColorBlendingMode mode, TestVector4 back, TestVector4 source, TestVector4 expected) + { + if (!Avx512F.IsSupported) + { + return; + } + + Vector512 actual = InvokeExtendedBlend(mode, CreateVector512(back), CreateVector512(source), Vector512.Create(1F)); + Assert.Equal(CreateVector512(expected), actual, FloatComparer); + } + + /// + /// Creates serializable vector test data without relying on , + /// which assigns Z from X and can hide channel-specific regressions. + /// + /// The X component. + /// The Y component. + /// The Z component. + /// The W component. + /// The test vector. + private static TestVector4 CreateTestVector(float x, float y, float z, float w) + => new() { X = x, Y = y, Z = z, W = w }; + + /// + /// Invokes a new blend mode through its straight-alpha source-over function. + /// + /// The color blending mode. + /// The backdrop vector. + /// The source vector. + /// The source amount. + /// The composition result. + private static Vector4 InvokeExtendedBlend(PixelColorBlendingMode mode, Vector4 backdrop, Vector4 source, float amount) + => mode switch + { + PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount), + PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount), + PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount), + _ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount), + }; + + /// + private static Vector256 InvokeExtendedBlend(PixelColorBlendingMode mode, Vector256 backdrop, Vector256 source, Vector256 amount) + => mode switch + { + PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount), + PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount), + PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount), + _ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount), + }; + + /// + private static Vector512 InvokeExtendedBlend(PixelColorBlendingMode mode, Vector512 backdrop, Vector512 source, Vector512 amount) + => mode switch + { + PixelColorBlendingMode.ColorDodge => PorterDuffFunctions.ColorDodgeSrcOver(backdrop, source, amount), + PixelColorBlendingMode.ColorBurn => PorterDuffFunctions.ColorBurnSrcOver(backdrop, source, amount), + PixelColorBlendingMode.SoftLight => PorterDuffFunctions.SoftLightSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Difference => PorterDuffFunctions.DifferenceSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Exclusion => PorterDuffFunctions.ExclusionSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Hue => PorterDuffFunctions.HueSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Saturation => PorterDuffFunctions.SaturationSrcOver(backdrop, source, amount), + PixelColorBlendingMode.Color => PorterDuffFunctions.ColorSrcOver(backdrop, source, amount), + _ => PorterDuffFunctions.LuminositySrcOver(backdrop, source, amount), + }; + + private static Vector512 CreateVector512(TestVector4 vector) + => Vector512.Create( + vector.X, + vector.Y, + vector.Z, + vector.W, + vector.X, + vector.Y, + vector.Z, + vector.W, + vector.X, + vector.Y, + vector.Z, + vector.W, + vector.X, + vector.Y, + vector.Z, + vector.W); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTestsTPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTestsTPixel.cs index d375a74c67..a2d571ad3e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTestsTPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTestsTPixel.cs @@ -9,13 +9,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders; public class PorterDuffFunctionsTestsTPixel { + private const int BulkBlendCount = 4; + private static Span AsSpan(T value) where T : struct { return new Span(new[] { value }); } - public static TheoryData NormalBlendFunctionData = new TheoryData + private static T[] CreateFilledArray(T value) + { + T[] values = new T[BulkBlendCount]; + values.AsSpan().Fill(value); + return values; + } + + public static TheoryData NormalBlendFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) } @@ -37,7 +46,7 @@ public void NormalBlendFunction(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.NormalSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -46,12 +55,17 @@ public void NormalBlendFunctionBlender(TestPixel back, TestPixel public void NormalBlendFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.NormalSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.NormalSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData MultiplyFunctionData = new TheoryData + public static TheoryData MultiplyFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, @@ -77,7 +91,7 @@ public void MultiplyFunction(TestPixel back, TestPixel s public void MultiplyFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.MultiplySrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -86,12 +100,17 @@ public void MultiplyFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.MultiplySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.MultiplySrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData AddFunctionData = new TheoryData + public static TheoryData AddFunctionData = new() { { new TestPixel(1, 1, 1, 1), @@ -127,7 +146,7 @@ public void AddFunction(TestPixel back, TestPixel source public void AddFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.AddSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -136,12 +155,17 @@ public void AddFunctionBlender(TestPixel back, TestPixel public void AddFunctionBlenderBulk(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.AddSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.AddSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData SubtractFunctionData = new TheoryData + public static TheoryData SubtractFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(0, 0, 0, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(1, 1, 1, 1f) }, @@ -167,7 +191,7 @@ public void SubtractFunction(TestPixel back, TestPixel s public void SubtractFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.SubtractSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -176,12 +200,17 @@ public void SubtractFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.SubtractSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.SubtractSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData ScreenFunctionData = new TheoryData + public static TheoryData ScreenFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(1, 1, 1, 1f) }, @@ -207,7 +236,7 @@ public void ScreenFunction(TestPixel back, TestPixel sou public void ScreenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.ScreenSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -216,12 +245,17 @@ public void ScreenFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.ScreenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.ScreenSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData DarkenFunctionData = new TheoryData + public static TheoryData DarkenFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(.6f, .6f, .6f, 1f) }, @@ -247,7 +281,7 @@ public void DarkenFunction(TestPixel back, TestPixel sou public void DarkenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.DarkenSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -256,12 +290,17 @@ public void DarkenFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.DarkenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.DarkenSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData LightenFunctionData = new TheoryData + public static TheoryData LightenFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(1, 1, 1, 1f) }, @@ -287,7 +326,7 @@ public void LightenFunction(TestPixel back, TestPixel so public void LightenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.LightenSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -296,12 +335,17 @@ public void LightenFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.LightenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.LightenSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData OverlayFunctionData = new TheoryData + public static TheoryData OverlayFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(1, 1, 1, 1f) }, @@ -327,7 +371,7 @@ public void OverlayFunction(TestPixel back, TestPixel so public void OverlayFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.OverlaySrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -336,12 +380,17 @@ public void OverlayFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.OverlaySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.OverlaySrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } - public static TheoryData HardLightFunctionData = new TheoryData + public static TheoryData HardLightFunctionData = new() { { new TestPixel(1, 1, 1, 1), new TestPixel(1, 1, 1, 1), 1, new TestPixel(1, 1, 1, 1) }, { new TestPixel(1, 1, 1, 1), new TestPixel(0, 0, 0, .8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1f) }, @@ -367,7 +416,7 @@ public void HardLightFunction(TestPixel back, TestPixel public void HardLightFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + TPixel actual = DefaultPixelBlenders.HardLightSrcOver.Blend(back.AsPixel(), source.AsPixel(), amount); VectorAssert.Equal(expected.AsPixel(), actual, 2); } @@ -376,8 +425,13 @@ public void HardLightFunctionBlender(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : unmanaged, IPixel { - var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.HardLightSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected.AsPixel(), dest[0], 2); + TPixel[] dest = new TPixel[BulkBlendCount]; + DefaultPixelBlenders.HardLightSrcOver.Blend(this.Configuration, dest, CreateFilledArray(back.AsPixel()), CreateFilledArray(source.AsPixel()), CreateFilledArray(amount)); + + TPixel expectedPixel = expected.AsPixel(); + foreach (TPixel pixel in dest) + { + VectorAssert.Equal(expectedPixel, pixel, 2); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs new file mode 100644 index 0000000000..05fd25cca9 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelColorTypeTests.cs @@ -0,0 +1,333 @@ +// 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_Exponent_ShouldBeSet() + { + 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_Binary_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Binary; + Assert.True(colorType.HasFlag(PixelColorType.Binary)); + } + + [Fact] + public void PixelColorType_Indexed_ShouldBeSet() + { + const PixelColorType colorType = PixelColorType.Indexed; + Assert.True(colorType.HasFlag(PixelColorType.Indexed)); + } + + [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_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.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)); + 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.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)); + 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.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)); + 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.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)); + 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.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)); + Assert.False(colorType.HasFlag(PixelColorType.Yellow)); + 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() + { + 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.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)); + 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_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.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)); + 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)); + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index 006cb9eb61..dbff541407 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -1,6 +1,7 @@ // 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; @@ -14,7 +15,7 @@ public static class ReferenceImplementations { 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 +30,7 @@ public static byte[] MakeRgba32ByteArray(byte r, byte g, byte b, byte a) 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 +45,7 @@ public static byte[] MakeArgb32ByteArray(byte r, byte g, byte b, byte a) 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 +60,7 @@ public static byte[] MakeBgra32ByteArray(byte r, byte g, byte b, byte a) 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) { @@ -73,13 +74,11 @@ public static byte[] MakeAbgr32ByteArray(byte r, byte g, byte b, byte a) } internal static void To( - Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) where TSourcePixel : unmanaged, IPixel where TDestinationPixel : unmanaged, IPixel { - Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); int count = sourcePixels.Length; @@ -87,49 +86,19 @@ internal static void To( 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.ConvertFromRgbaScaledVector4(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 + // Scalar source and destination boundaries are the reference for the bulk operation: each format owns any representation conversion and destination quantization it requires. 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()); + Vector4 vector = sp.ToUnassociatedScaledVector4(); + dp = TDestinationPixel.FromUnassociatedScaledVector4(vector); } } } 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 5ba5c1bedf..3747b41eeb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs @@ -7,12 +7,11 @@ using Xunit; using Xunit.Abstractions; - namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; public partial class PixelOperationsTests { - + public partial class A8_OperationsTests : PixelOperationsTests { public A8_OperationsTests(ITestOutputHelper output) @@ -20,19 +19,14 @@ public A8_OperationsTests(ITestOutputHelper output) { } - 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 +34,29 @@ public Argb32_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => Argb32.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Argb32.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class Argb32P_OperationsTests : PixelOperationsTests + { + public Argb32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = Argb32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class Abgr32_OperationsTests : PixelOperationsTests { public Abgr32_OperationsTests(ITestOutputHelper output) @@ -60,19 +64,29 @@ public Abgr32_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => Abgr32.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Abgr32.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class Abgr32P_OperationsTests : PixelOperationsTests + { + public Abgr32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = Abgr32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class Bgr24_OperationsTests : PixelOperationsTests { public Bgr24_OperationsTests(ITestOutputHelper output) @@ -80,19 +94,14 @@ public Bgr24_OperationsTests(ITestOutputHelper output) { } - 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 +109,14 @@ public Bgr565_OperationsTests(ITestOutputHelper output) { } - 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 +124,29 @@ public Bgra32_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => Bgra32.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Bgra32.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class Bgra32P_OperationsTests : PixelOperationsTests + { + public Bgra32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = Bgra32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class Bgra4444_OperationsTests : PixelOperationsTests { public Bgra4444_OperationsTests(ITestOutputHelper output) @@ -140,19 +154,14 @@ public Bgra4444_OperationsTests(ITestOutputHelper output) { } - 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 +169,14 @@ public Bgra5551_OperationsTests(ITestOutputHelper output) { } - 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 +184,14 @@ public Byte4_OperationsTests(ITestOutputHelper output) { } - 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 +199,14 @@ public HalfSingle_OperationsTests(ITestOutputHelper output) { } - 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 +214,14 @@ public HalfVector2_OperationsTests(ITestOutputHelper output) { } - 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 +229,29 @@ public HalfVector4_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => HalfVector4.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = HalfVector4.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class HalfVector4P_OperationsTests : PixelOperationsTests + { + public HalfVector4P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = HalfVector4P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class L16_OperationsTests : PixelOperationsTests { public L16_OperationsTests(ITestOutputHelper output) @@ -260,19 +259,14 @@ public L16_OperationsTests(ITestOutputHelper output) { } - 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 +274,14 @@ public L8_OperationsTests(ITestOutputHelper output) { } - 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 +289,14 @@ public La16_OperationsTests(ITestOutputHelper output) { } - 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 +304,14 @@ public La32_OperationsTests(ITestOutputHelper output) { } - 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 +319,14 @@ public NormalizedByte2_OperationsTests(ITestOutputHelper output) { } - 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 +334,29 @@ public NormalizedByte4_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => NormalizedByte4.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = NormalizedByte4.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class NormalizedByte4P_OperationsTests : PixelOperationsTests + { + public NormalizedByte4P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = NormalizedByte4P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class NormalizedShort2_OperationsTests : PixelOperationsTests { public NormalizedShort2_OperationsTests(ITestOutputHelper output) @@ -380,19 +364,14 @@ public NormalizedShort2_OperationsTests(ITestOutputHelper output) { } - 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 +379,14 @@ public NormalizedShort4_OperationsTests(ITestOutputHelper output) { } - 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 +394,14 @@ public Rg32_OperationsTests(ITestOutputHelper output) { } - 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 +409,14 @@ public Rgb24_OperationsTests(ITestOutputHelper output) { } - 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 +424,14 @@ public Rgb48_OperationsTests(ITestOutputHelper output) { } - 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 +439,14 @@ public Rgba1010102_OperationsTests(ITestOutputHelper output) { } - 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 +454,29 @@ public Rgba32_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => Rgba32.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Rgba32.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class Rgba32P_OperationsTests : PixelOperationsTests + { + public Rgba32P_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; - Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + var alphaRepresentation = Rgba32P.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); } } - + public partial class Rgba64_OperationsTests : PixelOperationsTests { public Rgba64_OperationsTests(ITestOutputHelper output) @@ -520,39 +484,59 @@ public Rgba64_OperationsTests(ITestOutputHelper output) { } - protected override PixelOperations Operations => Rgba64.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = Rgba64.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } + + public partial class RgbaHalf_OperationsTests : PixelOperationsTests + { + public RgbaHalf_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [Fact] public void PixelTypeInfoHasCorrectAlphaRepresentation() { - var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + var alphaRepresentation = RgbaHalf.GetPixelTypeInfo().AlphaRepresentation; Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - - public partial class RgbaVector_OperationsTests : PixelOperationsTests + + public partial class RgbaHalfP_OperationsTests : PixelOperationsTests { - public RgbaVector_OperationsTests(ITestOutputHelper output) + public RgbaHalfP_OperationsTests(ITestOutputHelper output) : base(output) { } - protected override PixelOperations Operations => RgbaVector.PixelOperations.Instance; - [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = RgbaHalfP.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Associated, alphaRepresentation); + } + } + + public partial class RgbaVector_OperationsTests : PixelOperationsTests + { + public RgbaVector_OperationsTests(ITestOutputHelper output) + : base(output) + { + } [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 +544,14 @@ public Short2_OperationsTests(ITestOutputHelper output) { } - 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 +559,10 @@ public Short4_OperationsTests(ITestOutputHelper output) { } - 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 0e7b1f3354..e4a7e881e6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude @@ -13,8 +13,8 @@ using Xunit; using Xunit.Abstractions; <#+ private static readonly string[] UnassociatedAlphaPixelTypes = - { - "A8", + [ + "A8", "Argb32", "Abgr32", "Bgra32", @@ -29,32 +29,47 @@ using Xunit.Abstractions; "Rgba1010102", "Rgba32", "Rgba64", + "RgbaHalf", "RgbaVector", "Short4" - }; + ]; - private static readonly string[] AssociatedAlphaPixelTypes = Array.Empty(); + private static readonly string[] AssociatedAlphaPixelTypes = + [ + "Argb32P", + "Abgr32P", + "Bgra32P", + "HalfVector4P", + "NormalizedByte4P", + "Rgba32P", + "RgbaHalfP" + ]; private static readonly string[] CommonPixelTypes = - { - "A8", + [ + "A8", "Argb32", + "Argb32P", "Abgr32", + "Abgr32P", "Bgr24", "Bgr565", "Bgra32", + "Bgra32P", "Bgra4444", "Bgra5551", "Byte4", "HalfSingle", "HalfVector2", "HalfVector4", + "HalfVector4P", "L16", "L8", "La16", "La32", "NormalizedByte2", "NormalizedByte4", + "NormalizedByte4P", "NormalizedShort2", "NormalizedShort4", "Rg32", @@ -62,15 +77,18 @@ using Xunit.Abstractions; "Rgb48", "Rgba1010102", "Rgba32", + "Rgba32P", "Rgba64", + "RgbaHalf", + "RgbaHalfP", "RgbaVector", "Short2", - "Short4", - }; + "Short4" + ]; void GenerateSpecializedClass(string pixelType, string alpha) {#> - + public partial class <#=pixelType#>_OperationsTests : PixelOperationsTests<<#=pixelType#>> { public <#=pixelType#>_OperationsTests(ITestOutputHelper output) @@ -78,15 +96,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 a9b3ee9a42..c569919f5f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -5,7 +5,7 @@ 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; @@ -39,49 +39,49 @@ protected PixelOperationsTests(ITestOutputHelper output) } 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; protected virtual PixelOperations Operations { get; } = PixelOperations.Instance; - protected bool HasUnassociatedAlpha => this.Operations.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated; + protected bool HasAssociatedAlpha => TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Associated; 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,43 @@ 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].FromScaledVector4(v); + expected[i] = TPixel.FromScaledVector4(v); + } + + return expected; + } + + /// + /// Creates the scalar oracle for companded scaled input. + /// + /// The source vectors. + /// Whether the source vectors use associated alpha. + /// The converted pixels. + internal static TPixel[] CreateCompandedExpectedPixelData(Vector4[] source, bool associated) + { + TPixel[] expected = new TPixel[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + Vector4 vector = source[i]; + + if (associated) + { + Numerics.UnPremultiply(ref vector); + } + + vector = SRgbCompanding.Compress(vector); + + // Transfer functions operate on straight RGB. Let the destination pixel perform any required association so its stored + // alpha quantization participates exactly once, matching the public bulk conversion contract. + expected[i] = TPixel.FromUnassociatedScaledVector4(vector); } return expected; @@ -105,7 +134,7 @@ internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source, RefActi [Fact] public void PixelTypeInfoHasCorrectBitsPerPixel() { - int bits = this.Operations.GetPixelTypeInfo().BitsPerPixel; + int bits = TPixel.GetPixelTypeInfo().BitsPerPixel; Assert.Equal(Unsafe.SizeOf() * 8, bits); } @@ -114,18 +143,16 @@ public void PixelAlphaRepresentation_DefinesPresenceOfAlphaChannel() { // 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 = this.Operations.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; + bool hasAlpha = TPixel.GetPixelTypeInfo().AlphaRepresentation != PixelAlphaRepresentation.None; - byte expectedAlpha = hasAlpha ? Alpha : NoAlpha; + byte expectedAlpha = hasAlpha ? alpha : noAlpha; Assert.Equal(expectedAlpha, dest.A); } @@ -163,12 +190,18 @@ public void FromScaledVector4(int count) [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); + if (this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref 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 = CreateCompandedExpectedPixelData(source, this.HasAssociatedAlpha); TestOperation( source, @@ -185,35 +218,27 @@ public void FromCompandedScaledVector4(int count) [MemberData(nameof(ArraySizesData))] public void FromPremultipliedVector4(int count) { - void SourceAction(ref Vector4 v) - { - if (this.HasUnassociatedAlpha) - { - Numerics.Premultiply(ref v); - } - } + Vector4[] source = CreateVector4TestData(count); + TPixel[] expected = new TPixel[count]; - void ExpectedAction(ref Vector4 v) + for (int i = 0; i < source.Length; i++) { - if (this.HasUnassociatedAlpha) - { - Numerics.UnPremultiply(ref v); - } + // Native W is not necessarily opacity. Use the per-pixel representation contract to create and consume associated-native vectors. + TPixel pixel = TPixel.FromUnassociatedVector4(source[i]); + source[i] = pixel.ToAssociatedVector4(); + expected[i] = TPixel.FromAssociatedVector4(source[i]); } - Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => SourceAction(ref v)); - TPixel[] expected = CreateExpectedPixelData(source, (ref Vector4 v) => ExpectedAction(ref v)); - TestOperation( source, expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - - this.Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan(), modifiers); + this.Operations.FromVector4Destructive( + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply); }); } @@ -223,7 +248,7 @@ public void FromPremultipliedScaledVector4(int count) { void SourceAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.Premultiply(ref v); } @@ -231,7 +256,7 @@ void SourceAction(ref Vector4 v) void ExpectedAction(ref Vector4 v) { - if (this.HasUnassociatedAlpha) + if (!this.HasAssociatedAlpha) { Numerics.UnPremultiply(ref v); } @@ -245,15 +270,11 @@ void ExpectedAction(ref Vector4 v) expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - this.Operations.FromVector4Destructive( - this.Configuration, - s, - d.GetSpan(), - modifiers | PixelConversionModifiers.Scale); + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale); }); } @@ -263,41 +284,23 @@ public void FromCompandedPremultipliedScaledVector4(int count) { void SourceAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); - - if (this.HasUnassociatedAlpha) - { - Numerics.Premultiply(ref v); - } - } - - void ExpectedAction(ref Vector4 v) - { - if (this.HasUnassociatedAlpha) - { - Numerics.UnPremultiply(ref v); - } - - SRgbCompanding.Compress(ref v); + v = SRgbCompanding.Expand(v); + Numerics.Premultiply(ref 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 = CreateCompandedExpectedPixelData(source, true); TestOperation( source, expected, (s, d) => { - PixelConversionModifiers modifiers = this.HasUnassociatedAlpha - ? PixelConversionModifiers.Premultiply - : PixelConversionModifiers.None; - this.Operations.FromVector4Destructive( - this.Configuration, - s, - d.GetSpan(), - modifiers | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale); + this.Configuration, + s, + d.GetSpan(), + PixelConversionModifiers.Premultiply | PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale); }, false); } @@ -319,22 +322,27 @@ public void ToVector4(int count) { new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), @@ -342,7 +350,10 @@ public void ToVector4(int count) new TestPixel(), new TestPixel(), new TestPixel(), + new TestPixel(), new TestPixel(), + new TestPixel(), + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), @@ -355,9 +366,9 @@ public void Generic_To(TestPixel _) { 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); + PixelConverterTests.ReferenceImplementations.To(source, expected); TestOperation(source, expected, (s, d) => this.Operations.To(this.Configuration, s, d.GetSpan()), false); } @@ -385,12 +396,29 @@ public void ToCompandedScaledVector4(int count) { void SourceAction(ref Vector4 v) { + if (this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } } - void ExpectedAction(ref Vector4 v) => SRgbCompanding.Expand(ref v); + void ExpectedAction(ref Vector4 v) + { + if (this.HasAssociatedAlpha) + { + Numerics.UnPremultiply(ref 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)); + if (this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } + } + + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedScaledVector4Data(source, ExpectedAction); TestOperation( source, @@ -406,15 +434,15 @@ void SourceAction(ref Vector4 v) [MemberData(nameof(ArraySizesData))] public void ToPremultipliedVector4(int count) { - void SourceAction(ref Vector4 v) + TPixel[] source = CreatePixelTestData(count); + Vector4[] expected = new Vector4[count]; + + for (int i = 0; i < source.Length; i++) { + // Association is defined by the pixel format because native W can be affine or integer-valued rather than normalized opacity. + expected[i] = source[i].ToAssociatedVector4(); } - 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)); - TestOperation( source, expected, @@ -429,9 +457,15 @@ void SourceAction(ref Vector4 v) { } - void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); + void ExpectedAction(ref Vector4 v) + { + if (!this.HasAssociatedAlpha) + { + 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( @@ -450,16 +484,25 @@ public void ToCompandedPremultipliedScaledVector4(int count) { void SourceAction(ref Vector4 v) { + if (this.HasAssociatedAlpha) + { + Numerics.Premultiply(ref v); + } } void ExpectedAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); + if (this.HasAssociatedAlpha) + { + Numerics.UnPremultiply(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, @@ -476,13 +519,13 @@ void ExpectedAction(ref Vector4 v) 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 +540,11 @@ public void ToArgb32Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i4] = argb.A; expected[i4 + 1] = argb.R; @@ -521,13 +563,13 @@ public void ToArgb32Bytes(int count) 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 +584,11 @@ public void ToBgr24Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i3] = bgr.B; expected[i3 + 1] = bgr.G; expected[i3 + 2] = bgr.R; @@ -564,13 +605,13 @@ public void ToBgr24Bytes(int count) 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 +626,11 @@ public void ToBgra32Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i4] = bgra.B; expected[i4 + 1] = bgra.G; expected[i4 + 2] = bgra.R; @@ -608,13 +648,13 @@ public void ToBgra32Bytes(int count) 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 +669,11 @@ public void ToAbgr32Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i4] = abgr.A; expected[i4 + 1] = abgr.B; expected[i4 + 2] = abgr.G; @@ -653,14 +692,14 @@ public void FromBgra5551Bytes(int count) { 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 +715,11 @@ public void ToBgra5551Bytes(int count) 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(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref bgra); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -699,11 +737,11 @@ public void FromL8(int count) { 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 +755,11 @@ public void FromL8(int count) 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(ToUnassociatedScaledVector4(source[i])); } TestOperation( @@ -734,18 +772,13 @@ public void ToL8(int count) [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 +792,11 @@ public void FromL16(int count) 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(ToUnassociatedScaledVector4(source[i])); } TestOperation( @@ -778,14 +811,14 @@ public void FromLa16Bytes(int count) { 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 +834,11 @@ public void ToLa16Bytes(int count) 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(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -824,14 +856,14 @@ public void FromLa32Bytes(int count) { 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 +879,11 @@ public void ToLa32Bytes(int count) 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(ToUnassociatedScaledVector4(source[i])); OctetBytes bytes = Unsafe.As(ref la); expected[offset] = bytes[0]; expected[offset + 1] = bytes[1]; @@ -871,13 +902,13 @@ public void ToLa32Bytes(int count) 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 +923,11 @@ public void ToRgb24Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i3] = rgb.R; expected[i3 + 1] = rgb.G; expected[i3 + 2] = rgb.B; @@ -914,13 +944,13 @@ public void ToRgb24Bytes(int count) 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 +965,11 @@ public void ToRgba32Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); expected[i4] = rgba.R; expected[i4 + 1] = rgba.G; expected[i4 + 2] = rgba.B; @@ -959,12 +988,12 @@ public void FromRgb48Bytes(int count) { 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 +1008,11 @@ public void ToRgb48Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); OctetBytes rgb48Bytes = Unsafe.As(ref rgb); expected[i6] = rgb48Bytes[0]; expected[i6 + 1] = rgb48Bytes[1]; @@ -1006,12 +1034,12 @@ public void FromRgba64Bytes(int count) { 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 +1054,11 @@ public void ToRgba64Bytes(int count) { 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(ToUnassociatedScaledVector4(source[i])); OctetBytes rgba64Bytes = Unsafe.As(ref rgba); expected[i8] = rgba64Bytes[0]; expected[i8 + 1] = rgba64Bytes[1]; @@ -1060,11 +1087,11 @@ public void PackFromRgbPlanes(int count) 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 +1103,7 @@ 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++) { @@ -1090,6 +1117,9 @@ internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source, RefAc return expected; } + // The scalar conversion boundary owns representation-specific rounding. Byte-export tests compare each bulk operation with that scalar result. + private static Vector4 ToUnassociatedScaledVector4(TPixel source) => source.ToUnassociatedScaledVector4(); + internal static void TestOperation( TSource[] source, TDest[] expected, @@ -1098,7 +1128,7 @@ internal static void TestOperation( 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 +1137,8 @@ internal static void TestOperation( 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 +1153,9 @@ internal static Vector4[] CreateVector4TestData(int length, RefAction v 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 +1163,7 @@ internal static TPixel[] CreatePixelTestData(int length, RefAction vect vectorModifier?.Invoke(ref v); - result[i].FromVector4(v); + result[i] = TPixel.FromVector4(v); } return result; @@ -1141,9 +1171,9 @@ internal static TPixel[] CreatePixelTestData(int length, RefAction vect 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 +1181,7 @@ internal static TPixel[] CreateScaledPixelTestData(int length, RefAction 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,22 +1249,41 @@ public void Verify() { 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++) { Assert.Equal(expected[i], actual[i], comparer); } } - else if (!this.PreferExactComparison && typeof(IPixel).IsAssignableFrom(typeof(TDest)) && IsComplexPixel()) + else if (!this.PreferExactComparison && typeof(IPixel).IsAssignableFrom(typeof(TDest))) { Span expected = this.ExpectedDestBuffer.AsSpan(); Span actual = this.ActualDestBuffer.GetSpan(); - var comparer = new ApproximateFloatComparer(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); - for (int i = 0; i < count; i++) + if (IsComplexPixel()) + { + ApproximateFloatComparer comparer = new(TestEnvironment.Is64BitProcess ? 0.0001F : 0.001F); + + for (int i = 0; i < count; i++) + { + Assert.Equal(((IPixel)expected[i]).ToScaledVector4(), ((IPixel)actual[i]).ToScaledVector4(), comparer); + } + } + else { - Assert.Equal((IPixel)expected[i], (IPixel)actual[i], comparer); + // SIMD and scalar conversion can select adjacent packed values at a quantization boundary. + int tolerance = Unsafe.SizeOf() <= sizeof(ushort) ? 17 : 1; + + for (int i = 0; i < count; i++) + { + Rgba32 expectedPixel = ((IPixel)expected[i]).ToRgba32(); + Rgba32 actualPixel = ((IPixel)actual[i]).ToRgba32(); + Assert.InRange(Math.Abs(expectedPixel.R - actualPixel.R), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.G - actualPixel.G), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.B - actualPixel.B), 0, tolerance); + Assert.InRange(Math.Abs(expectedPixel.A - actualPixel.A), 0, tolerance); + } } } else @@ -1249,7 +1298,7 @@ public void Verify() } } - // 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/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index 2900b0d292..b2790469a1 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,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); @@ -33,7 +34,7 @@ public void Rg32_ToVector2() public void Rg32_ToScaledVector4() { // arrange - var rg32 = new Rg32(Vector2.One); + Rg32 rg32 = new(Vector2.One); // act Vector4 actual = rg32.ToScaledVector4(); @@ -49,13 +50,12 @@ public void Rg32_ToScaledVector4() public void Rg32_FromScaledVector4() { // arrange - var rg32 = new Rg32(Vector2.One); - var pixel = default(Rg32); - uint expected = 0xFFFFFFFF; + Rg32 rg32 = new(Vector2.One); + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rg32.ToScaledVector4(); - pixel.FromScaledVector4(scaled); + Rg32 pixel = Rg32.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -66,11 +66,10 @@ public void Rg32_FromScaledVector4() public void Rg32_FromBgra5551() { // arrange - var rg32 = new Rg32(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); @@ -82,4 +81,20 @@ public void Rg32_Clamping() 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); + + 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 2d1be8ab44..6364378c15 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -21,7 +22,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 +32,7 @@ public void Constructor(byte r, byte g, byte b) [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 +44,8 @@ public unsafe void ByteLayoutIsSequentialRgb() [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 +58,8 @@ public void Equals_WhenTrue(byte r, byte g, byte b) [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,8 +68,7 @@ public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b [Fact] public void FromRgba32() { - var rgb = default(Rgb24); - 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); @@ -84,8 +84,7 @@ public void FromRgba32() [Fact] public void FromVector4() { - var rgb = default(Rgb24); - 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); @@ -95,7 +94,7 @@ public void FromVector4() [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,12 +103,11 @@ public void ToVector4() public void ToRgba32() { // arrange - var rgb = new Rgb24(1, 2, 3); - Rgba32 rgba = default; - var expected = new Rgba32(1, 2, 3, 255); + Rgb24 rgb = new(1, 2, 3); + Rgba32 expected = new(1, 2, 3, 255); // act - rgb.ToRgba32(ref rgba); + Rgba32 rgba = rgb.ToRgba32(); // assert Assert.Equal(expected, rgba); @@ -118,15 +116,29 @@ public void ToRgba32() [Fact] public void Rgb24_FromBgra5551() { - // arrange - var rgb = new Rgb24(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); 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB, info.ColorType); + + 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 d8a61940b2..764627ee34 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -12,7 +13,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,13 +33,12 @@ public void Rgb48_ToScaledVector4() 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 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); @@ -48,12 +48,11 @@ public void Rgb48_FromScaledVector4() 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; - rgba48.ToRgba32(ref actual); + Rgba32 actual = rgba48.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -63,15 +62,31 @@ public void Rgb48_ToRgba32() public void Rgb48_FromBgra5551() { // arrange - var rgb = default(Rgb48); - 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); 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB, info.ColorType); + + 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/Rgb96Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb96Tests.cs new file mode 100644 index 0000000000..e70e2fa0f9 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb96Tests.cs @@ -0,0 +1,311 @@ +// 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.Tests.PixelFormats; + +[Trait("Category", "PixelFormats")] +public class Rgb96Tests +{ + [Theory] + [InlineData(1, 2, 3)] + [InlineData(50, 70, 80)] + [InlineData(1000, 2000, 3000)] + public void Constructor(uint r, uint g, uint b) + { + Rgb96 p = new(r, g, b); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + } + + [Fact] + public void Rgb96_ToVector4() + { + Assert.Equal(Vector4.UnitW, new Rgb96(0, 0, 0).ToVector4()); + Assert.Equal(Vector4.One, new Rgb96(uint.MaxValue, uint.MaxValue, uint.MaxValue).ToVector4()); + Assert.Equal(new Vector4(0.5F, 0.5F, 0.5F, 1.0F), new Rgb96(uint.MaxValue / 2, uint.MaxValue / 2, uint.MaxValue / 2).ToVector4()); + } + + [Theory] + [InlineData(uint.MaxValue, uint.MaxValue, uint.MaxValue)] + [InlineData(0, 0, 0)] + [InlineData(uint.MaxValue / 2, 100, 2222)] + public void Rgb96_ToScaledVector4(uint r, uint g, uint b) + { + // arrange + Rgb96 rgb = new(r, g, b); + + float max = uint.MaxValue; + float rr = r / max; + float gg = g / max; + float bb = b / max; + + // act + Vector4 actual = rgb.ToScaledVector4(); + + // assert + Assert.Equal(rr, actual.X); + Assert.Equal(gg, actual.Y); + Assert.Equal(bb, actual.Z); + Assert.Equal(1.0F, actual.W); + } + + [Theory] + [InlineData(0, 0, 0)] + [InlineData(5000, 100, 2222)] + public void Rgb96_FromScaledVector4(uint r, uint g, uint b) + { + // arrange + Rgb96 source = new(r, g, b); + Vector4 scaled = source.ToScaledVector4(); + + // act + Rgb96 actual = Rgb96.FromScaledVector4(scaled); + + // assert + Assert.Equal(source, actual); + } + + [Fact] + public void Rgb96_ToRgba32() + { + // arrange + Rgb96 rgb96 = new((uint)(uint.MaxValue * 0.1), uint.MaxValue / 2, uint.MaxValue); + Rgba32 expected = new(25, 127, 255, 255); + + // act + Rgba32 actual = rgb96.ToRgba32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Equality_WhenTrue() + { + Rgb96 c1 = new(100, 2000, 3000); + Rgb96 c2 = new(100, 2000, 3000); + + Assert.True(c1.Equals(c2)); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + Rgb96 c1 = new(100, 2000, 3000); + Rgb96 c2 = new(101, 2000, 3000); + Rgb96 c3 = new(100, 2000, 3001); + + Assert.False(c1.Equals(c2)); + Assert.False(c2.Equals(c3)); + Assert.False(c3.Equals(c1)); + } + + [Fact] + public void Rgb96_FromRgba32() + { + // arrange + Rgba32 source = new(25, 127, 255, 255); + Rgb96 expected = new(421075225, 2139062143, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromRgba32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromRgb24() + { + // arrange + Rgb24 source = new(25, 127, 255); + Rgb96 expected = new(421075225, 2139062143, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromRgb24(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromRgb48() + { + // arrange + Rgb48 source = new(0, 32767, ushort.MaxValue); + Rgb96 expected = new(0, 2147450879, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromRgb48(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromRgba64() + { + // arrange + Rgba64 source = new(0, 32767, ushort.MaxValue, ushort.MaxValue); + Rgb96 expected = new(0, 2147450879, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromRgba64(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromLa32() + { + // arrange + La32 source = new(32767, ushort.MaxValue); + Rgb96 expected = new(2147450879, 2147450879, 2147450879); + + // act + Rgb96 actual = Rgb96.FromLa32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromLa16() + { + // arrange + La16 source = new(127, byte.MaxValue); + Rgb96 expected = new(2139062143, 2139062143, 2139062143); + + // act + Rgb96 actual = Rgb96.FromLa16(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromL16() + { + // arrange + L16 source = new(32767); + Rgb96 expected = new(2147450879, 2147450879, 2147450879); + + // act + Rgb96 actual = Rgb96.FromL16(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromL8() + { + // arrange + L8 source = new(127); + Rgb96 expected = new(2139062143, 2139062143, 2139062143); + + // act + Rgb96 actual = Rgb96.FromL8(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromBgra32() + { + // arrange + Bgra32 source = new(127, 25, 255); + Rgb96 expected = new(2139062143, 421075225, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromBgra32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromBgr24() + { + // arrange + Bgr24 source = new(127, 25, 255); + Rgb96 expected = new(2139062143, 421075225, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromBgr24(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromBgra5551() + { + // arrange + Bgra5551 source = new(1.0f, 1.0f, 1.0f, 1.0f); + Rgb96 expected = new(uint.MaxValue, uint.MaxValue, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromBgra5551(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromArgb32() + { + // arrange + Argb32 source = new(127, 25, 255); + Rgb96 expected = new(2139062143, 421075225, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromArgb32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_FromAbgr32() + { + // arrange + Abgr32 source = new(127, 25, 255); + Rgb96 expected = new(2139062143, 421075225, uint.MaxValue); + + // act + Rgb96 actual = Rgb96.FromAbgr32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgb96_PixelInformation() + { + PixelTypeInfo info = Rgb96.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB, info.ColorType); + + PixelComponentInfo? maybeComponentInfo = info.ComponentInfo; + Assert.NotNull(maybeComponentInfo); + PixelComponentInfo componentInfo = maybeComponentInfo.Value; + + Assert.Equal(3, 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.GetMaximumComponentPrecision()); + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 0c28b35c69..79a1aefc9c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -15,10 +16,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(1f, 0.0f, 1f, 1f)); + Rgba1010102 color4 = new(1f, 0.0f, 1f, 1f); Assert.Equal(color1, color2); Assert.Equal(color3, color4); @@ -30,10 +31,10 @@ public void AreEqual() [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(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); @@ -42,10 +43,10 @@ public void AreNotEqual() [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); @@ -66,7 +67,7 @@ public void Rgba1010102_ToVector4() public void Rgba1010102_ToScaledVector4() { // arrange - var rgba = new Rgba1010102(Vector4.One); + Rgba1010102 rgba = new(Vector4.One); // act Vector4 actual = rgba.ToScaledVector4(); @@ -82,13 +83,12 @@ public void Rgba1010102_ToScaledVector4() public void Rgba1010102_FromScaledVector4() { // arrange - var rgba = new Rgba1010102(Vector4.One); - var actual = default(Rgba1010102); - uint expected = 0xFFFFFFFF; + Rgba1010102 rgba = new(Vector4.One); + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rgba.ToScaledVector4(); - actual.FromScaledVector4(scaled); + Rgba1010102 actual = Rgba1010102.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -98,11 +98,10 @@ public void Rgba1010102_FromScaledVector4() public void Rgba1010102_FromBgra5551() { // arrange - var rgba = new Rgba1010102(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); @@ -112,11 +111,10 @@ public void Rgba1010102_FromBgra5551() public void Rgba1010102_FromArgb32() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -126,14 +124,12 @@ public void Rgba1010102_FromArgb32() public void Rgba1010102_FromRgba32() { // arrange - var rgba1 = default(Rgba1010102); - var rgba2 = default(Rgba1010102); - 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); @@ -144,11 +140,10 @@ public void Rgba1010102_FromRgba32() public void Rgba1010102_FromBgr24() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -158,11 +153,10 @@ public void Rgba1010102_FromBgr24() public void Rgba1010102_FromGrey8() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -172,11 +166,10 @@ public void Rgba1010102_FromGrey8() public void Rgba1010102_FromGrey16() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -186,11 +179,10 @@ public void Rgba1010102_FromGrey16() public void Rgba1010102_FromRgb24() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -200,11 +192,10 @@ public void Rgba1010102_FromRgb24() public void Rgba1010102_FromRgb48() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -214,11 +205,10 @@ public void Rgba1010102_FromRgb48() public void Rgba1010102_FromRgba64() { // arrange - var rgba = default(Rgba1010102); - 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); @@ -235,14 +225,31 @@ public void Rgba1010102_Clamping() 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; - rgba.ToRgba32(ref actual); + Rgba32 actual = rgba.ToRgba32(); // assert Assert.Equal(expected, actual); } + + [Fact] + public void Rgba1010102_PixelInformation() + { + 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); + 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/Rgba128Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba128Tests.cs new file mode 100644 index 0000000000..339d57ed47 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba128Tests.cs @@ -0,0 +1,313 @@ +// 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.Tests.PixelFormats; + +[Trait("Category", "PixelFormats")] +public class Rgba128Tests +{ + [Theory] + [InlineData(1, 2, 3, 4)] + [InlineData(50, 70, 80, 90)] + [InlineData(1000, 2000, 3000, 4000)] + public void Constructor(uint r, uint g, uint b, uint a) + { + Rgba128 p = new(r, g, b, a); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + } + + [Fact] + public void Rgba128_ToVector4() + { + Assert.Equal(Vector4.Zero, new Rgba128(0, 0, 0, 0).ToVector4()); + Assert.Equal(Vector4.One, new Rgba128(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue).ToVector4()); + Assert.Equal(new Vector4(0.5F, 0.5F, 0.5F, 0.5F), new Rgba128(uint.MaxValue / 2, uint.MaxValue / 2, uint.MaxValue / 2, uint.MaxValue / 2).ToVector4()); + } + + [Theory] + [InlineData(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue)] + [InlineData(0, 0, 0, 0)] + [InlineData(uint.MaxValue / 2, 100, 2222, 3333)] + public void Rgba128_ToScaledVector4(uint r, uint g, uint b, uint a) + { + // arrange + Rgba128 rgb = new(r, g, b, a); + + float max = uint.MaxValue; + float rr = r / max; + float gg = g / max; + float bb = b / max; + float aa = a / max; + + // act + Vector4 actual = rgb.ToScaledVector4(); + + // assert + Assert.Equal(rr, actual.X); + Assert.Equal(gg, actual.Y); + Assert.Equal(bb, actual.Z); + Assert.Equal(aa, actual.W); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(5000, 100, 2222, 3333)] + public void Rgba128_FromScaledVector4(uint r, uint g, uint b, uint a) + { + // arrange + Rgba128 source = new(r, g, b, a); + Vector4 scaled = source.ToScaledVector4(); + + // act + Rgba128 actual = Rgba128.FromScaledVector4(scaled); + + // assert + Assert.Equal(source, actual); + } + + [Fact] + public void Rgba128_ToRgba32() + { + // arrange + Rgba128 rgb96 = new((uint)(uint.MaxValue * 0.1), uint.MaxValue / 2, uint.MaxValue, uint.MaxValue); + Rgba32 expected = new(25, 127, 255, 255); + + // act + Rgba32 actual = rgb96.ToRgba32(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Equality_WhenTrue() + { + Rgba128 c1 = new(100, 2000, 3000, 4000); + Rgba128 c2 = new(100, 2000, 3000, 4000); + + Assert.True(c1.Equals(c2)); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + Rgba128 c1 = new(100, 2000, 3000, 4000); + Rgba128 c2 = new(101, 2000, 3000, 4000); + Rgba128 c3 = new(100, 2000, 3001, 4000); + + Assert.False(c1.Equals(c2)); + Assert.False(c2.Equals(c3)); + Assert.False(c3.Equals(c1)); + } + + [Fact] + public void Rgba128_FromRgba32() + { + // arrange + Rgba32 source = new(25, 127, 255, 255); + Rgba128 expected = new(421075225, 2139062143, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromRgba32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromRgb24() + { + // arrange + Rgb24 source = new(25, 127, 255); + Rgba128 expected = new(421075225, 2139062143, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromRgb24(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromRgb48() + { + // arrange + Rgb48 source = new(0, 32767, ushort.MaxValue); + Rgba128 expected = new(0, 2147450879, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromRgb48(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromRgba64() + { + // arrange + Rgba64 source = new(0, 32767, ushort.MaxValue, ushort.MaxValue); + Rgba128 expected = new(0, 2147450879, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromRgba64(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromLa32() + { + // arrange + La32 source = new(32767, ushort.MaxValue); + Rgba128 expected = new(2147450879, 2147450879, 2147450879, 2147450879); + + // act + Rgba128 actual = Rgba128.FromLa32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromLa16() + { + // arrange + La16 source = new(127, byte.MaxValue); + Rgba128 expected = new(2139062143, 2139062143, 2139062143, 2139062143); + + // act + Rgba128 actual = Rgba128.FromLa16(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromL16() + { + // arrange + L16 source = new(32767); + Rgba128 expected = new(2147450879, 2147450879, 2147450879, 2147450879); + + // act + Rgba128 actual = Rgba128.FromL16(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromL8() + { + // arrange + L8 source = new(127); + Rgba128 expected = new(2139062143, 2139062143, 2139062143, 2139062143); + + // act + Rgba128 actual = Rgba128.FromL8(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromBgra32() + { + // arrange + Bgra32 source = new(127, 25, 255); + Rgba128 expected = new(2139062143, 421075225, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromBgra32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromBgr24() + { + // arrange + Bgr24 source = new(127, 25, 255); + Rgba128 expected = new(2139062143, 421075225, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromBgr24(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromBgra5551() + { + // arrange + Bgra5551 source = new(1.0f, 1.0f, 1.0f, 1.0f); + Rgba128 expected = new(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromBgra5551(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromArgb32() + { + // arrange + Argb32 source = new(127, 25, 255); + Rgba128 expected = new(2139062143, 421075225, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromArgb32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_FromAbgr32() + { + // arrange + Abgr32 source = new(127, 25, 255); + Rgba128 expected = new(2139062143, 421075225, uint.MaxValue, uint.MaxValue); + + // act + Rgba128 actual = Rgba128.FromAbgr32(source); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Rgba128_PixelInformation() + { + PixelTypeInfo info = Rgba128.GetPixelTypeInfo(); + Assert.Equal(Unsafe.SizeOf() * 8, info.BitsPerPixel); + Assert.Equal(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); + + PixelComponentInfo? maybeComponentInfo = info.ComponentInfo; + Assert.NotNull(maybeComponentInfo); + PixelComponentInfo componentInfo = maybeComponentInfo.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/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 64903f65bb..3bb1fead1c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -18,12 +19,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 = Color.ParseHex("#000").ToPixel(); + Rgba32 color4 = Color.ParseHex("#000F").ToPixel(); + Rgba32 color5 = Color.ParseHex("#000000").ToPixel(); + Rgba32 color6 = Color.ParseHex("#000000FF").ToPixel(); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -38,11 +39,11 @@ public void AreEqual() [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 = Color.ParseHex("#000").ToPixel(); + Rgba32 color4 = Color.ParseHex("#000000").ToPixel(); + Rgba32 color5 = Color.ParseHex("#FF000000").ToPixel(); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -56,62 +57,38 @@ public void AreNotEqual() [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); Assert.Equal(Math.Round(.5f * 255), color5.A); } - /// - /// Tests whether FromHex and ToHex work correctly. - /// - [Fact] - public void FromAndToHex() - { - // 8 digit hex matches css4 spec. RRGGBBAA - var color = Rgba32.ParseHex("#AABBCCDD"); // 170, 187, 204, 221 - Assert.Equal(170, color.R); - Assert.Equal(187, color.G); - Assert.Equal(204, color.B); - Assert.Equal(221, color.A); - - Assert.Equal("AABBCCDD", color.ToHex()); - - color.R = 0; - - Assert.Equal("00BBCCDD", color.ToHex()); - - color.A = 255; - - Assert.Equal("00BBCCFF", color.ToHex()); - } - /// /// Tests that the individual byte elements are laid out in RGBA order. /// [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 +123,7 @@ public void Rgba32_ToVector4() public void Rgba32_ToScaledVector4() { // arrange - var rgba = new Rgba32(Vector4.One); + Rgba32 rgba = new(Vector4.One); // act Vector4 actual = rgba.ToScaledVector4(); @@ -162,13 +139,12 @@ public void Rgba32_ToScaledVector4() public void Rgba32_FromScaledVector4() { // arrange - var rgba = new Rgba32(Vector4.One); - var actual = default(Rgba32); - uint expected = 0xFFFFFFFF; + Rgba32 rgba = new(Vector4.One); + const uint expected = 0xFFFFFFFF; // act Vector4 scaled = rgba.ToScaledVector4(); - actual.FromScaledVector4(scaled); + Rgba32 actual = Rgba32.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -185,12 +161,11 @@ public void Rgba32_Clamping() 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 expected = new(0x1a, 0, 0x80, 0); // act - actual.FromRgba32(rgba); + Rgba32 actual = Rgba32.FromRgba32(rgba); // assert Assert.Equal(expected, actual); @@ -200,13 +175,11 @@ public void Rgba32_ToRgba32() public void Rgba32_FromRgba32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Rgba32); - var expected = new Rgba32(0x1a, 0, 0x80, 0); + 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); @@ -216,13 +189,11 @@ public void Rgba32_FromRgba32_ToRgba32() public void Rgba32_FromBgra32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Bgra32); - var expected = new Bgra32(0x1a, 0, 0x80, 0); + 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); @@ -232,13 +203,11 @@ public void Rgba32_FromBgra32_ToRgba32() public void Rgba32_FromAbgr32_ToRgba32() { // arrange - var rgba = default(Rgba32); - var actual = default(Abgr32); - var expected = new Abgr32(0x1a, 0, 0x80, 0); + 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); @@ -248,13 +217,11 @@ public void Rgba32_FromAbgr32_ToRgba32() public void Rgba32_FromArgb32_ToArgb32() { // arrange - var rgba = default(Rgba32); - var actual = default(Argb32); - var expected = new Argb32(0x1a, 0, 0x80, 0); + 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); @@ -264,13 +231,11 @@ public void Rgba32_FromArgb32_ToArgb32() public void Rgba32_FromRgb48() { // arrange - var input = default(Rgba32); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + 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); @@ -280,13 +245,11 @@ public void Rgba32_FromRgb48() public void Rgba32_FromRgba64() { // arrange - var input = default(Rgba32); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + 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); @@ -296,13 +259,30 @@ public void Rgba32_FromRgba64() public void Rgba32_FromBgra5551() { // arrange - var rgb = default(Rgba32); - 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); } + + [Fact] + public void Rgba32_PixelInformation() + { + 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); + 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 f60ed8a529..694d0ace10 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -38,7 +39,7 @@ public void Rgba64_ToVector4() 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,13 +64,12 @@ public void Rgba64_ToScaledVector4(ushort r, ushort g, ushort b, ushort a) 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(); - Rgba64 actual = default; - actual.FromScaledVector4(scaled); + Rgba64 actual = Rgba64.FromScaledVector4(scaled); // assert Assert.Equal(source, actual); @@ -78,10 +78,9 @@ public void Rgba64_FromScaledVector4(ushort r, ushort g, ushort b, ushort a) [Fact] public void Rgba64_Clamping() { - var zero = default(Rgba64); - var one = default(Rgba64); - 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()); } @@ -90,12 +89,11 @@ public void Rgba64_Clamping() 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 expected = new(20, 38, 76, 115); // act - rgba64.ToRgba32(ref actual); + Rgba32 actual = rgba64.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -105,11 +103,10 @@ public void Rgba64_ToRgba32() public void Rgba64_FromBgra5551() { // arrange - var rgba = default(Rgba64); - 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); @@ -121,8 +118,8 @@ public void Rgba64_FromBgra5551() [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 +128,9 @@ public void Equality_WhenTrue() [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,11 +140,10 @@ public void Equality_WhenFalse() [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); + Rgba64 actual = Rgba64.FromRgba32(source); Assert.Equal(expected, actual); } @@ -155,9 +151,9 @@ public void Rgba64_FromRgba32() [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 +161,9 @@ public void ConstructFrom_Rgba32() [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 +171,9 @@ public void ConstructFrom_Bgra32() [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 +181,9 @@ public void ConstructFrom_Argb32() [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 +191,9 @@ public void ConstructFrom_Abgr32() [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 +201,9 @@ public void ConstructFrom_Rgb24() [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 +211,10 @@ public void ConstructFrom_Bgr24() [Fact] public void ConstructFrom_Vector4() { - var source = new Vector4(0f, 0.2f, 0.5f, 1f); - Rgba64 expected = default; - expected.FromScaledVector4(source); + Vector4 source = new(0f, 0.2f, 0.5f, 1f); + Rgba64 expected = Rgba64.FromScaledVector4(source); - var actual = new Rgba64(source); + Rgba64 actual = new(source); Assert.Equal(expected, actual); } @@ -228,11 +223,11 @@ public void ConstructFrom_Vector4() 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 +237,11 @@ public void ToRgba32_Retval() 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 +251,11 @@ public void ToBgra32_Retval() 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 +265,11 @@ public void ToArgb32_Retval() 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 +279,11 @@ public void ToAbgr32_Retval() 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 +293,31 @@ public void ToRgb24_Retval() 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(PixelAlphaRepresentation.Unassociated, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); + + 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/RgbaHalfTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaHalfTests.cs new file mode 100644 index 0000000000..9b22a9f8c8 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaHalfTests.cs @@ -0,0 +1,283 @@ +// 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; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +/// +/// Tests the unit-range binary16 RGBA pixel formats. +/// +[Trait("Category", "PixelFormats")] +public class RgbaHalfTests +{ + /// + /// Verifies that the unassociated format has the native layout required by RGBA binary16 surfaces. + /// + [Fact] + public void RgbaHalfHasRgbaBinary16Layout() + { + RgbaHalf pixel = new(.25F, .5F, .75F, 1F); + ulong expected = BitConverter.HalfToUInt16Bits((Half).25F) + | ((ulong)BitConverter.HalfToUInt16Bits((Half).5F) << 16) + | ((ulong)BitConverter.HalfToUInt16Bits((Half).75F) << 32) + | ((ulong)BitConverter.HalfToUInt16Bits((Half)1F) << 48); + + Assert.Equal(8, Unsafe.SizeOf()); + Assert.Equal(expected, pixel.PackedValue); + Assert.Equal(new Vector4(.25F, .5F, .75F, 1F), pixel.ToVector4()); + Assert.Equal(pixel.ToVector4(), pixel.ToScaledVector4()); + } + + /// + /// Verifies that zero-filled binary16 storage represents transparent black without affine remapping. + /// + [Fact] + public void RgbaHalfDefaultIsTransparentBlack() + { + RgbaHalf pixel = default; + + Assert.Equal(Vector4.Zero, pixel.ToVector4()); + Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); + } + + /// + /// Verifies that scaled input is clamped to the pixel format's unit color range. + /// + [Fact] + public void RgbaHalfFromScaledVector4ClampsToUnitRange() + { + RgbaHalf pixel = RgbaHalf.FromScaledVector4(new Vector4(-1F, .5F, 2F, 1F)); + + Assert.Equal(new Vector4(0F, .5F, 1F, 1F), pixel.ToScaledVector4()); + } + + /// + /// Verifies that the associated format stores associated binary16 components in the same RGBA order. + /// + [Fact] + public void RgbaHalfPHasAssociatedRgbaBinary16Layout() + { + RgbaHalfP pixel = new(.125F, .25F, .375F, .5F); + ulong expected = BitConverter.HalfToUInt16Bits((Half).125F) + | ((ulong)BitConverter.HalfToUInt16Bits((Half).25F) << 16) + | ((ulong)BitConverter.HalfToUInt16Bits((Half).375F) << 32) + | ((ulong)BitConverter.HalfToUInt16Bits((Half).5F) << 48); + + Assert.Equal(8, Unsafe.SizeOf()); + Assert.Equal(expected, pixel.PackedValue); + Assert.Equal(new Vector4(.125F, .25F, .375F, .5F), pixel.ToAssociatedScaledVector4()); + } + + /// + /// Verifies that zero-filled associated binary16 storage represents transparent black. + /// + [Fact] + public void RgbaHalfPDefaultIsTransparentBlack() + { + RgbaHalfP pixel = default; + + Assert.Equal(Vector4.Zero, pixel.ToVector4()); + Assert.Equal(Vector4.Zero, pixel.ToScaledVector4()); + Assert.Equal(Vector4.Zero, pixel.ToUnassociatedScaledVector4()); + } + + /// + /// Verifies that association uses the alpha value that survives binary16 quantization. + /// + [Fact] + public void RgbaHalfPQuantizesAlphaBeforeAssociation() + { + Vector4 source = new(.75F, .5F, .25F, 1F / 3F); + float storedAlpha = (float)(Half)source.W; + RgbaHalfP pixel = RgbaHalfP.FromUnassociatedScaledVector4(source); + + Assert.Equal((Half)(source.X * storedAlpha), pixel.R); + Assert.Equal((Half)(source.Y * storedAlpha), pixel.G); + Assert.Equal((Half)(source.Z * storedAlpha), pixel.B); + Assert.Equal((Half)storedAlpha, pixel.A); + } + + /// + /// Verifies every bulk representation path against its scalar pixel contract at each SIMD width and tail boundary. + /// + [Fact] + public void RgbaHalfBulkConversionsMatchScalarAcrossHardwareWidths() + => FeatureTestRunner.RunWithHwIntrinsicsFeature( + static () => AssertBulkConversionsMatchScalar(), + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Verifies every associated bulk representation path against its scalar pixel contract at each SIMD width and tail boundary. + /// + [Fact] + public void RgbaHalfPBulkConversionsMatchScalarAcrossHardwareWidths() + => FeatureTestRunner.RunWithHwIntrinsicsFeature( + static () => AssertBulkConversionsMatchScalar(), + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Verifies the declared component precision and alpha representation for the unassociated format. + /// + [Fact] + public void RgbaHalfPixelInformationIsCorrect() => AssertPixelInformation(PixelAlphaRepresentation.Unassociated); + + /// + /// Verifies the declared component precision and alpha representation for the associated format. + /// + [Fact] + public void RgbaHalfPPixelInformationIsCorrect() => AssertPixelInformation(PixelAlphaRepresentation.Associated); + + /// + /// Compares bulk conversions with the corresponding scalar pixel operations for representative vector widths and remainders. + /// + /// The binary16 pixel format to test. + private static void AssertBulkConversionsMatchScalar() + where TPixel : unmanaged, IPixel + { + int[] lengths = [0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 259]; + PixelConversionModifiers[] modifiers = + [ + PixelConversionModifiers.None, + PixelConversionModifiers.Scale, + PixelConversionModifiers.Premultiply, + PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply, + PixelConversionModifiers.UnPremultiply, + PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply + ]; + + foreach (int length in lengths) + { + TPixel[] pixels = new TPixel[length]; + + for (int i = 0; i < length; i++) + { + pixels[i] = TPixel.FromUnassociatedScaledVector4(CreateUnassociatedVector(i)); + } + + foreach (PixelConversionModifiers modifier in modifiers) + { + Vector4[] expectedVectors = new Vector4[length]; + Vector4[] actualVectors = new Vector4[length]; + Vector4[] sourceVectors = new Vector4[length]; + TPixel[] expectedPixels = new TPixel[length]; + TPixel[] actualPixels = new TPixel[length]; + bool associated = RequestsAssociated(modifier); + bool scaled = modifier.IsDefined(PixelConversionModifiers.Scale); + + for (int i = 0; i < length; i++) + { + expectedVectors[i] = ToScalarVector(pixels[i], associated, scaled); + sourceVectors[i] = CreateUnassociatedVector(i + 19); + + if (associated) + { + Numerics.Premultiply(ref sourceVectors[i]); + } + + expectedPixels[i] = FromScalarVector(sourceVectors[i], associated, scaled); + } + + PixelOperations.Instance.ToVector4(Configuration.Default, pixels, actualVectors, modifier); + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, sourceVectors.AsSpan(), actualPixels, modifier); + + Assert.Equal(expectedVectors, actualVectors); + Assert.Equal(expectedPixels, actualPixels); + } + } + } + + /// + /// Creates a deterministic unassociated unit-range vector for bulk conversion tests. + /// + /// The source index used to vary the component values. + /// The generated vector. + private static Vector4 CreateUnassociatedVector(int index) + => new( + ((index * 37) % 101) / 100F, + ((index * 53) % 101) / 100F, + ((index * 71) % 101) / 100F, + ((index * 89) % 101) / 100F); + + /// + /// Resolves whether a modifier combination requests associated output under the pixel format's native representation. + /// + /// The pixel format whose native representation participates in modifier resolution. + /// The conversion modifiers. + /// when the resulting vector representation is associated. + private static bool RequestsAssociated(PixelConversionModifiers modifiers) + where TPixel : unmanaged, IPixel + => modifiers.IsDefined(PixelConversionModifiers.Premultiply) + || (TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Associated + && !modifiers.IsDefined(PixelConversionModifiers.UnPremultiply)); + + /// + /// Converts one pixel through the scalar API selected by the requested representation and range. + /// + /// The source pixel format. + /// The source pixel. + /// Whether the result should use associated alpha. + /// Whether the result should use the scaled range. + /// The converted vector. + private static Vector4 ToScalarVector(TPixel pixel, bool associated, bool scaled) + where TPixel : unmanaged, IPixel + => (associated, scaled) switch + { + (true, true) => pixel.ToAssociatedScaledVector4(), + (true, false) => pixel.ToAssociatedVector4(), + (false, true) => pixel.ToUnassociatedScaledVector4(), + _ => pixel.ToUnassociatedVector4() + }; + + /// + /// Converts one vector through the scalar API selected by its representation and range. + /// + /// The destination pixel format. + /// The source vector. + /// Whether the source uses associated alpha. + /// Whether the source uses the scaled range. + /// The converted pixel. + private static TPixel FromScalarVector(Vector4 vector, bool associated, bool scaled) + where TPixel : unmanaged, IPixel + => (associated, scaled) switch + { + (true, true) => TPixel.FromAssociatedScaledVector4(vector), + (true, false) => TPixel.FromAssociatedVector4(vector), + (false, true) => TPixel.FromUnassociatedScaledVector4(vector), + _ => TPixel.FromUnassociatedVector4(vector) + }; + + /// + /// Verifies the component layout and alpha metadata exposed by a binary16 pixel format. + /// + /// The pixel format to inspect. + /// The expected alpha representation. + private static void AssertPixelInformation(PixelAlphaRepresentation alphaRepresentation) + where TPixel : unmanaged, IPixel + { + PixelTypeInfo info = TPixel.GetPixelTypeInfo(); + PixelComponentInfo componentInfo = info.ComponentInfo.Value; + + Assert.Equal(64, info.BitsPerPixel); + Assert.Equal(alphaRepresentation, info.AlphaRepresentation); + Assert.Equal(PixelColorType.RGB | PixelColorType.Alpha, info.ColorType); + Assert.Equal(4, componentInfo.ComponentCount); + Assert.Equal(0, componentInfo.Padding); + + for (int i = 0; i < componentInfo.ComponentCount; i++) + { + Assert.Equal(16, componentInfo.GetComponentPrecision(i)); + } + } +} + +/// +/// Applies the shared associated-alpha contract to . +/// +public class RgbaHalfPAssociatedAlphaTests : AssociatedAlphaPixelTests +{ +} diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index ac6d2e3ca5..5273482efb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -19,12 +19,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 +39,11 @@ public void AreEqual() [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 +57,13 @@ public void AreNotEqual() [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 +76,7 @@ public void ConstructorAssignsProperties() [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 +104,7 @@ public void FromAndToHex() [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,13 +119,11 @@ public void FloatLayout() public void RgbaVector_FromRgb48() { // arrange - var input = default(RgbaVector); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + 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 void RgbaVector_FromRgb48() public void RgbaVector_FromRgba64() { // arrange - var input = default(RgbaVector); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + 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 void RgbaVector_FromRgba64() public void RgbaVector_FromBgra5551() { // arrange - var rgb = default(RgbaVector); 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 void RgbaVector_FromBgra5551() public void RgbaVector_FromGrey16() { // arrange - var rgba = default(RgbaVector); 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 void RgbaVector_FromGrey16() public void RgbaVector_FromGrey8() { // arrange - var rgba = default(RgbaVector); 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,11 +190,27 @@ public void Issue2048() 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); } + + [Fact] + public void RgbaVector_PixelInformation() + { + 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); + 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 8fc080d818..86fc828304 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.PixelFormats; @@ -50,46 +52,55 @@ public void Short2_Clamping() [Fact] public void Short2_ToScaledVector4() { - // arrange - var short2 = new Short2(Vector2.One * 0x7FFF); - - // act - Vector4 actual = short2.ToScaledVector4(); - - // assert - Assert.Equal(1, actual.X); - Assert.Equal(1, actual.Y); - Assert.Equal(0, actual.Z); - Assert.Equal(1, actual.W); + Assert.Equal(new Vector4(1F, 1F, 0F, 1F), new Short2(Vector2.One * short.MaxValue).ToScaledVector4()); + Assert.Equal(new Vector4(0F, 0F, 0F, 1F), new Short2(Vector2.One * short.MinValue).ToScaledVector4()); } [Fact] public void Short2_FromScaledVector4() { - // arrange - var pixel = default(Short2); - var short2 = new Short2(Vector2.One * 0x7FFF); - const ulong expected = 0x7FFF7FFF; + Assert.Equal(0x80008000U, Short2.FromScaledVector4(new Vector4(0F, 0F, 0F, 1F)).PackedValue); + Assert.Equal(0x7FFF7FFFU, Short2.FromScaledVector4(Vector4.One).PackedValue); + } - // act - Vector4 scaled = short2.ToScaledVector4(); - pixel.FromScaledVector4(scaled); - uint actual = pixel.PackedValue; + [Fact] + public void Short2_BulkScaledConversionsCoverFullSignedRange() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertShort2BulkScaledConversionsCoverFullSignedRange, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); - // assert - Assert.Equal(expected, actual); + private static void AssertShort2BulkScaledConversionsCoverFullSignedRange() + { + const int length = 17; + Short2[] source = new Short2[length]; + Vector4[] expectedVectors = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + bool minimum = (i & 1) == 0; + source[i].PackedValue = minimum ? 0x80008000U : 0x7FFF7FFFU; + expectedVectors[i] = minimum ? new Vector4(0F, 0F, 0F, 1F) : new Vector4(1F, 1F, 0F, 1F); + } + + Vector4[] actualVectors = new Vector4[length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actualVectors, PixelConversionModifiers.Scale); + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] destructiveSource = [.. expectedVectors]; + Short2[] actualPixels = new Short2[length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.Equal(source, actualPixels); } [Fact] 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 expected = new(128, 127, 0, 255); // act - short2.ToRgba32(ref actual); + Rgba32 actual = short2.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -99,13 +110,11 @@ public void Short2_ToRgba32() public void Short2_FromRgba32_ToRgba32() { // arrange - var short2 = default(Short2); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 0, 255); + 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); @@ -115,13 +124,11 @@ public void Short2_FromRgba32_ToRgba32() public void Short2_FromRgb48() { // arrange - var input = default(Short2); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); + 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); @@ -131,13 +138,11 @@ public void Short2_FromRgb48() public void Short2_FromRgba64() { // arrange - var input = default(Short2); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); + 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); @@ -146,11 +151,8 @@ public void Short2_FromRgba64() [Fact] public void Short2_FromBgra5551() { - // arrange - var short2 = default(Short2); - // 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(); @@ -159,4 +161,20 @@ public void Short2_FromBgra5551() 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(PixelAlphaRepresentation.None, info.AlphaRepresentation); + Assert.Equal(PixelColorType.Red | PixelColorType.Green, info.ColorType); + + 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 c420627034..c8502462df 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -2,7 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; 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); @@ -37,45 +39,56 @@ public void Short4_ToVector4() [Fact] public void Short4_ToScaledVector4() { - // arrange - var short4 = new Short4(Vector4.One * 0x7FFF); - - // act - Vector4 actual = short4.ToScaledVector4(); - - // assert - Assert.Equal(1, actual.X); - Assert.Equal(1, actual.Y); - Assert.Equal(1, actual.Z); - Assert.Equal(1, actual.W); + Assert.Equal(Vector4.One, new Short4(Vector4.One * short.MaxValue).ToScaledVector4()); + Assert.Equal(Vector4.Zero, new Short4(Vector4.One * short.MinValue).ToScaledVector4()); } [Fact] public void Short4_FromScaledVector4() { - // arrange - var short4 = new Short4(Vector4.One * 0x7FFF); - Vector4 scaled = short4.ToScaledVector4(); - const long expected = 0x7FFF7FFF7FFF7FFF; + Assert.Equal(0x8000800080008000UL, Short4.FromScaledVector4(Vector4.Zero).PackedValue); + Assert.Equal(0x7FFF7FFF7FFF7FFFUL, Short4.FromScaledVector4(Vector4.One).PackedValue); + } - // act - var pixel = default(Short4); - pixel.FromScaledVector4(scaled); + [Fact] + public void Short4_BulkScaledConversionsCoverFullSignedRange() => + FeatureTestRunner.RunWithHwIntrinsicsFeature( + AssertShort4BulkScaledConversionsCoverFullSignedRange, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); - // assert - Assert.Equal((ulong)expected, pixel.PackedValue); + private static void AssertShort4BulkScaledConversionsCoverFullSignedRange() + { + const int length = 17; + Short4[] source = new Short4[length]; + Vector4[] expectedVectors = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + bool minimum = (i & 1) == 0; + source[i].PackedValue = minimum ? 0x8000800080008000UL : 0x7FFF7FFF7FFF7FFFUL; + expectedVectors[i] = minimum ? Vector4.Zero : Vector4.One; + } + + Vector4[] actualVectors = new Vector4[length]; + PixelOperations.Instance.ToVector4(Configuration.Default, source, actualVectors, PixelConversionModifiers.Scale); + Assert.Equal(expectedVectors, actualVectors); + + Vector4[] destructiveSource = [.. expectedVectors]; + Short4[] actualPixels = new Short4[length]; + PixelOperations.Instance.FromVector4Destructive(Configuration.Default, destructiveSource, actualPixels, PixelConversionModifiers.Scale); + Assert.Equal(source, actualPixels); } [Fact] 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,12 +99,11 @@ public void Short4_Clamping() 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 expected = new(172, 177, 243, 128); // act - shortValue.ToRgba32(ref actual); + Rgba32 actual = shortValue.ToRgba32(); // assert Assert.Equal(expected, actual); @@ -101,13 +113,11 @@ public void Short4_ToRgba32() public void Short4_FromRgba32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 0, 255); + 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); @@ -117,15 +127,12 @@ public void Short4_FromRgba32_ToRgba32() public void Short4_FromBgra32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Bgra32); - var expected = new Bgra32(20, 38, 0, 255); + 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); @@ -135,15 +142,12 @@ public void Short4_FromBgra32_ToRgba32() public void Short4_FromArgb32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Argb32); - var expected = new Argb32(20, 38, 0, 255); + 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); @@ -153,15 +157,12 @@ public void Short4_FromArgb32_ToRgba32() public void Short4_FromAbgrb32_ToRgba32() { // arrange - var short4 = default(Short4); - var actual = default(Abgr32); - var expected = new Abgr32(20, 38, 0, 255); + 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); @@ -171,13 +172,11 @@ public void Short4_FromAbgrb32_ToRgba32() public void Short4_FromRgb48_ToRgb48() { // arrange - var input = default(Short4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); + 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); @@ -187,13 +186,11 @@ public void Short4_FromRgb48_ToRgb48() public void Short4_FromRgba64_ToRgba64() { // arrange - var input = default(Short4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); + 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); @@ -203,13 +200,30 @@ public void Short4_FromRgba64_ToRgba64() public void Short4_FromBgra5551() { // arrange - var short4 = default(Short4); 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()); } + + [Fact] + public void Short4_PixelInformation() + { + 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); + 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/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 5cc35b43ed..eb93ba230e 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 void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() [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 void Color_Types_From_Floats_Produce_Equal_Scaled_Component_OutPut() [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 void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() [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 void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() [Fact] public void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() { - var color = Rgba32.ParseHex("183060C0"); - var colorVector = RgbaVector.FromHex("183060C0"); + Rgba32 color = Color.ParseHex("183060C0").ToPixel(); + 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 void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() [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,13 +81,11 @@ public void Color_Types_To_Vector4_Produce_Equal_OutPut() [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; - color.ToRgba32(ref rgba); - colorVector.ToRgba32(ref rgbaVector); + Rgba32 rgba = color.ToRgba32(); + Rgba32 rgbaVector = colorVector.ToRgba32(); Assert.Equal(rgba, rgbaVector); } @@ -95,8 +93,8 @@ public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() [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/PixelFormats/Vector4ConvertersTests.cs b/tests/ImageSharp.Tests/PixelFormats/Vector4ConvertersTests.cs new file mode 100644 index 0000000000..837d807f0c --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Vector4ConvertersTests.cs @@ -0,0 +1,100 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats.Utils; +using SixLabors.ImageSharp.Tests.TestUtilities; + +namespace SixLabors.ImageSharp.Tests.PixelFormats; + +/// +/// Verifies the shared stateful traversal used by affine pixel-vector conversions. +/// +[Trait("Category", "PixelFormats")] +public class Vector4ConvertersTests +{ + private static readonly int[] Lengths = [0, 1, 2, 3, 4, 5, 7, 8, 15, 16, 17, 257]; + + /// + /// Verifies multiply-then-add behavior for every SIMD boundary and the software fallback. + /// + [Fact] + public void MultiplyThenAddMatchesComponentArithmeticAcrossHardwareWidths() + => FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertMultiplyThenAddMatchesComponentArithmetic, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Verifies add-then-divide behavior for every SIMD boundary and the software fallback. + /// + [Fact] + public void AddThenDivideMatchesComponentArithmeticAcrossHardwareWidths() + => FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertAddThenDivideMatchesComponentArithmetic, HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + + /// + /// Compares the multiply-then-add traversal with independently evaluated component expressions. + /// + private static void AssertMultiplyThenAddMatchesComponentArithmetic() + { + Vector4 multiplier = new(2F, -3F, .5F, 4F); + Vector4 offset = new(-7F, 11F, 13F, -17F); + + foreach (int length in Lengths) + { + Vector4[] actual = CreateSource(length); + Vector4[] expected = new Vector4[length]; + + for (int i = 0; i < expected.Length; i++) + { + Vector4 value = actual[i]; + + expected[i] = new Vector4((value.X * multiplier.X) + offset.X, (value.Y * multiplier.Y) + offset.Y, (value.Z * multiplier.Z) + offset.Z, (value.W * multiplier.W) + offset.W); + } + + Vector4Converters.MultiplyThenAdd(actual, multiplier, offset); + + Assert.Equal(expected, actual); + } + } + + /// + /// Compares the add-then-divide traversal with independently evaluated component expressions. + /// + private static void AssertAddThenDivideMatchesComponentArithmetic() + { + Vector4 offset = new(-7F, 11F, 13F, -17F); + Vector4 divisor = new(2F, -3F, .5F, 4F); + + foreach (int length in Lengths) + { + Vector4[] actual = CreateSource(length); + Vector4[] expected = new Vector4[length]; + + for (int i = 0; i < expected.Length; i++) + { + Vector4 value = actual[i]; + + expected[i] = new Vector4((value.X + offset.X) / divisor.X, (value.Y + offset.Y) / divisor.Y, (value.Z + offset.Z) / divisor.Z, (value.W + offset.W) / divisor.W); + } + + Vector4Converters.AddThenDivide(actual, offset, divisor); + + Assert.Equal(expected, actual); + } + } + + /// + /// Creates non-uniform values that expose component ordering and traversal overlap errors. + /// + /// The number of vectors to create. + /// The populated vector buffer. + private static Vector4[] CreateSource(int length) + { + Vector4[] result = new Vector4[length]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = new Vector4((i * 17F) - 31F, (i * -23F) + 37F, (i * .25F) - 41F, (i * 3F) + 43F); + } + + return result; + } +} diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs index 9c0e83a7fe..0ea429a771 100644 --- a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -49,7 +49,7 @@ public void ColorMatrixMultiply() ColorMatrix value1 = CreateAllTwos(); ColorMatrix value2 = CreateAllThrees(); - var m = default(ColorMatrix); + ColorMatrix m = default(ColorMatrix); // First row m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41); diff --git a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs index ecc5923264..f83ebc53c5 100644 --- a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs @@ -38,7 +38,7 @@ public void DenseMatrixThrowsOnEmptyInitializer() [Fact] public void DenseMatrixReturnsCorrectDimensions() { - var dense = new DenseMatrix(FloydSteinbergMatrix); + DenseMatrix dense = new(FloydSteinbergMatrix); Assert.True(dense.Columns == FloydSteinbergMatrix.GetLength(1)); Assert.True(dense.Rows == FloydSteinbergMatrix.GetLength(0)); Assert.Equal(3, dense.Columns); @@ -63,7 +63,7 @@ public void DenseMatrixGetReturnsCorrectResults() [Fact] public void DenseMatrixGetSetReturnsCorrectResults() { - var dense = new DenseMatrix(4, 4); + DenseMatrix dense = new(4, 4); const int Val = 5; dense[3, 3] = Val; @@ -74,7 +74,7 @@ public void DenseMatrixGetSetReturnsCorrectResults() [Fact] public void DenseMatrixCanFillAndClear() { - var dense = new DenseMatrix(9); + DenseMatrix dense = new(9); dense.Fill(4); for (int i = 0; i < dense.Data.Length; i++) @@ -100,7 +100,7 @@ public void DenseMatrixCorrectlyCasts() [Fact] public void DenseMatrixCanTranspose() { - var dense = new DenseMatrix(3, 1); + DenseMatrix dense = new(3, 1); dense[0, 0] = 1; dense[0, 1] = 2; dense[0, 2] = 3; @@ -117,9 +117,9 @@ public void DenseMatrixCanTranspose() [Fact] public void DenseMatrixEquality() { - var dense = new DenseMatrix(3, 1); - var dense2 = new DenseMatrix(3, 1); - var dense3 = new DenseMatrix(1, 3); + DenseMatrix dense = new(3, 1); + DenseMatrix dense2 = new(3, 1); + DenseMatrix dense3 = new(1, 3); Assert.True(dense == dense2); Assert.False(dense != dense2); diff --git a/tests/ImageSharp.Tests/Primitives/PointFTests.cs b/tests/ImageSharp.Tests/Primitives/PointFTests.cs index c3d9e8176b..bea0f802fc 100644 --- a/tests/ImageSharp.Tests/Primitives/PointFTests.cs +++ b/tests/ImageSharp.Tests/Primitives/PointFTests.cs @@ -9,13 +9,12 @@ namespace SixLabors.ImageSharp.Tests; public class PointFTests { - private static readonly ApproximateFloatComparer ApproximateFloatComparer = - new ApproximateFloatComparer(1e-6f); + private static readonly ApproximateFloatComparer ApproximateFloatComparer = new(1e-6f); [Fact] public void CanReinterpretCastFromVector2() { - var vector = new Vector2(1, 2); + Vector2 vector = new(1, 2); PointF point = Unsafe.As(ref vector); @@ -37,7 +36,7 @@ public void DefaultConstructorTest() [InlineData(0.0, 0.0)] public void NonDefaultConstructorTest(float x, float y) { - var p1 = new PointF(x, y); + PointF p1 = new(x, y); Assert.Equal(x, p1.X); Assert.Equal(y, p1.Y); @@ -67,7 +66,7 @@ public void IsEmptyRandomTest(float x, float y) [InlineData(0, 0)] public void CoordinatesTest(float x, float y) { - var p = new PointF(x, y); + PointF p = new(x, y); Assert.Equal(x, p.X); Assert.Equal(y, p.Y); @@ -84,11 +83,11 @@ public void CoordinatesTest(float x, float y) [InlineData(0, 0, 0, 0)] public void ArithmeticTestWithSize(float x, float y, int x1, int y1) { - var p = new PointF(x, y); - var s = new Size(x1, y1); + PointF p = new(x, y); + Size s = new(x1, y1); - var addExpected = new PointF(x + x1, y + y1); - var subExpected = new PointF(x - x1, y - y1); + PointF addExpected = new(x + x1, y + y1); + PointF subExpected = new(x - x1, y - y1); Assert.Equal(addExpected, p + s); Assert.Equal(subExpected, p - s); Assert.Equal(addExpected, PointF.Add(p, s)); @@ -101,11 +100,11 @@ public void ArithmeticTestWithSize(float x, float y, int x1, int y1) [InlineData(0, 0)] public void ArithmeticTestWithSizeF(float x, float y) { - var p = new PointF(x, y); - var s = new SizeF(y, x); + PointF p = new(x, y); + SizeF s = new(y, x); - var addExpected = new PointF(x + y, y + x); - var subExpected = new PointF(x - y, y - x); + PointF addExpected = new(x + y, y + x); + PointF subExpected = new(x - y, y - x); Assert.Equal(addExpected, p + s); Assert.Equal(subExpected, p - s); Assert.Equal(addExpected, PointF.Add(p, s)); @@ -115,10 +114,10 @@ public void ArithmeticTestWithSizeF(float x, float y) [Fact] public void RotateTest() { - var p = new PointF(13, 17); + PointF p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); - var pout = PointF.Transform(p, matrix); + PointF pout = PointF.Transform(p, matrix); Assert.Equal(-2.82842732F, pout.X, ApproximateFloatComparer); Assert.Equal(21.2132034F, pout.Y, ApproximateFloatComparer); @@ -127,13 +126,76 @@ public void RotateTest() [Fact] public void SkewTest() { - var p = new PointF(13, 17); + PointF p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, PointF.Empty); - var pout = PointF.Transform(p, matrix); + PointF pout = PointF.Transform(p, matrix); Assert.Equal(new PointF(30, 30), pout); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + PointF p = new(13, 17); + Matrix3x2 m3 = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); + Matrix4x4 m4 = new(m3); + + PointF r3 = PointF.Transform(p, m3); + PointF r4 = PointF.Transform(p, m4); + + Assert.Equal(r3.X, r4.X, ApproximateFloatComparer); + Assert.Equal(r3.Y, r4.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + PointF p = new(42.5F, -17.3F); + PointF result = PointF.Transform(p, Matrix4x4.Identity); + + Assert.Equal(p.X, result.X, ApproximateFloatComparer); + Assert.Equal(p.Y, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + PointF p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + PointF result = PointF.Transform(p, m); + + Assert.Equal(15F, result.X, ApproximateFloatComparer); + Assert.Equal(17F, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Scale() + { + PointF p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateScale(2, 3, 1); + PointF result = PointF.Transform(p, m); + + Assert.Equal(20F, result.X, ApproximateFloatComparer); + Assert.Equal(60F, result.Y, ApproximateFloatComparer); + } + + [Fact] + public void TransformMatrix4x4_Projective() + { + // A taper matrix with M14 != 0 produces W != 1, requiring perspective divide. + PointF p = new(100, 50); + Matrix4x4 m = Matrix4x4.Identity; + m.M14 = 0.005F; // perspective component + + PointF result = PointF.Transform(p, m); + + // W = x*M14 + M44 = 100*0.005 + 1 = 1.5 + // X = x*M11 + M41 = 100, Y = y*M22 + M42 = 50 + // result = (100/1.5, 50/1.5) + Assert.Equal(100F / 1.5F, result.X, ApproximateFloatComparer); + Assert.Equal(50F / 1.5F, result.Y, ApproximateFloatComparer); + } + [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MaxValue)] @@ -142,8 +204,8 @@ public void SkewTest() [InlineData(0, 0)] public void EqualityTest(float x, float y) { - var pLeft = new PointF(x, y); - var pRight = new PointF(y, x); + PointF pLeft = new(x, y); + PointF pRight = new(y, x); if (x == y) { @@ -164,7 +226,7 @@ public void EqualityTest(float x, float y) [Fact] public void EqualityTest_NotPointF() { - var point = new PointF(0, 0); + PointF point = new(0, 0); Assert.False(point.Equals(null)); Assert.False(point.Equals(0)); @@ -180,7 +242,7 @@ public void EqualityTest_NotPointF() [Fact] public void GetHashCodeTest() { - var point = new PointF(10, 10); + PointF point = new(10, 10); Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); @@ -189,7 +251,7 @@ public void GetHashCodeTest() [Fact] public void ToStringTest() { - var p = new PointF(5.1F, -5.123F); + PointF p = new(5.1F, -5.123F); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); } @@ -200,7 +262,7 @@ public void ToStringTest() [InlineData(0, 0)] public void DeconstructTest(float x, float y) { - PointF p = new PointF(x, y); + PointF p = new(x, y); (float deconstructedX, float deconstructedY) = p; diff --git a/tests/ImageSharp.Tests/Primitives/PointTests.cs b/tests/ImageSharp.Tests/Primitives/PointTests.cs index f5c64abf52..d2192f53bb 100644 --- a/tests/ImageSharp.Tests/Primitives/PointTests.cs +++ b/tests/ImageSharp.Tests/Primitives/PointTests.cs @@ -21,8 +21,8 @@ public void DefaultConstructorTest() [InlineData(0, 0)] public void NonDefaultConstructorTest(int x, int y) { - var p1 = new Point(x, y); - var p2 = new Point(new Size(x, y)); + Point p1 = new(x, y); + Point p2 = new(new Size(x, y)); Assert.Equal(p1, p2); } @@ -33,8 +33,8 @@ public void NonDefaultConstructorTest(int x, int y) [InlineData(0)] public void SingleIntConstructorTest(int x) { - var p1 = new Point(x); - var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); + Point p1 = new(x); + Point p2 = new(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); Assert.Equal(p1, p2); } @@ -63,7 +63,7 @@ public void IsEmptyRandomTest(int x, int y) [InlineData(0, 0)] public void CoordinatesTest(int x, int y) { - var p = new Point(x, y); + Point p = new(x, y); Assert.Equal(x, p.X); Assert.Equal(y, p.Y); } @@ -86,7 +86,7 @@ public void PointFConversionTest(int x, int y) [InlineData(0, 0)] public void SizeConversionTest(int x, int y) { - var sz = (Size)new Point(x, y); + Size sz = (Size)new Point(x, y); Assert.Equal(new Size(x, y), sz); } @@ -97,8 +97,8 @@ public void SizeConversionTest(int x, int y) [InlineData(0, 0)] public void ArithmeticTest(int x, int y) { - Point addExpected, subExpected, p = new Point(x, y); - var s = new Size(y, x); + Point addExpected, subExpected, p = new(x, y); + Size s = new(y, x); unchecked { @@ -119,7 +119,7 @@ public void ArithmeticTest(int x, int y) [InlineData(0, 0)] public void PointFMathematicalTest(float x, float y) { - var pf = new PointF(x, y); + PointF pf = new(x, y); Point pCeiling, pTruncate, pRound; unchecked @@ -141,8 +141,8 @@ public void PointFMathematicalTest(float x, float y) [InlineData(0, 0)] public void OffsetTest(int x, int y) { - var p1 = new Point(x, y); - var p2 = new Point(y, x); + Point p1 = new(x, y); + Point p2 = new(y, x); p1.Offset(p2); @@ -156,22 +156,71 @@ public void OffsetTest(int x, int y) [Fact] public void RotateTest() { - var p = new Point(13, 17); + Point p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); - var pout = Point.Transform(p, matrix); + PointF pout = Point.Transform(p, matrix); - Assert.Equal(new Point(-3, 21), pout); + Assert.Equal(-2.828427F, pout.X, 4); + Assert.Equal(21.213203F, pout.Y, 4); } [Fact] public void SkewTest() { - var p = new Point(13, 17); + Point p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, Point.Empty); - var pout = Point.Transform(p, matrix); - Assert.Equal(new Point(30, 30), pout); + PointF pout = Point.Transform(p, matrix); + Assert.Equal(30F, pout.X, 4); + Assert.Equal(30F, pout.Y, 4); + } + + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + Point p = new(13, 17); + Matrix3x2 m3 = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); + Matrix4x4 m4 = new(m3); + + PointF r3 = Point.Transform(p, m3); + PointF r4 = Point.Transform(p, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + Point p = new(42, -17); + PointF result = Point.Transform(p, Matrix4x4.Identity); + + Assert.Equal((PointF)p, result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + Point p = new(10, 20); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + PointF result = Point.Transform(p, m); + + Assert.Equal(15F, result.X, 4); + Assert.Equal(17F, result.Y, 4); + } + + [Fact] + public void TransformMatrix4x4_Projective() + { + Point p = new(100, 50); + Matrix4x4 m = Matrix4x4.Identity; + m.M14 = 0.005F; + + PointF result = Point.Transform(p, m); + + // W = 100*0.005 + 1 = 1.5 => (100/1.5, 50/1.5) + Assert.Equal(100F / 1.5F, result.X, 4); + Assert.Equal(50F / 1.5F, result.Y, 4); } [Theory] @@ -181,9 +230,9 @@ public void SkewTest() [InlineData(0, 0)] public void EqualityTest(int x, int y) { - var p1 = new Point(x, y); - var p2 = new Point((x / 2) - 1, (y / 2) - 1); - var p3 = new Point(x, y); + Point p1 = new(x, y); + Point p2 = new((x / 2) - 1, (y / 2) - 1); + Point p3 = new(x, y); Assert.True(p1 == p3); Assert.True(p1 != p2); @@ -203,7 +252,7 @@ public void EqualityTest(int x, int y) [Fact] public void EqualityTest_NotPoint() { - var point = new Point(0, 0); + Point point = new(0, 0); Assert.False(point.Equals(null)); Assert.False(point.Equals(0)); Assert.False(point.Equals(new PointF(0, 0))); @@ -212,7 +261,7 @@ public void EqualityTest_NotPoint() [Fact] public void GetHashCodeTest() { - var point = new Point(10, 10); + Point point = new(10, 10); Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode()); @@ -223,7 +272,7 @@ public void GetHashCodeTest() [InlineData(1, -2, 3, -4)] public void ConversionTest(int x, int y, int width, int height) { - var rect = new Rectangle(x, y, width, height); + Rectangle rect = new(x, y, width, height); RectangleF rectF = rect; Assert.Equal(x, rectF.X); Assert.Equal(y, rectF.Y); @@ -234,7 +283,7 @@ public void ConversionTest(int x, int y, int width, int height) [Fact] public void ToStringTest() { - var p = new Point(5, -5); + Point p = new(5, -5); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); } @@ -245,7 +294,7 @@ public void ToStringTest() [InlineData(0, 0)] public void DeconstructTest(int x, int y) { - Point p = new Point(x, y); + Point p = new(x, y); (int deconstructedX, int deconstructedY) = p; diff --git a/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs index a9b3251ca3..9a6ff0957b 100644 --- a/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs +++ b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; namespace SixLabors.ImageSharp.Tests; @@ -23,10 +24,10 @@ public void DefaultConstructorTest() [InlineData(0, float.MinValue, float.MaxValue, 0)] public void NonDefaultConstructorTest(float x, float y, float width, float height) { - var rect1 = new RectangleF(x, y, width, height); - var p = new PointF(x, y); - var s = new SizeF(width, height); - var rect2 = new RectangleF(p, s); + RectangleF rect1 = new(x, y, width, height); + PointF p = new(x, y); + SizeF s = new(width, height); + RectangleF rect2 = new(p, s); Assert.Equal(rect1, rect2); } @@ -38,8 +39,8 @@ public void NonDefaultConstructorTest(float x, float y, float width, float heigh [InlineData(0, float.MinValue, float.MaxValue, 0)] public void FromLTRBTest(float left, float top, float right, float bottom) { - var expected = new RectangleF(left, top, right - left, bottom - top); - var actual = RectangleF.FromLTRB(left, top, right, bottom); + RectangleF expected = new(left, top, right - left, bottom - top); + RectangleF actual = RectangleF.FromLTRB(left, top, right, bottom); Assert.Equal(expected, actual); } @@ -51,9 +52,9 @@ public void FromLTRBTest(float left, float top, float right, float bottom) [InlineData(0, float.MinValue, float.MaxValue, 0)] public void DimensionsTest(float x, float y, float width, float height) { - var rect = new RectangleF(x, y, width, height); - var p = new PointF(x, y); - var s = new SizeF(width, height); + RectangleF rect = new(x, y, width, height); + PointF p = new(x, y); + SizeF s = new(width, height); Assert.Equal(p, rect.Location); Assert.Equal(s, rect.Size); @@ -84,8 +85,8 @@ public void IsEmptyTest() [InlineData(float.MaxValue, float.MinValue)] public void LocationSetTest(float x, float y) { - var point = new PointF(x, y); - var rect = new RectangleF(10, 10, 10, 10) { Location = point }; + PointF point = new(x, y); + RectangleF rect = new(10, 10, 10, 10) { Location = point }; Assert.Equal(point, rect.Location); Assert.Equal(point.X, rect.X); Assert.Equal(point.Y, rect.Y); @@ -96,8 +97,8 @@ public void LocationSetTest(float x, float y) [InlineData(float.MaxValue, float.MinValue)] public void SizeSetTest(float x, float y) { - var size = new SizeF(x, y); - var rect = new RectangleF(10, 10, 10, 10) { Size = size }; + SizeF size = new(x, y); + RectangleF rect = new(10, 10, 10, 10) { Size = size }; Assert.Equal(size, rect.Size); Assert.Equal(size.Width, rect.Width); Assert.Equal(size.Height, rect.Height); @@ -109,8 +110,8 @@ public void SizeSetTest(float x, float y) [InlineData(0, float.MinValue, float.MaxValue, 0)] public void EqualityTest(float x, float y, float width, float height) { - var rect1 = new RectangleF(x, y, width, height); - var rect2 = new RectangleF(width, height, x, y); + RectangleF rect1 = new(x, y, width, height); + RectangleF rect2 = new(width, height, x, y); Assert.True(rect1 != rect2); Assert.False(rect1 == rect2); @@ -121,7 +122,7 @@ public void EqualityTest(float x, float y, float width, float height) [Fact] public void EqualityTestNotRectangleF() { - var rectangle = new RectangleF(0, 0, 0, 0); + RectangleF rectangle = new(0, 0, 0, 0); Assert.False(rectangle.Equals(null)); Assert.False(rectangle.Equals(0)); @@ -137,8 +138,8 @@ public void EqualityTestNotRectangleF() [Fact] public void GetHashCodeTest() { - var rect1 = new RectangleF(10, 10, 10, 10); - var rect2 = new RectangleF(10, 10, 10, 10); + RectangleF rect1 = new(10, 10, 10, 10); + RectangleF rect2 = new(10, 10, 10, 10); Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode()); Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode()); @@ -151,11 +152,11 @@ public void GetHashCodeTest() [InlineData(0, float.MinValue, float.MaxValue, 0)] public void ContainsTest(float x, float y, float width, float height) { - var rect = new RectangleF(x, y, width, height); + RectangleF rect = new(x, y, width, height); float x1 = (x + width) / 2; float y1 = (y + height) / 2; - var p = new PointF(x1, y1); - var r = new RectangleF(x1, y1, width / 2, height / 2); + PointF p = new(x1, y1); + RectangleF r = new(x1, y1, width / 2, height / 2); Assert.False(rect.Contains(x1, y1)); Assert.False(rect.Contains(p)); @@ -168,13 +169,13 @@ public void ContainsTest(float x, float y, float width, float height) [InlineData(0, float.MinValue, float.MaxValue, 0)] public void InflateTest(float x, float y, float width, float height) { - var rect = new RectangleF(x, y, width, height); - var inflatedRect = new RectangleF(x - width, y - height, width + (2 * width), height + (2 * height)); + RectangleF rect = new(x, y, width, height); + RectangleF inflatedRect = new(x - width, y - height, width + (2 * width), height + (2 * height)); rect.Inflate(width, height); Assert.Equal(inflatedRect, rect); - var s = new SizeF(x, y); + SizeF s = new(x, y); inflatedRect = RectangleF.Inflate(rect, x, y); rect.Inflate(s); @@ -186,9 +187,9 @@ public void InflateTest(float x, float y, float width, float height) [InlineData(0, float.MinValue, float.MaxValue, 0)] public void IntersectTest(float x, float y, float width, float height) { - var rect1 = new RectangleF(x, y, width, height); - var rect2 = new RectangleF(y, x, width, height); - var expectedRect = RectangleF.Intersect(rect1, rect2); + RectangleF rect1 = new(x, y, width, height); + RectangleF rect2 = new(y, x, width, height); + RectangleF expectedRect = RectangleF.Intersect(rect1, rect2); rect1.Intersect(rect2); Assert.Equal(expectedRect, rect1); Assert.False(rect1.IntersectsWith(expectedRect)); @@ -197,9 +198,9 @@ public void IntersectTest(float x, float y, float width, float height) [Fact] public void IntersectIntersectingRectsTest() { - var rect1 = new RectangleF(0, 0, 5, 5); - var rect2 = new RectangleF(1, 1, 3, 3); - var expected = new RectangleF(1, 1, 3, 3); + RectangleF rect1 = new(0, 0, 5, 5); + RectangleF rect2 = new(1, 1, 3, 3); + RectangleF expected = new(1, 1, 3, 3); Assert.Equal(expected, RectangleF.Intersect(rect1, rect2)); } @@ -211,15 +212,15 @@ public void IntersectIntersectingRectsTest() [InlineData(0, float.MinValue, float.MaxValue, 0)] public void UnionTest(float x, float y, float width, float height) { - var a = new RectangleF(x, y, width, height); - var b = new RectangleF(width, height, x, y); + RectangleF a = new(x, y, width, height); + RectangleF b = new(width, height, x, y); float x1 = Math.Min(a.X, b.X); float x2 = Math.Max(a.X + a.Width, b.X + b.Width); float y1 = Math.Min(a.Y, b.Y); float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); - var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1); + RectangleF expectedRectangle = new(x1, y1, x2 - x1, y2 - y1); Assert.Equal(expectedRectangle, RectangleF.Union(a, b)); } @@ -231,9 +232,9 @@ public void UnionTest(float x, float y, float width, float height) [InlineData(0, float.MinValue, float.MaxValue, 0)] public void OffsetTest(float x, float y, float width, float height) { - var r1 = new RectangleF(x, y, width, height); - var expectedRect = new RectangleF(x + width, y + height, width, height); - var p = new PointF(width, height); + RectangleF r1 = new(x, y, width, height); + RectangleF expectedRect = new(x + width, y + height, width, height); + PointF p = new(width, height); r1.Offset(p); Assert.Equal(expectedRect, r1); @@ -243,10 +244,65 @@ public void OffsetTest(float x, float y, float width, float height) Assert.Equal(expectedRect, r1); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix3x2 m3 = Matrix3x2.CreateTranslation(5, -3); + Matrix4x4 m4 = new(m3); + + RectangleF r3 = RectangleF.Transform(rect, m3); + RectangleF r4 = RectangleF.Transform(rect, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + RectangleF rect = new(10, 20, 100, 50); + RectangleF result = RectangleF.Transform(rect, Matrix4x4.Identity); + + Assert.Equal(rect, result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(new RectangleF(15, 17, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_Scale() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateScale(2, 3, 1); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(new RectangleF(20, 60, 200, 150), result); + } + + [Fact] + public void TransformMatrix4x4_RotationReturnsBoundingBox() + { + RectangleF rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateRotationZ(MathF.PI / 2F); + RectangleF result = RectangleF.Transform(rect, m); + + Assert.Equal(-70F, result.X, 4); + Assert.Equal(10F, result.Y, 4); + Assert.Equal(50F, result.Width, 4); + Assert.Equal(100F, result.Height, 4); + } + [Fact] public void ToStringTest() { - var r = new RectangleF(5, 5.1F, 1.3F, 1); + RectangleF r = new(5, 5.1F, 1.3F, 1); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); } @@ -270,7 +326,7 @@ public void ToStringTest() [InlineData(0, 0, 0, 0)] public void DeconstructTest(float x, float y, float width, float height) { - RectangleF r = new RectangleF(x, y, width, height); + RectangleF r = new(x, y, width, height); (float dx, float dy, float dw, float dh) = r; diff --git a/tests/ImageSharp.Tests/Primitives/RectangleTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs index 2ba3a77315..a8ce990789 100644 --- a/tests/ImageSharp.Tests/Primitives/RectangleTests.cs +++ b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; namespace SixLabors.ImageSharp.Tests; @@ -23,8 +24,8 @@ public void DefaultConstructorTest() [InlineData(0, int.MinValue, 0, int.MaxValue)] public void NonDefaultConstructorTest(int x, int y, int width, int height) { - var rect1 = new Rectangle(x, y, width, height); - var rect2 = new Rectangle(new Point(x, y), new Size(width, height)); + Rectangle rect1 = new(x, y, width, height); + Rectangle rect2 = new(new Point(x, y), new Size(width, height)); Assert.Equal(rect1, rect2); } @@ -36,8 +37,8 @@ public void NonDefaultConstructorTest(int x, int y, int width, int height) [InlineData(0, int.MinValue, 0, int.MaxValue)] public void FromLTRBTest(int left, int top, int right, int bottom) { - var rect1 = new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); - var rect2 = Rectangle.FromLTRB(left, top, right, bottom); + Rectangle rect1 = new(left, top, unchecked(right - left), unchecked(bottom - top)); + Rectangle rect2 = Rectangle.FromLTRB(left, top, right, bottom); Assert.Equal(rect1, rect2); } @@ -68,7 +69,7 @@ public void NonEmptyTest(int x, int y, int width, int height) [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] public void DimensionsTest(int x, int y, int width, int height) { - var rect = new Rectangle(x, y, width, height); + Rectangle rect = new(x, y, width, height); Assert.Equal(new Point(x, y), rect.Location); Assert.Equal(new Size(width, height), rect.Size); @@ -81,8 +82,8 @@ public void DimensionsTest(int x, int y, int width, int height) Assert.Equal(unchecked(x + width), rect.Right); Assert.Equal(unchecked(y + height), rect.Bottom); - var p = new Point(width, height); - var s = new Size(x, y); + Point p = new(width, height); + Size s = new(x, y); rect.Location = p; rect.Size = s; @@ -104,8 +105,8 @@ public void DimensionsTest(int x, int y, int width, int height) [InlineData(int.MaxValue, int.MinValue)] public void LocationSetTest(int x, int y) { - var point = new Point(x, y); - var rect = new Rectangle(10, 10, 10, 10) { Location = point }; + Point point = new(x, y); + Rectangle rect = new(10, 10, 10, 10) { Location = point }; Assert.Equal(point, rect.Location); Assert.Equal(point.X, rect.X); Assert.Equal(point.Y, rect.Y); @@ -116,8 +117,8 @@ public void LocationSetTest(int x, int y) [InlineData(int.MaxValue, int.MinValue)] public void SizeSetTest(int x, int y) { - var size = new Size(x, y); - var rect = new Rectangle(10, 10, 10, 10) { Size = size }; + Size size = new(x, y); + Rectangle rect = new(10, 10, 10, 10) { Size = size }; Assert.Equal(size, rect.Size); Assert.Equal(size.Width, rect.Width); Assert.Equal(size.Height, rect.Height); @@ -130,8 +131,8 @@ public void SizeSetTest(int x, int y) [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] public void EqualityTest(int x, int y, int width, int height) { - var rect1 = new Rectangle(x, y, width, height); - var rect2 = new Rectangle(width / 2, height / 2, x, y); + Rectangle rect1 = new(x, y, width, height); + Rectangle rect2 = new(width / 2, height / 2, x, y); Assert.True(rect1 != rect2); Assert.False(rect1 == rect2); @@ -142,7 +143,7 @@ public void EqualityTest(int x, int y, int width, int height) [Fact] public void EqualityTestNotRectangle() { - var rectangle = new Rectangle(0, 0, 0, 0); + Rectangle rectangle = new(0, 0, 0, 0); Assert.False(rectangle.Equals(null)); Assert.False(rectangle.Equals(0)); Assert.False(rectangle.Equals(new RectangleF(0, 0, 0, 0))); @@ -151,8 +152,8 @@ public void EqualityTestNotRectangle() [Fact] public void GetHashCodeTest() { - var rect1 = new Rectangle(10, 10, 10, 10); - var rect2 = new Rectangle(10, 10, 10, 10); + Rectangle rect1 = new(10, 10, 10, 10); + Rectangle rect2 = new(10, 10, 10, 10); Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); Assert.NotEqual(rect1.GetHashCode(), new Rectangle(20, 10, 10, 10).GetHashCode()); Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 20, 10, 10).GetHashCode()); @@ -166,7 +167,7 @@ public void GetHashCodeTest() [InlineData(0, 0, 0, 0)] public void RectangleFConversionTest(float x, float y, float width, float height) { - var rect = new RectangleF(x, y, width, height); + RectangleF rect = new(x, y, width, height); Rectangle rCeiling, rTruncate, rRound; unchecked @@ -196,9 +197,9 @@ public void RectangleFConversionTest(float x, float y, float width, float height [InlineData(0, int.MinValue, int.MaxValue, 0)] public void ContainsTest(int x, int y, int width, int height) { - var rect = new Rectangle(unchecked((2 * x) - width), unchecked((2 * y) - height), width, height); - var p = new Point(x, y); - var r = new Rectangle(x, y, width / 2, height / 2); + Rectangle rect = new(unchecked((2 * x) - width), unchecked((2 * y) - height), width, height); + Point p = new(x, y); + Rectangle r = new(x, y, width / 2, height / 2); Assert.False(rect.Contains(x, y)); Assert.False(rect.Contains(p)); @@ -211,7 +212,7 @@ public void ContainsTest(int x, int y, int width, int height) [InlineData(0, int.MinValue, int.MaxValue, 0)] public void InflateTest(int x, int y, int width, int height) { - Rectangle inflatedRect, rect = new Rectangle(x, y, width, height); + Rectangle inflatedRect, rect = new(x, y, width, height); unchecked { inflatedRect = new Rectangle(x - width, y - height, width + (2 * width), height + (2 * height)); @@ -222,7 +223,7 @@ public void InflateTest(int x, int y, int width, int height) rect.Inflate(width, height); Assert.Equal(inflatedRect, rect); - var s = new Size(x, y); + Size s = new(x, y); unchecked { inflatedRect = new Rectangle(rect.X - x, rect.Y - y, rect.Width + (2 * x), rect.Height + (2 * y)); @@ -238,8 +239,8 @@ public void InflateTest(int x, int y, int width, int height) [InlineData(0, int.MinValue, int.MaxValue, 0)] public void IntersectTest(int x, int y, int width, int height) { - var rect = new Rectangle(x, y, width, height); - var expectedRect = Rectangle.Intersect(rect, rect); + Rectangle rect = new(x, y, width, height); + Rectangle expectedRect = Rectangle.Intersect(rect, rect); rect.Intersect(rect); Assert.Equal(expectedRect, rect); Assert.False(rect.IntersectsWith(expectedRect)); @@ -248,9 +249,9 @@ public void IntersectTest(int x, int y, int width, int height) [Fact] public void IntersectIntersectingRectsTest() { - var rect1 = new Rectangle(0, 0, 5, 5); - var rect2 = new Rectangle(1, 1, 3, 3); - var expected = new Rectangle(1, 1, 3, 3); + Rectangle rect1 = new(0, 0, 5, 5); + Rectangle rect2 = new(1, 1, 3, 3); + Rectangle expected = new(1, 1, 3, 3); Assert.Equal(expected, Rectangle.Intersect(rect1, rect2)); } @@ -262,15 +263,15 @@ public void IntersectIntersectingRectsTest() [InlineData(0, int.MinValue, int.MaxValue, 0)] public void UnionTest(int x, int y, int width, int height) { - var a = new Rectangle(x, y, width, height); - var b = new Rectangle(width, height, x, y); + Rectangle a = new(x, y, width, height); + Rectangle b = new(width, height, x, y); int x1 = Math.Min(a.X, b.X); int x2 = Math.Max(a.X + a.Width, b.X + b.Width); int y1 = Math.Min(a.Y, b.Y); int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); - var expectedRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); + Rectangle expectedRectangle = new(x1, y1, x2 - x1, y2 - y1); Assert.Equal(expectedRectangle, Rectangle.Union(a, b)); } @@ -282,9 +283,9 @@ public void UnionTest(int x, int y, int width, int height) [InlineData(0, int.MinValue, int.MaxValue, 0)] public void OffsetTest(int x, int y, int width, int height) { - var r1 = new Rectangle(x, y, width, height); - var expectedRect = new Rectangle(x + width, y + height, width, height); - var p = new Point(width, height); + Rectangle r1 = new(x, y, width, height); + Rectangle expectedRect = new(x + width, y + height, width, height); + Point p = new(width, height); r1.Offset(p); Assert.Equal(expectedRect, r1); @@ -294,10 +295,55 @@ public void OffsetTest(int x, int y, int width, int height) Assert.Equal(expectedRect, r1); } + [Fact] + public void TransformMatrix4x4_AffineMatchesMatrix3x2() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix3x2 m3 = Matrix3x2.CreateTranslation(5, -3); + Matrix4x4 m4 = new(m3); + + RectangleF r3 = Rectangle.Transform(rect, m3); + RectangleF r4 = Rectangle.Transform(rect, m4); + + Assert.Equal(r3, r4); + } + + [Fact] + public void TransformMatrix4x4_Identity() + { + Rectangle rect = new(10, 20, 100, 50); + RectangleF result = Rectangle.Transform(rect, Matrix4x4.Identity); + + Assert.Equal(new RectangleF(10, 20, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_Translation() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); + RectangleF result = Rectangle.Transform(rect, m); + + Assert.Equal(new RectangleF(15, 17, 100, 50), result); + } + + [Fact] + public void TransformMatrix4x4_RotationReturnsBoundingBox() + { + Rectangle rect = new(10, 20, 100, 50); + Matrix4x4 m = Matrix4x4.CreateRotationZ(MathF.PI / 2F); + RectangleF result = Rectangle.Transform(rect, m); + + Assert.Equal(-70F, result.X, 4); + Assert.Equal(10F, result.Y, 4); + Assert.Equal(50F, result.Width, 4); + Assert.Equal(100F, result.Height, 4); + } + [Fact] public void ToStringTest() { - var r = new Rectangle(5, -5, 0, 1); + Rectangle r = new(5, -5, 0, 1); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Rectangle [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); } @@ -321,7 +367,7 @@ public void ToStringTest() [InlineData(0, 0, 0, 0)] public void DeconstructTest(int x, int y, int width, int height) { - var r = new Rectangle(x, y, width, height); + Rectangle r = new(x, y, width, height); (int dx, int dy, int dw, int dh) = r; diff --git a/tests/ImageSharp.Tests/Primitives/SizeFTests.cs b/tests/ImageSharp.Tests/Primitives/SizeFTests.cs index 1a36b4921a..2893a7c715 100644 --- a/tests/ImageSharp.Tests/Primitives/SizeFTests.cs +++ b/tests/ImageSharp.Tests/Primitives/SizeFTests.cs @@ -20,9 +20,9 @@ public void DefaultConstructorTest() [InlineData(0, 0)] public void NonDefaultConstructorAndDimensionsTest(float width, float height) { - var s1 = new SizeF(width, height); - var p1 = new PointF(width, height); - var s2 = new SizeF(s1); + SizeF s1 = new(width, height); + PointF p1 = new(width, height); + SizeF s2 = new(s1); Assert.Equal(s1, s2); Assert.Equal(s1, new SizeF(p1)); @@ -62,10 +62,10 @@ public void IsEmptyRandomTest(float width, float height) [InlineData(0, 0)] public void ArithmeticTest(float width, float height) { - var s1 = new SizeF(width, height); - var s2 = new SizeF(height, width); - var addExpected = new SizeF(width + height, width + height); - var subExpected = new SizeF(width - height, height - width); + SizeF s1 = new(width, height); + SizeF s2 = new(height, width); + SizeF addExpected = new(width + height, width + height); + SizeF subExpected = new(width - height, height - width); Assert.Equal(addExpected, s1 + s2); Assert.Equal(addExpected, SizeF.Add(s1, s2)); @@ -81,8 +81,8 @@ public void ArithmeticTest(float width, float height) [InlineData(0, 0)] public void EqualityTest(float width, float height) { - var sLeft = new SizeF(width, height); - var sRight = new SizeF(height, width); + SizeF sLeft = new(width, height); + SizeF sRight = new(height, width); if (width == height) { @@ -103,7 +103,7 @@ public void EqualityTest(float width, float height) [Fact] public void EqualityTest_NotSizeF() { - var size = new SizeF(0, 0); + SizeF size = new(0, 0); Assert.False(size.Equals(null)); Assert.False(size.Equals(0)); @@ -119,7 +119,7 @@ public void EqualityTest_NotSizeF() [Fact] public void GetHashCodeTest() { - var size = new SizeF(10, 10); + SizeF size = new(10, 10); Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode()); Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode()); Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode()); @@ -132,9 +132,9 @@ public void GetHashCodeTest() [InlineData(0, 0)] public void ConversionTest(float width, float height) { - var s1 = new SizeF(width, height); - var p1 = (PointF)s1; - var s2 = new Size(unchecked((int)width), unchecked((int)height)); + SizeF s1 = new(width, height); + PointF p1 = (PointF)s1; + Size s2 = new(unchecked((int)width), unchecked((int)height)); Assert.Equal(new PointF(width, height), p1); Assert.Equal(p1, (PointF)s1); @@ -144,7 +144,7 @@ public void ConversionTest(float width, float height) [Fact] public void ToStringTest() { - var sz = new SizeF(10, 5); + SizeF sz = new(10, 5); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); } @@ -172,7 +172,7 @@ public void ToStringTest() [InlineData(float.MinValue, float.MinValue)] public void MultiplicationTest(float dimension, float multiplier) { - SizeF sz1 = new SizeF(dimension, dimension); + SizeF sz1 = new(dimension, dimension); SizeF mulExpected; mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); @@ -185,7 +185,7 @@ public void MultiplicationTest(float dimension, float multiplier) [InlineData(1111.1111f, 2222.2222f, 3333.3333f)] public void MultiplicationTestWidthHeightMultiplier(float width, float height, float multiplier) { - SizeF sz1 = new SizeF(width, height); + SizeF sz1 = new(width, height); SizeF mulExpected; mulExpected = new SizeF(width * multiplier, height * multiplier); @@ -215,8 +215,8 @@ public void MultiplicationTestWidthHeightMultiplier(float width, float height, f [InlineData(-1.0f, float.MaxValue)] public void DivideTestSizeFloat(float dimension, float divisor) { - SizeF size = new SizeF(dimension, dimension); - SizeF expected = new SizeF(dimension / divisor, dimension / divisor); + SizeF size = new(dimension, dimension); + SizeF expected = new(dimension / divisor, dimension / divisor); Assert.Equal(expected, size / divisor); } @@ -224,8 +224,8 @@ public void DivideTestSizeFloat(float dimension, float divisor) [InlineData(-111.111f, 222.222f, 333.333f)] public void DivideTestSizeFloatWidthHeightDivisor(float width, float height, float divisor) { - SizeF size = new SizeF(width, height); - SizeF expected = new SizeF(width / divisor, height / divisor); + SizeF size = new(width, height); + SizeF expected = new(width / divisor, height / divisor); Assert.Equal(expected, size / divisor); } @@ -236,7 +236,7 @@ public void DivideTestSizeFloatWidthHeightDivisor(float width, float height, flo [InlineData(0, 0)] public void DeconstructTest(float width, float height) { - SizeF s = new SizeF(width, height); + SizeF s = new(width, height); (float deconstructedWidth, float deconstructedHeight) = s; diff --git a/tests/ImageSharp.Tests/Primitives/SizeTests.cs b/tests/ImageSharp.Tests/Primitives/SizeTests.cs index eec1fb63b0..63320796a1 100644 --- a/tests/ImageSharp.Tests/Primitives/SizeTests.cs +++ b/tests/ImageSharp.Tests/Primitives/SizeTests.cs @@ -23,8 +23,8 @@ public void DefaultConstructorTest() [InlineData(0, 0)] public void NonDefaultConstructorTest(int width, int height) { - var s1 = new Size(width, height); - var s2 = new Size(new Point(width, height)); + Size s1 = new(width, height); + Size s2 = new(new Point(width, height)); Assert.Equal(s1, s2); @@ -59,7 +59,7 @@ public void IsEmptyRandomTest(int width, int height) [InlineData(0, 0)] public void DimensionsTest(int width, int height) { - var p = new Size(width, height); + Size p = new(width, height); Assert.Equal(width, p.Width); Assert.Equal(height, p.Height); } @@ -82,7 +82,7 @@ public void PointFConversionTest(int width, int height) [InlineData(0, 0)] public void SizeConversionTest(int width, int height) { - var sz = (Point)new Size(width, height); + Point sz = (Point)new Size(width, height); Assert.Equal(new Point(width, height), sz); } @@ -93,8 +93,8 @@ public void SizeConversionTest(int width, int height) [InlineData(0, 0)] public void ArithmeticTest(int width, int height) { - var sz1 = new Size(width, height); - var sz2 = new Size(height, width); + Size sz1 = new(width, height); + Size sz2 = new(height, width); Size addExpected, subExpected; unchecked @@ -116,7 +116,7 @@ public void ArithmeticTest(int width, int height) [InlineData(0, 0)] public void PointFMathematicalTest(float width, float height) { - var szF = new SizeF(width, height); + SizeF szF = new(width, height); Size pCeiling, pTruncate, pRound; unchecked @@ -138,9 +138,9 @@ public void PointFMathematicalTest(float width, float height) [InlineData(0, 0)] public void EqualityTest(int width, int height) { - var p1 = new Size(width, height); - var p2 = new Size(unchecked(width - 1), unchecked(height - 1)); - var p3 = new Size(width, height); + Size p1 = new(width, height); + Size p2 = new(unchecked(width - 1), unchecked(height - 1)); + Size p3 = new(width, height); Assert.True(p1 == p3); Assert.True(p1 != p2); @@ -160,7 +160,7 @@ public void EqualityTest(int width, int height) [Fact] public void EqualityTest_NotSize() { - var size = new Size(0, 0); + Size size = new(0, 0); Assert.False(size.Equals(null)); Assert.False(size.Equals(0)); Assert.False(size.Equals(new SizeF(0, 0))); @@ -169,7 +169,7 @@ public void EqualityTest_NotSize() [Fact] public void GetHashCodeTest() { - var size = new Size(10, 10); + Size size = new(10, 10); Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode()); Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode()); Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode()); @@ -178,7 +178,7 @@ public void GetHashCodeTest() [Fact] public void ToStringTest() { - var sz = new Size(10, 5); + Size sz = new(10, 5); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); } @@ -206,7 +206,7 @@ public void ToStringTest() [InlineData(int.MinValue, int.MinValue)] public void MultiplicationTestSizeInt(int dimension, int multiplier) { - Size sz1 = new Size(dimension, dimension); + Size sz1 = new(dimension, dimension); Size mulExpected; unchecked @@ -222,7 +222,7 @@ public void MultiplicationTestSizeInt(int dimension, int multiplier) [InlineData(1000, 2000, 3000)] public void MultiplicationTestSizeIntWidthHeightMultiplier(int width, int height, int multiplier) { - Size sz1 = new Size(width, height); + Size sz1 = new(width, height); Size mulExpected; unchecked @@ -258,7 +258,7 @@ public void MultiplicationTestSizeIntWidthHeightMultiplier(int width, int height [InlineData(int.MinValue, float.MinValue)] public void MultiplicationTestSizeFloat(int dimension, float multiplier) { - Size sz1 = new Size(dimension, dimension); + Size sz1 = new(dimension, dimension); SizeF mulExpected; mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); @@ -271,7 +271,7 @@ public void MultiplicationTestSizeFloat(int dimension, float multiplier) [InlineData(1000, 2000, 30.33f)] public void MultiplicationTestSizeFloatWidthHeightMultiplier(int width, int height, float multiplier) { - Size sz1 = new Size(width, height); + Size sz1 = new(width, height); SizeF mulExpected; mulExpected = new SizeF(width * multiplier, height * multiplier); @@ -283,10 +283,10 @@ public void MultiplicationTestSizeFloatWidthHeightMultiplier(int width, int heig [Fact] public void DivideByZeroChecks() { - Size size = new Size(100, 100); + Size size = new(100, 100); Assert.Throws(() => size / 0); - SizeF expectedSizeF = new SizeF(float.PositiveInfinity, float.PositiveInfinity); + SizeF expectedSizeF = new(float.PositiveInfinity, float.PositiveInfinity); Assert.Equal(expectedSizeF, size / 0.0f); } @@ -305,7 +305,7 @@ public void DivideByZeroChecks() [InlineData(int.MaxValue, -1)] public void DivideTestSizeInt(int dimension, int divisor) { - Size size = new Size(dimension, dimension); + Size size = new(dimension, dimension); Size expected; expected = new Size(dimension / divisor, dimension / divisor); @@ -317,7 +317,7 @@ public void DivideTestSizeInt(int dimension, int divisor) [InlineData(1111, 2222, 3333)] public void DivideTestSizeIntWidthHeightDivisor(int width, int height, int divisor) { - Size size = new Size(width, height); + Size size = new(width, height); Size expected; expected = new Size(width / divisor, height / divisor); @@ -341,7 +341,7 @@ public void DivideTestSizeIntWidthHeightDivisor(int width, int height, int divis [InlineData(int.MinValue, -1.0f)] public void DivideTestSizeFloat(int dimension, float divisor) { - SizeF size = new SizeF(dimension, dimension); + SizeF size = new(dimension, dimension); SizeF expected; expected = new SizeF(dimension / divisor, dimension / divisor); @@ -352,7 +352,7 @@ public void DivideTestSizeFloat(int dimension, float divisor) [InlineData(1111, 2222, -333.33f)] public void DivideTestSizeFloatWidthHeightDivisor(int width, int height, float divisor) { - SizeF size = new SizeF(width, height); + SizeF size = new(width, height); SizeF expected; expected = new SizeF(width / divisor, height / divisor); @@ -366,7 +366,7 @@ public void DivideTestSizeFloatWidthHeightDivisor(int width, int height, float d [InlineData(0, 0)] public void DeconstructTest(int width, int height) { - Size s = new Size(width, height); + Size s = new(width, height); (int deconstructedWidth, int deconstructedHeight) = s; diff --git a/tests/ImageSharp.Tests/Processing/AlphaAssociationProcessorTests.cs b/tests/ImageSharp.Tests/Processing/AlphaAssociationProcessorTests.cs new file mode 100644 index 0000000000..485f641748 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/AlphaAssociationProcessorTests.cs @@ -0,0 +1,441 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Extensions.Convolution; +using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors.Normalization; + +namespace SixLabors.ImageSharp.Tests.Processing; + +[Trait("Category", "Processors")] +public class AlphaAssociationProcessorTests +{ + [Theory] + [InlineData(ProcessorCase.Opaque)] + [InlineData(ProcessorCase.Invert)] + [InlineData(ProcessorCase.ColorMatrix)] + [InlineData(ProcessorCase.Convolution)] + [InlineData(ProcessorCase.ConvolutionPreserveAlpha)] + [InlineData(ProcessorCase.Convolution2D)] + [InlineData(ProcessorCase.Convolution2DPreserveAlpha)] + [InlineData(ProcessorCase.Convolution2Pass)] + [InlineData(ProcessorCase.Convolution2PassPreserveAlpha)] + [InlineData(ProcessorCase.Median)] + [InlineData(ProcessorCase.MedianPreserveAlpha)] + [InlineData(ProcessorCase.BokehBlur)] + [InlineData(ProcessorCase.OilPaint)] + [InlineData(ProcessorCase.HistogramGlobal)] + [InlineData(ProcessorCase.HistogramAdaptiveSlidingWindow)] + [InlineData(ProcessorCase.HistogramAdaptiveTileInterpolation)] + [InlineData(ProcessorCase.HistogramAutoLevel)] + [InlineData(ProcessorCase.HistogramAutoLevelSeparateChannels)] + [InlineData(ProcessorCase.ResizeCompanded)] + [InlineData(ProcessorCase.ResizeUnassociated)] + [InlineData(ProcessorCase.AffineTransform)] + [InlineData(ProcessorCase.ProjectiveTransform)] + [InlineData(ProcessorCase.OrderedDither)] + [InlineData(ProcessorCase.EntropyCrop)] + public void EquivalentAlphaRepresentationsProduceEquivalentResults(ProcessorCase processor) + { + using Image unassociated = processor == ProcessorCase.EntropyCrop ? CreateEntropyCropImage() : CreateTestImage(); + using Image associated = CreateAssociatedImage(unassociated); + + ApplyProcessor(unassociated, processor); + ApplyProcessor(associated, processor); + + AssertEquivalent(unassociated, associated); + } + + /// + /// Verifies the native-range bulk hooks required by the public associated-alpha operation contract. + /// + [Fact] + public void ScaledAssociatedPixelNativeBulkConversionsMatchScalarConversions() + { + const int length = 259; + ScaledRgbaVectorP pixel = ScaledRgbaVectorP.FromUnassociatedScaledVector4(new Vector4(.8F, .4F, .2F, .5F)); + ScaledRgbaVectorP[] pixels = new ScaledRgbaVectorP[length]; + Vector4[] expectedVectors = new Vector4[length]; + Vector4[] actualVectors = new Vector4[length]; + ScaledRgbaVectorP[] expectedPixels = new ScaledRgbaVectorP[length]; + ScaledRgbaVectorP[] actualPixels = new ScaledRgbaVectorP[length]; + PixelOperations operations = PixelOperations.Instance; + + Array.Fill(pixels, pixel); + + for (int i = 0; i < length; i++) + { + expectedVectors[i] = pixels[i].ToUnassociatedVector4(); + } + + operations.ToVector4(Configuration.Default, pixels, actualVectors, PixelConversionModifiers.UnPremultiply); + Assert.Equal(expectedVectors, actualVectors); + + for (int i = 0; i < length; i++) + { + expectedVectors[i] = pixels[i].ToAssociatedVector4(); + } + + operations.ToVector4(Configuration.Default, pixels, actualVectors, PixelConversionModifiers.Premultiply); + Assert.Equal(expectedVectors, actualVectors); + + for (int i = 0; i < length; i++) + { + expectedVectors[i] = pixels[i].ToUnassociatedVector4(); + expectedPixels[i] = ScaledRgbaVectorP.FromUnassociatedVector4(expectedVectors[i]); + } + + operations.FromVector4Destructive(Configuration.Default, expectedVectors, actualPixels, PixelConversionModifiers.UnPremultiply); + Assert.Equal(expectedPixels, actualPixels); + + for (int i = 0; i < length; i++) + { + expectedVectors[i] = pixels[i].ToAssociatedVector4(); + expectedPixels[i] = ScaledRgbaVectorP.FromAssociatedVector4(expectedVectors[i]); + } + + operations.FromVector4Destructive(Configuration.Default, expectedVectors, actualPixels, PixelConversionModifiers.Premultiply); + Assert.Equal(expectedPixels, actualPixels); + } + + [Fact] + public void ErrorDiffusionUsesLogicalUnassociatedError() + { + Vector4 background = new(.25F, .25F, .25F, .5F); + using Image unassociated = new(4, 4, RgbaVector.FromScaledVector4(background)); + using Image associated = CreateAssociatedImage(unassociated); + + Vector4 source = new(.5F, .5F, .5F, .5F); + Vector4 transformed = new(.25F, .25F, .25F, .5F); + RgbaVector unassociatedSource = RgbaVector.FromScaledVector4(source); + RgbaVector unassociatedTransformed = RgbaVector.FromScaledVector4(transformed); + ScaledRgbaVectorP associatedSource = ScaledRgbaVectorP.FromUnassociatedScaledVector4(source); + ScaledRgbaVectorP associatedTransformed = ScaledRgbaVectorP.FromUnassociatedScaledVector4(transformed); + + ErrorDither.FloydSteinberg.Dither(unassociated.Frames.RootFrame, unassociated.Bounds, unassociatedSource, unassociatedTransformed, 1, 1, 1F); + ErrorDither.FloydSteinberg.Dither(associated.Frames.RootFrame, associated.Bounds, associatedSource, associatedTransformed, 1, 1, 1F); + + AssertEquivalent(unassociated, associated); + } + + [Fact] + public void AffineTransformFractionalEdgesMatchNormalPixelConversion() + { + using Image rgba = new(4, 4, new Rgba32(255, 64, 16, 255)); + using Image bgra = rgba.CloneAs(); + using Image rgb = rgba.CloneAs(); + + ApplyProcessor(rgba, ProcessorCase.AffineTransform); + ApplyProcessor(bgra, ProcessorCase.AffineTransform); + ApplyProcessor(rgb, ProcessorCase.AffineTransform); + + // Alpha-less output drops fractional coverage after unassociation, just like a normal pixel-format conversion. + using Image expectedBgra = rgba.CloneAs(); + using Image expectedRgb = rgba.CloneAs(); + + Assert.Equal(rgba.Size, bgra.Size); + Assert.Equal(rgba.Size, rgb.Size); + + bool foundFractionalCoverage = false; + for (int y = 0; y < rgba.Height; y++) + { + for (int x = 0; x < rgba.Width; x++) + { + Rgba32 pixel = rgba[x, y]; + + if (pixel.A is > 0 and < 255) + { + foundFractionalCoverage = true; + Assert.Equal(new Rgba32(255, 64, 16, pixel.A), pixel); + } + + Assert.Equal(expectedBgra[x, y], bgra[x, y]); + Assert.Equal(expectedRgb[x, y], rgb[x, y]); + } + } + + Assert.True(foundFractionalCoverage); + } + + [Fact] + public void ConvolutionDoesNotObserveColorBehindZeroAlpha() + { + using Image hiddenColor = new(3, 1); + using Image transparentBlack = new(3, 1); + + hiddenColor[0, 0] = new RgbaVector(1, 0, 0, 0); + hiddenColor[1, 0] = new RgbaVector(0, 0, 1, 1); + hiddenColor[2, 0] = new RgbaVector(1, 0, 0, 0); + transparentBlack[1, 0] = hiddenColor[1, 0]; + + ApplyProcessor(hiddenColor, ProcessorCase.Convolution); + ApplyProcessor(transparentBlack, ProcessorCase.Convolution); + + // Associated filtering makes fully transparent RGB unobservable before the kernel is evaluated. + for (int x = 0; x < hiddenColor.Width; x++) + { + Assert.Equal(transparentBlack[x, 0], hiddenColor[x, 0]); + } + } + + private static Image CreateTestImage() + { + Image image = new(8, 8); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + float alpha = ((x + (2 * y)) % 3) switch + { + 0 => .25F, + 1 => .5F, + _ => 1F + }; + + // Dyadic components and alpha make the initial association round trip exact in binary floating point. + float red = ((x + y) & 7) / 8F; + float green = (((3 * x) + y + 1) & 7) / 8F; + float blue = ((x + (5 * y) + 2) & 7) / 8F; + image[x, y] = new RgbaVector(red, green, blue, alpha); + } + } + + return image; + } + + private static Image CreateEntropyCropImage() + { + Image image = new(8, 8, new RgbaVector(0, 0, 0, 1)); + + for (int y = 2; y < 6; y++) + { + for (int x = 2; x < 6; x++) + { + image[x, y] = new RgbaVector(1, 1, 1, 1); + } + } + + return image; + } + + private static Image CreateAssociatedImage(Image source) + { + Image image = new(source.Width, source.Height); + + for (int y = 0; y < source.Height; y++) + { + for (int x = 0; x < source.Width; x++) + { + Vector4 vector = source[x, y].ToScaledVector4(); + Numerics.Premultiply(ref vector); + image[x, y] = ScaledRgbaVectorP.FromScaledVector4(vector); + } + } + + return image; + } + + private static void ApplyProcessor(Image image, ProcessorCase processor) + where TPixel : unmanaged, IPixel + { + if (processor == ProcessorCase.Opaque) + { + using OpaqueProcessor opaque = new(image.Configuration, image, image.Bounds); + opaque.Apply(image.Frames.RootFrame); + return; + } + + if (processor is ProcessorCase.Convolution2D or ProcessorCase.Convolution2DPreserveAlpha) + { + DenseMatrix kernelX = new float[,] + { + { -.25F, 0, .25F }, + { -.5F, 0, .5F }, + { -.25F, 0, .25F } + }; + DenseMatrix kernelY = new float[,] + { + { -.25F, -.5F, -.25F }, + { 0, 0, 0 }, + { .25F, .5F, .25F } + }; + + using Convolution2DProcessor convolution = new( + image.Configuration, + kernelX, + kernelY, + processor == ProcessorCase.Convolution2DPreserveAlpha, + image, + image.Bounds); + + convolution.Apply(image.Frames.RootFrame); + return; + } + + if (processor is ProcessorCase.Convolution2Pass or ProcessorCase.Convolution2PassPreserveAlpha) + { + float[] kernel = [.25F, .5F, .25F]; + using Convolution2PassProcessor convolution = new( + image.Configuration, + kernel, + processor == ProcessorCase.Convolution2PassPreserveAlpha, + image, + image.Bounds, + BorderWrappingMode.Repeat, + BorderWrappingMode.Repeat); + + convolution.Apply(image.Frames.RootFrame); + return; + } + + image.Mutate(context => + { + switch (processor) + { + case ProcessorCase.Invert: + context.Invert(); + break; + case ProcessorCase.ColorMatrix: + ColorMatrix matrix = new() + { + M11 = .5F, + M22 = .5F, + M33 = .5F, + M41 = .25F, + M42 = .25F, + M43 = .25F, + M14 = .125F, + M24 = .125F, + M34 = .125F, + M44 = .5F + }; + + context.Filter(matrix); + break; + case ProcessorCase.Convolution: + context.Convolve(new float[,] { { .25F, .5F, .25F } }); + break; + case ProcessorCase.ConvolutionPreserveAlpha: + context.Convolve(new float[,] { { .25F, .5F, .25F } }, true); + break; + case ProcessorCase.Median: + context.MedianBlur(1, false); + break; + case ProcessorCase.MedianPreserveAlpha: + context.MedianBlur(1, true); + break; + case ProcessorCase.BokehBlur: + context.BokehBlur(2, 2, 2F); + break; + case ProcessorCase.OilPaint: + context.OilPaint(4, 3); + break; + case ProcessorCase.HistogramGlobal: + context.HistogramEqualization(CreateHistogramOptions(HistogramEqualizationMethod.Global)); + break; + case ProcessorCase.HistogramAdaptiveSlidingWindow: + context.HistogramEqualization(CreateHistogramOptions(HistogramEqualizationMethod.AdaptiveSlidingWindow)); + break; + case ProcessorCase.HistogramAdaptiveTileInterpolation: + context.HistogramEqualization(CreateHistogramOptions(HistogramEqualizationMethod.AdaptiveTileInterpolation)); + break; + case ProcessorCase.HistogramAutoLevel: + context.HistogramEqualization(CreateHistogramOptions(HistogramEqualizationMethod.AutoLevel)); + break; + case ProcessorCase.HistogramAutoLevelSeparateChannels: + HistogramEqualizationOptions options = CreateHistogramOptions(HistogramEqualizationMethod.AutoLevel); + options.SyncChannels = false; + context.HistogramEqualization(options); + break; + case ProcessorCase.ResizeCompanded: + context.Resize(new ResizeOptions + { + Size = new Size(5, 5), + Sampler = KnownResamplers.Box, + Compand = true, + PremultiplyAlpha = true + }); + break; + case ProcessorCase.ResizeUnassociated: + context.Resize(new ResizeOptions + { + Size = new Size(5, 5), + Sampler = KnownResamplers.Box, + PremultiplyAlpha = false + }); + break; + case ProcessorCase.AffineTransform: + context.Transform(image.Bounds, Matrix3x2.CreateTranslation(.5F, .5F), image.Size, KnownResamplers.Bicubic); + break; + case ProcessorCase.ProjectiveTransform: + context.Transform(image.Bounds, Matrix4x4.CreateTranslation(.5F, .5F, 0), image.Size, KnownResamplers.Bicubic); + break; + case ProcessorCase.OrderedDither: + context.Dither(KnownDitherings.Bayer4x4); + break; + case ProcessorCase.EntropyCrop: + context.EntropyCrop(.5F); + break; + } + }); + } + + private static HistogramEqualizationOptions CreateHistogramOptions(HistogramEqualizationMethod method) + => new() + { + Method = method, + LuminanceLevels = 16, + NumberOfTiles = 2 + }; + + private static void AssertEquivalent(Image unassociated, Image associated) + { + // Materialize both representations through the same 16-bit logical-color boundary. Their floating-point storage paths can differ by one rounding operation when associated results are unassociated for RgbaVector storage. + using Image expected = unassociated.CloneAs(); + using Image actual = associated.CloneAs(); + + Assert.Equal(expected.Size, actual.Size); + + for (int y = 0; y < expected.Height; y++) + { + for (int x = 0; x < expected.Width; x++) + { + Assert.Equal(expected[x, y], actual[x, y]); + } + } + } + + public enum ProcessorCase + { + Opaque, + Invert, + ColorMatrix, + Convolution, + ConvolutionPreserveAlpha, + Convolution2D, + Convolution2DPreserveAlpha, + Convolution2Pass, + Convolution2PassPreserveAlpha, + Median, + MedianPreserveAlpha, + BokehBlur, + OilPaint, + HistogramGlobal, + HistogramAdaptiveSlidingWindow, + HistogramAdaptiveTileInterpolation, + HistogramAutoLevel, + HistogramAutoLevelSeparateChannels, + ResizeCompanded, + ResizeUnassociated, + AffineTransform, + ProjectiveTransform, + OrderedDither, + EntropyCrop + } +} diff --git a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index 403865e662..5e5887c923 100644 --- a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs index c7eb959535..32b8bf276a 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs @@ -10,14 +10,14 @@ public class OrderedDitherFactoryTests { #pragma warning disable SA1025 // Code should not contain multiple whitespace in a row - private static readonly DenseMatrix Expected2x2Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected2x2Matrix = new( new uint[2, 2] { { 0, 2 }, { 3, 1 } }); - private static readonly DenseMatrix Expected3x3Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected3x3Matrix = new( new uint[3, 3] { { 0, 5, 2 }, @@ -25,7 +25,7 @@ public class OrderedDitherFactoryTests { 3, 6, 1 } }); - private static readonly DenseMatrix Expected4x4Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected4x4Matrix = new( new uint[4, 4] { { 0, 8, 2, 10 }, @@ -34,7 +34,7 @@ public class OrderedDitherFactoryTests { 15, 7, 13, 5 } }); - private static readonly DenseMatrix Expected8x8Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected8x8Matrix = new( new uint[8, 8] { { 0, 32, 8, 40, 2, 34, 10, 42 }, diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index da2bc465f6..2337e4567d 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -13,7 +13,7 @@ public class BoxBlurTest : BaseImageOperationsExtensionTest public void BoxBlur_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(); - var processor = this.Verify(); + BoxBlurProcessor processor = this.Verify(); Assert.Equal(7, processor.Radius); } @@ -22,7 +22,7 @@ public void BoxBlur_BoxBlurProcessorDefaultsSet() public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(34); - var processor = this.Verify(); + BoxBlurProcessor processor = this.Verify(); Assert.Equal(34, processor.Radius); } @@ -31,7 +31,7 @@ public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(5, this.rect); - var processor = this.Verify(this.rect); + BoxBlurProcessor processor = this.Verify(this.rect); Assert.Equal(5, processor.Radius); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 2a1273ebb7..713daa0e9f 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -59,7 +59,7 @@ public void DetectEdges_EdgeDetector2DProcessor_DefaultGrayScale_Set(EdgeDetecto [MemberData(nameof(EdgeDetector2DKernelData))] public void DetectEdges_Rect_EdgeDetector2DProcessor_DefaultGrayScale_Set(EdgeDetector2DKernel kernel, bool _) { - this.operations.DetectEdges(kernel, this.rect); + this.operations.DetectEdges(this.rect, kernel); EdgeDetector2DProcessor processor = this.Verify(this.rect); Assert.True(processor.Grayscale); @@ -81,7 +81,7 @@ public void DetectEdges_EdgeDetector2DProcessorSet(EdgeDetector2DKernel kernel, [MemberData(nameof(EdgeDetector2DKernelData))] public void DetectEdges_Rect_EdgeDetector2DProcessorSet(EdgeDetector2DKernel kernel, bool grayscale) { - this.operations.DetectEdges(kernel, grayscale, this.rect); + this.operations.DetectEdges(this.rect, kernel, grayscale); EdgeDetector2DProcessor processor = this.Verify(this.rect); Assert.Equal(grayscale, processor.Grayscale); @@ -114,7 +114,7 @@ public void DetectEdges_EdgeDetectorProcessor_DefaultGrayScale_Set(EdgeDetectorK [MemberData(nameof(EdgeDetectorKernelData))] public void DetectEdges_Rect_EdgeDetectorProcessor_DefaultGrayScale_Set(EdgeDetectorKernel kernel, bool _) { - this.operations.DetectEdges(kernel, this.rect); + this.operations.DetectEdges(this.rect, kernel); EdgeDetectorProcessor processor = this.Verify(this.rect); Assert.True(processor.Grayscale); @@ -136,7 +136,7 @@ public void DetectEdges_EdgeDetectorProcessorSet(EdgeDetectorKernel kernel, bool [MemberData(nameof(EdgeDetectorKernelData))] public void DetectEdges_Rect_EdgeDetectorProcessorSet(EdgeDetectorKernel kernel, bool grayscale) { - this.operations.DetectEdges(kernel, grayscale, this.rect); + this.operations.DetectEdges(this.rect, kernel, grayscale); EdgeDetectorProcessor processor = this.Verify(this.rect); Assert.Equal(grayscale, processor.Grayscale); @@ -167,7 +167,7 @@ public void DetectEdges_EdgeDetectorCompassProcessor_DefaultGrayScale_Set(EdgeDe [MemberData(nameof(EdgeDetectorCompassKernelData))] public void DetectEdges_Rect_EdgeDetectorCompassProcessor_DefaultGrayScale_Set(EdgeDetectorCompassKernel kernel, bool _) { - this.operations.DetectEdges(kernel, this.rect); + this.operations.DetectEdges(this.rect, kernel); EdgeDetectorCompassProcessor processor = this.Verify(this.rect); Assert.True(processor.Grayscale); @@ -189,7 +189,7 @@ public void DetectEdges_EdgeDetectorCompassProcessorSet(EdgeDetectorCompassKerne [MemberData(nameof(EdgeDetectorCompassKernelData))] public void DetectEdges_Rect_EdgeDetectorCompassProcessorSet(EdgeDetectorCompassKernel kernel, bool grayscale) { - this.operations.DetectEdges(kernel, grayscale, this.rect); + this.operations.DetectEdges(this.rect, kernel, grayscale); EdgeDetectorCompassProcessor processor = this.Verify(this.rect); Assert.Equal(grayscale, processor.Grayscale); diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index 5142791603..410862ebfa 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -13,7 +13,7 @@ public class GaussianBlurTest : BaseImageOperationsExtensionTest public void GaussianBlur_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(); - var processor = this.Verify(); + GaussianBlurProcessor processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -22,7 +22,7 @@ public void GaussianBlur_GaussianBlurProcessorDefaultsSet() public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(0.2f); - var processor = this.Verify(); + GaussianBlurProcessor processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -30,8 +30,8 @@ public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() [Fact] public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet() { - this.operations.GaussianBlur(0.6f, this.rect); - var processor = this.Verify(this.rect); + this.operations.GaussianBlur(this.rect, 0.6f); + GaussianBlurProcessor processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index b48ebac0dc..fd9e64c467 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -13,7 +13,7 @@ public class GaussianSharpenTest : BaseImageOperationsExtensionTest public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(); - var processor = this.Verify(); + GaussianSharpenProcessor processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -22,7 +22,7 @@ public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.2f); - var processor = this.Verify(); + GaussianSharpenProcessor processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -30,8 +30,8 @@ public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() [Fact] public void GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() { - this.operations.GaussianSharpen(0.6f, this.rect); - var processor = this.Verify(this.rect); + this.operations.GaussianSharpen(this.rect, 0.6f); + GaussianSharpenProcessor processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs index 476e5b0a90..eb1d3031a4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs @@ -11,87 +11,87 @@ public class KernelSamplingMapTest [Fact] public void KernalSamplingMap_Kernel5Image7x7RepeatBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Repeat; + Size kernelSize = new(5, 5); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Repeat; int[] expected = - { + [ 0, 0, 0, 1, 2, 0, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 6, - 4, 5, 6, 6, 6, - }; + 4, 5, 6, 6, 6 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7BounceBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Bounce; + Size kernelSize = new(5, 5); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Bounce; int[] expected = - { + [ 2, 1, 0, 1, 2, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 5, - 4, 5, 6, 5, 4, - }; + 4, 5, 6, 5, 4 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7MirrorBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Mirror; + Size kernelSize = new(5, 5); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Mirror; int[] expected = - { + [ 1, 0, 0, 1, 2, 0, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 6, - 4, 5, 6, 6, 5, - }; + 4, 5, 6, 6, 5 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7WrapBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(5, 5); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] expected = - { + [ 5, 6, 0, 1, 2, 6, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 0, - 4, 5, 6, 0, 1, - }; + 4, 5, 6, 0, 1 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image9x9BounceBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(1, 1, 9, 9); - var mode = BorderWrappingMode.Bounce; + Size kernelSize = new(5, 5); + Rectangle bounds = new(1, 1, 9, 9); + BorderWrappingMode mode = BorderWrappingMode.Bounce; int[] expected = - { + [ 3, 2, 1, 2, 3, 2, 1, 2, 3, 4, 1, 2, 3, 4, 5, @@ -100,19 +100,19 @@ public void KernalSamplingMap_Kernel5Image9x9BounceBorder() 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 6, 7, 8, 9, 8, - 7, 8, 9, 8, 7, - }; + 7, 8, 9, 8, 7 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image9x9MirrorBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(1, 1, 9, 9); - var mode = BorderWrappingMode.Mirror; + Size kernelSize = new(5, 5); + Rectangle bounds = new(1, 1, 9, 9); + BorderWrappingMode mode = BorderWrappingMode.Mirror; int[] expected = - { + [ 2, 1, 1, 2, 3, 1, 1, 2, 3, 4, 1, 2, 3, 4, 5, @@ -121,19 +121,19 @@ public void KernalSamplingMap_Kernel5Image9x9MirrorBorder() 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 6, 7, 8, 9, 9, - 7, 8, 9, 9, 8, - }; + 7, 8, 9, 9, 8 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image9x9WrapBorder() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(1, 1, 9, 9); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(5, 5); + Rectangle bounds = new(1, 1, 9, 9); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] expected = - { + [ 8, 9, 1, 2, 3, 9, 1, 2, 3, 4, 1, 2, 3, 4, 5, @@ -142,278 +142,278 @@ public void KernalSamplingMap_Kernel5Image9x9WrapBorder() 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 6, 7, 8, 9, 1, - 7, 8, 9, 1, 2, - }; + 7, 8, 9, 1, 2 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7RepeatBorderTile() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Repeat; + Size kernelSize = new(5, 5); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Repeat; int[] expected = - { + [ 2, 2, 2, 3, 4, 2, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 5, 6, 7, 8, 8, - 6, 7, 8, 8, 8, - }; + 6, 7, 8, 8, 8 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7BounceBorderTile() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Bounce; + Size kernelSize = new(5, 5); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Bounce; int[] expected = - { + [ 4, 3, 2, 3, 4, 3, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 5, 6, 7, 8, 7, - 6, 7, 8, 7, 6, - }; + 6, 7, 8, 7, 6 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7MirrorBorderTile() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Mirror; + Size kernelSize = new(5, 5); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Mirror; int[] expected = - { + [ 3, 2, 2, 3, 4, 2, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 5, 6, 7, 8, 8, - 6, 7, 8, 8, 7, - }; + 6, 7, 8, 8, 7 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel5Image7x7WrapBorderTile() { - var kernelSize = new Size(5, 5); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(5, 5); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] expected = - { + [ 7, 8, 2, 3, 4, 8, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 5, 6, 7, 8, 2, - 6, 7, 8, 2, 3, - }; + 6, 7, 8, 2, 3 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7RepeatBorder() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Repeat; + Size kernelSize = new(3, 3); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Repeat; int[] expected = - { + [ 0, 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, - 5, 6, 6, - }; + 5, 6, 6 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7BounceBorder() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Bounce; + Size kernelSize = new(3, 3); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Bounce; int[] expected = - { + [ 1, 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, - 5, 6, 5, - }; + 5, 6, 5 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7MirrorBorder() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Mirror; + Size kernelSize = new(3, 3); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Mirror; int[] expected = - { + [ 0, 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, - 5, 6, 6, - }; + 5, 6, 6 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7WrapBorder() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(0, 0, 7, 7); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(3, 3); + Rectangle bounds = new(0, 0, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] expected = - { + [ 6, 0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, - 5, 6, 0, - }; + 5, 6, 0 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7RepeatBorderTile() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Repeat; + Size kernelSize = new(3, 3); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Repeat; int[] expected = - { + [ 2, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, - 7, 8, 8, - }; + 7, 8, 8 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7BounceBorderTile() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Bounce; + Size kernelSize = new(3, 3); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Bounce; int[] expected = - { + [ 3, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, - 7, 8, 7, - }; + 7, 8, 7 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7MirrorBorderTile() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Mirror; + Size kernelSize = new(3, 3); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Mirror; int[] expected = - { + [ 2, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, - 7, 8, 8, - }; + 7, 8, 8 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x7WrapBorderTile() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(2, 2, 7, 7); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(3, 3); + Rectangle bounds = new(2, 2, 7, 7); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] expected = - { + [ 8, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, - 7, 8, 2, - }; + 7, 8, 2 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected); } [Fact] public void KernalSamplingMap_Kernel3Image7x5WrapBorderTile() { - var kernelSize = new Size(3, 3); - var bounds = new Rectangle(2, 2, 7, 5); - var mode = BorderWrappingMode.Wrap; + Size kernelSize = new(3, 3); + Rectangle bounds = new(2, 2, 7, 5); + BorderWrappingMode mode = BorderWrappingMode.Wrap; int[] xExpected = - { + [ 8, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 6, 7, 8, - 7, 8, 2, - }; + 7, 8, 2 + ]; int[] yExpected = - { + [ 6, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, - 5, 6, 2, - }; + 5, 6, 2 + ]; this.AssertOffsets(kernelSize, bounds, mode, mode, xExpected, yExpected); } private void AssertOffsets(Size kernelSize, Rectangle bounds, BorderWrappingMode xBorderMode, BorderWrappingMode yBorderMode, int[] xExpected, int[] yExpected) { // Arrange - var map = new KernelSamplingMap(Configuration.Default.MemoryAllocator); + KernelSamplingMap map = new(Configuration.Default.MemoryAllocator); // Act map.BuildSamplingOffsetMap(kernelSize.Height, kernelSize.Width, bounds, xBorderMode, yBorderMode); // Assert - var xOffsets = map.GetColumnOffsetSpan().ToArray(); + int[] xOffsets = map.GetColumnOffsetSpan().ToArray(); Assert.Equal(xExpected, xOffsets); - var yOffsets = map.GetRowOffsetSpan().ToArray(); + int[] yOffsets = map.GetRowOffsetSpan().ToArray(); Assert.Equal(yExpected, yOffsets); } } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/MedianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/MedianBlurTest.cs index dc497628fb..77fbc70823 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/MedianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/MedianBlurTest.cs @@ -13,7 +13,7 @@ public class MedianBlurTest : BaseImageOperationsExtensionTest public void Median_radius_MedianProcessorDefaultsSet() { this.operations.MedianBlur(3, true); - var processor = this.Verify(); + MedianBlurProcessor processor = this.Verify(); Assert.Equal(3, processor.Radius); Assert.True(processor.PreserveAlpha); @@ -22,8 +22,8 @@ public void Median_radius_MedianProcessorDefaultsSet() [Fact] public void Median_radius_rect_MedianProcessorDefaultsSet() { - this.operations.MedianBlur(5, false, this.rect); - var processor = this.Verify(this.rect); + this.operations.MedianBlur(this.rect, 5, false); + MedianBlurProcessor processor = this.Verify(this.rect); Assert.Equal(5, processor.Radius); Assert.False(processor.PreserveAlpha); diff --git a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs index 800cb06ac1..6c6fa6b2be 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs @@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution.Processors; public class LaplacianKernelFactoryTests { - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateFloatComparer ApproximateComparer = new(0.0001F); - private static readonly DenseMatrix Expected3x3Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected3x3Matrix = new( new float[,] { { -1, -1, -1 }, @@ -17,7 +17,7 @@ public class LaplacianKernelFactoryTests { -1, -1, -1 } }); - private static readonly DenseMatrix Expected5x5Matrix = new DenseMatrix( + private static readonly DenseMatrix Expected5x5Matrix = new( new float[,] { { -1, -1, -1, -1, -1 }, diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index 1b3d1bd9ac..43785da6e7 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -20,11 +20,11 @@ public static void Equal(ReadOnlySpan a, ReadOnlySpan b) private readonly IDither orderedDither; private readonly IDither errorDiffuser; private readonly Color[] testPalette = - { + [ Color.Red, Color.Green, Color.Blue - }; + ]; public DitherTest() { diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 6d5973dbbb..b24ab6c817 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -13,7 +13,7 @@ public class OilPaintTest : BaseImageOperationsExtensionTest public void OilPaint_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(); - var processor = this.Verify(); + OilPaintingProcessor processor = this.Verify(); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -23,7 +23,7 @@ public void OilPaint_OilPaintingProcessorDefaultsSet() public void OilPaint_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(this.rect); - var processor = this.Verify(this.rect); + OilPaintingProcessor processor = this.Verify(this.rect); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -33,7 +33,7 @@ public void OilPaint_rect_OilPaintingProcessorDefaultsSet() public void OilPaint_Levels_Brush_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(34, 65); - var processor = this.Verify(); + OilPaintingProcessor processor = this.Verify(); Assert.Equal(34, processor.Levels); Assert.Equal(65, processor.BrushSize); @@ -43,7 +43,7 @@ public void OilPaint_Levels_Brush_OilPaintingProcessorDefaultsSet() public void OilPaint_Levels_Brush_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(54, 43, this.rect); - var processor = this.Verify(this.rect); + OilPaintingProcessor processor = this.Verify(this.rect); Assert.Equal(54, processor.Levels); Assert.Equal(43, processor.BrushSize); diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index 6531f7b442..f557183d6e 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -13,7 +13,7 @@ public class PixelateTest : BaseImageOperationsExtensionTest public void Pixelate_PixelateProcessorDefaultsSet() { this.operations.Pixelate(); - var processor = this.Verify(); + PixelateProcessor processor = this.Verify(); Assert.Equal(4, processor.Size); } @@ -22,7 +22,7 @@ public void Pixelate_PixelateProcessorDefaultsSet() public void Pixelate_Size_PixelateProcessorDefaultsSet() { this.operations.Pixelate(12); - var processor = this.Verify(); + PixelateProcessor processor = this.Verify(); Assert.Equal(12, processor.Size); } @@ -31,7 +31,7 @@ public void Pixelate_Size_PixelateProcessorDefaultsSet() public void Pixelate_Size_rect_PixelateProcessorDefaultsSet() { this.operations.Pixelate(23, this.rect); - var processor = this.Verify(this.rect); + PixelateProcessor processor = this.Verify(this.rect); Assert.Equal(23, processor.Size); } diff --git a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs index 17f63384e0..f55500f99f 100644 --- a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Tests.Processing; internal class FakeImageOperationsProvider : IImageProcessingContextFactory { - private readonly List imageOperators = new List(); + private readonly List imageOperators = []; public bool HasCreated(Image source) where TPixel : unmanaged, IPixel @@ -35,7 +35,7 @@ public IEnumerable> Created(Image so public IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) where TPixel : unmanaged, IPixel { - var op = new FakeImageOperations(configuration, source, mutate); + FakeImageOperations op = new(configuration, source, mutate); this.imageOperators.Add(op); return op; } @@ -51,7 +51,7 @@ public FakeImageOperations(Configuration configuration, Image source, bo public Image Source { get; } - public List Applied { get; } = new List(); + public List Applied { get; } = []; public Configuration Configuration { get; } diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs index 87436e4a35..d5f7f69d99 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs @@ -31,7 +31,7 @@ public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() [Fact] public void Brightness_scaled_vector() { - var rgbImage = new Image(Configuration.Default, 100, 100, new Rgb24(0, 0, 0)); + Image rgbImage = new(Configuration.Default, 100, 100, new Rgb24(0, 0, 0)); rgbImage.Mutate(x => x.ApplyProcessor(new BrightnessProcessor(2))); @@ -43,13 +43,14 @@ public void Brightness_scaled_vector() Assert.Equal(new Rgb24(20, 20, 20), rgbImage[0, 0]); - var halfSingleImage = new Image(Configuration.Default, 100, 100, new HalfSingle(-1)); + // HalfSingle normalizes the complete finite binary16 interval, making -65504 logical zero and -32752 logical .25. + Image halfSingleImage = new(Configuration.Default, 100, 100, new HalfSingle((float)Half.MinValue)); halfSingleImage.Mutate(x => x.ApplyProcessor(new BrightnessProcessor(2))); - Assert.Equal(new HalfSingle(-1), halfSingleImage[0, 0]); + Assert.Equal(new HalfSingle((float)Half.MinValue), halfSingleImage[0, 0]); - halfSingleImage = new Image(Configuration.Default, 100, 100, new HalfSingle(-0.5f)); + halfSingleImage = new Image(Configuration.Default, 100, 100, new HalfSingle(-32752F)); halfSingleImage.Mutate(x => x.ApplyProcessor(new BrightnessProcessor(2))); diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs index 7fb93bba54..f00087e1c0 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs @@ -11,17 +11,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters; [Trait("Category", "Processors")] public class ColorBlindnessTest : BaseImageOperationsExtensionTest { - public static IEnumerable TheoryData = new[] - { - new object[] { new TestType(), ColorBlindnessMode.Achromatomaly }, - new object[] { new TestType(), ColorBlindnessMode.Achromatopsia }, - new object[] { new TestType(), ColorBlindnessMode.Deuteranomaly }, - new object[] { new TestType(), ColorBlindnessMode.Deuteranopia }, - new object[] { new TestType(), ColorBlindnessMode.Protanomaly }, - new object[] { new TestType(), ColorBlindnessMode.Protanopia }, - new object[] { new TestType(), ColorBlindnessMode.Tritanomaly }, - new object[] { new TestType(), ColorBlindnessMode.Tritanopia } - }; + public static IEnumerable TheoryData = + [ + [new TestType(), ColorBlindnessMode.Achromatomaly], + [new TestType(), ColorBlindnessMode.Achromatopsia], + [new TestType(), ColorBlindnessMode.Deuteranomaly], + [new TestType(), ColorBlindnessMode.Deuteranopia], + [new TestType(), ColorBlindnessMode.Protanomaly], + [new TestType(), ColorBlindnessMode.Protanopia], + [new TestType(), ColorBlindnessMode.Tritanomaly], + [new TestType(), ColorBlindnessMode.Tritanopia] + ]; [Theory] [MemberData(nameof(TheoryData))] diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs index 36edc10e5d..6f0f06535f 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters; [Trait("Category", "Processors")] public class GrayscaleTest : BaseImageOperationsExtensionTest { - public static IEnumerable ModeTheoryData = new[] - { - new object[] { new TestType(), GrayscaleMode.Bt709 } - }; + public static IEnumerable ModeTheoryData = + [ + [new TestType(), GrayscaleMode.Bt709] + ]; [Theory] [MemberData(nameof(ModeTheoryData))] diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs index 0c197a96f7..33deb715ba 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs @@ -13,7 +13,7 @@ public class HueTest : BaseImageOperationsExtensionTest public void Hue_amount_HueProcessorDefaultsSet() { this.operations.Hue(34f); - var processor = this.Verify(); + HueProcessor processor = this.Verify(); Assert.Equal(34f, processor.Degrees); } @@ -22,7 +22,7 @@ public void Hue_amount_HueProcessorDefaultsSet() public void Hue_amount_rect_HueProcessorDefaultsSet() { this.operations.Hue(5f, this.rect); - var processor = this.Verify(this.rect); + HueProcessor processor = this.Verify(this.rect); Assert.Equal(5f, processor.Degrees); } diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index e84bd243e4..f68ecef08f 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -13,7 +13,7 @@ public class LomographTest : BaseImageOperationsExtensionTest public void Lomograph_amount_LomographProcessorDefaultsSet() { this.operations.Lomograph(); - var processor = this.Verify(); + LomographProcessor processor = this.Verify(); Assert.Equal(processor.GraphicsOptions, this.options); } @@ -21,7 +21,7 @@ public void Lomograph_amount_LomographProcessorDefaultsSet() public void Lomograph_amount_rect_LomographProcessorDefaultsSet() { this.operations.Lomograph(this.rect); - var processor = this.Verify(this.rect); + LomographProcessor processor = this.Verify(this.rect); Assert.Equal(processor.GraphicsOptions, this.options); } } diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs index 7d3e0aed0f..89d8d47bcf 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs @@ -13,7 +13,7 @@ public class PolaroidTest : BaseImageOperationsExtensionTest public void Polaroid_amount_PolaroidProcessorDefaultsSet() { this.operations.Polaroid(); - var processor = this.Verify(); + PolaroidProcessor processor = this.Verify(); Assert.Equal(processor.GraphicsOptions, this.options); } @@ -21,7 +21,7 @@ public void Polaroid_amount_PolaroidProcessorDefaultsSet() public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() { this.operations.Polaroid(this.rect); - var processor = this.Verify(this.rect); + PolaroidProcessor processor = this.Verify(this.rect); Assert.Equal(processor.GraphicsOptions, this.options); } } diff --git a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs index 744e26d8f7..09fbcdc70c 100644 --- a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs @@ -24,7 +24,7 @@ public ImageOperationTests() { this.provider = new FakeImageOperationsProvider(); - var processorMock = new Mock(); + Mock processorMock = new(); this.processorDefinition = processorMock.Object; this.image = new Image( @@ -103,7 +103,7 @@ public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOriginal() [Fact] public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation() { - var operations = new FakeImageOperationsProvider.FakeImageOperations(Configuration.Default, null, false); + FakeImageOperationsProvider.FakeImageOperations operations = new(Configuration.Default, null, false); operations.ApplyProcessors(this.processorDefinition); Assert.Contains(this.processorDefinition, operations.Applied.Select(x => x.NonGenericProcessor)); } @@ -152,7 +152,7 @@ private static string GetExpectedExceptionText() { try { - var img = new Image(1, 1); + Image img = new(1, 1); img.Dispose(); img.EnsureNotDisposed(); } diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index 88707c60ff..e39d8075ba 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -23,7 +23,7 @@ public class ImageProcessingContextTests : IDisposable private readonly Mock> cloningProcessorImpl; - private static readonly Rectangle Bounds = new Rectangle(3, 3, 5, 5); + private static readonly Rectangle Bounds = new(3, 3, 5, 5); public ImageProcessingContextTests() { @@ -34,7 +34,7 @@ public ImageProcessingContextTests() } // bool throwException, bool useBounds - public static readonly TheoryData ProcessorTestData = new TheoryData() + public static readonly TheoryData ProcessorTestData = new() { { false, false }, { false, true }, diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs index cd61d68be5..a0ea99a9d0 100644 --- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs +++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs @@ -21,14 +21,7 @@ public void CalculateIntegralImage_Rgba32Works(TestImageProvider provide 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 void CalculateIntegralImage_WithBounds_Rgba32Works(TestImageProvider 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,15 +74,15 @@ public void CalculateIntegralImage_WithBounds_L8Works(TestImageProvider prov private static void VerifySumValues( TestImageProvider provider, Buffer2D integralBuffer, - System.Func getPixel) + Func getPixel) where TPixel : unmanaged, IPixel - => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds(), getPixel); + => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds, getPixel); private static void VerifySumValues( 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/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 60e33835af..c39648d014 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -21,7 +21,7 @@ public void GlobalHistogramEqualization_WithDifferentLuminanceLevels(int luminan { // Arrange byte[] pixels = - { + [ 52, 55, 61, 59, 70, 61, 76, 61, 62, 59, 55, 104, 94, 85, 59, 71, 63, 65, 66, 113, 144, 104, 63, 72, @@ -30,9 +30,9 @@ public void GlobalHistogramEqualization_WithDifferentLuminanceLevels(int luminan 68, 79, 60, 79, 77, 66, 58, 75, 69, 85, 64, 58, 55, 61, 65, 83, 70, 87, 69, 68, 65, 73, 78, 90 - }; + ]; - using (var image = new Image(8, 8)) + using (Image image = new(8, 8)) { for (int y = 0; y < 8; y++) { @@ -44,7 +44,7 @@ public void GlobalHistogramEqualization_WithDifferentLuminanceLevels(int luminan } byte[] expected = - { + [ 0, 12, 53, 32, 146, 53, 174, 53, 57, 32, 12, 227, 219, 202, 32, 154, 65, 85, 93, 239, 251, 227, 65, 158, @@ -53,7 +53,7 @@ public void GlobalHistogramEqualization_WithDifferentLuminanceLevels(int luminan 117, 190, 36, 190, 178, 93, 20, 170, 130, 202, 73, 20, 12, 53, 85, 194, 146, 206, 130, 117, 85, 166, 182, 215 - }; + ]; // Act image.Mutate(x => x.HistogramEqualization(new HistogramEqualizationOptions @@ -83,7 +83,7 @@ public void GlobalHistogramEqualization_CompareToReferenceOutput(TestIma { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.Global, LuminanceLevels = 256, @@ -101,7 +101,7 @@ public void Adaptive_SlidingWindow_15Tiles_WithClipping(TestImageProvide { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AdaptiveSlidingWindow, LuminanceLevels = 256, @@ -121,7 +121,7 @@ public void Adaptive_TileInterpolation_10Tiles_WithClipping(TestImagePro { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, @@ -141,7 +141,7 @@ public void AutoLevel_SeparateChannels_CompareToReferenceOutput(TestImag { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AutoLevel, LuminanceLevels = 256, @@ -160,7 +160,7 @@ public void AutoLevel_SynchronizedChannels_CompareToReferenceOutput(Test { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AutoLevel, LuminanceLevels = 256, @@ -187,7 +187,7 @@ public void Issue984(TestImageProvider provider) { using (Image image = provider.GetImage()) { - var options = new HistogramEqualizationOptions() + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, @@ -214,7 +214,7 @@ public unsafe void Issue1640(TestImageProvider provider) // https://github.com/SixLabors/ImageSharp/discussions/1640 // Test using isolated memory to ensure clean buffers for reference provider.Configuration = Configuration.CreateDefaultInstance(); - var options = new HistogramEqualizationOptions + HistogramEqualizationOptions options = new() { Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 4096, diff --git a/tests/ImageSharp.Tests/Processing/Normalization/MagickCompareTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/MagickCompareTests.cs index 5fb0a4e934..2b609a9b2a 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/MagickCompareTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/MagickCompareTests.cs @@ -1,13 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using ImageMagick; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Normalization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using ImageMagick; - namespace SixLabors.ImageSharp.Tests.Processing.Normalization; // ReSharper disable InconsistentNaming @@ -22,46 +21,43 @@ public void AutoLevel_CompareToMagick(TestImageProvider provider Image imageFromMagick; using (Stream stream = LoadAsStream(provider)) { - var magickImage = new MagickImage(stream); + using MagickImage magickImage = new(stream); // Apply Auto Level using the Grey (BT.709) channel. magickImage.AutoLevel(Channels.Gray); imageFromMagick = ConvertImageFromMagick(magickImage); } - using (Image image = provider.GetImage()) + using Image image = provider.GetImage(); + HistogramEqualizationOptions options = new() { - var options = new HistogramEqualizationOptions - { - Method = HistogramEqualizationMethod.AutoLevel, - LuminanceLevels = 256, - SyncChannels = true - }; - image.Mutate(x => x.HistogramEqualization(options)); - image.DebugSave(provider); - ExactImageComparer.Instance.CompareImages(imageFromMagick, image); - } + Method = HistogramEqualizationMethod.AutoLevel, + LuminanceLevels = 256, + SyncChannels = true + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + ExactImageComparer.Instance.CompareImages(imageFromMagick, image); + + imageFromMagick.Dispose(); } - private Stream LoadAsStream(TestImageProvider provider) + private static FileStream LoadAsStream(TestImageProvider provider) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { - string path = TestImageProvider.GetFilePathOrNull(provider); - if (path == null) - { - throw new InvalidOperationException("CompareToMagick() works only with file providers!"); - } + string path = TestImageProvider.GetFilePathOrNull(provider) + ?? throw new InvalidOperationException("CompareToMagick() works only with file providers!"); - var testFile = TestFile.Create(path); + TestFile testFile = TestFile.Create(path); return new FileStream(testFile.FullPath, FileMode.Open); } - private Image ConvertImageFromMagick(MagickImage magickImage) + private static Image ConvertImageFromMagick(MagickImage magickImage) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { Configuration configuration = Configuration.Default.Clone(); configuration.PreferContiguousImageBuffers = true; - var result = new Image(configuration, magickImage.Width, magickImage.Height); + Image result = new(configuration, (int)magickImage.Width, (int)magickImage.Height); Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels)); diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 7eec6eb0c1..c7881597ab 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -45,7 +45,7 @@ public void Glow_Radux_GlowProcessorWithDefaultValues() [Fact] public void Glow_Rect_GlowProcessorWithDefaultValues() { - var rect = new Rectangle(12, 123, 43, 65); + Rectangle rect = new(12, 123, 43, 65); this.operations.Glow(rect); GlowProcessor p = this.Verify(rect); diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 1602b5c7a2..e222c566ff 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -48,7 +48,7 @@ public void Vignette_Radux_VignetteProcessorWithDefaultValues() [Fact] public void Vignette_Rect_VignetteProcessorWithDefaultValues() { - var rect = new Rectangle(12, 123, 43, 65); + Rectangle rect = new(12, 123, 43, 65); this.operations.Vignette(rect); VignetteProcessor p = this.Verify(rect); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs index 9694aeb222..62682e837c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs @@ -13,11 +13,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization; public class BinaryDitherTests { public static readonly string[] CommonTestImages = - { - TestImages.Png.CalliphoraPartial, TestImages.Png.Bike - }; + [ + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + ]; - public static readonly TheoryData OrderedDitherers = new TheoryData + public static readonly TheoryData OrderedDitherers = new() { { "Bayer8x8", KnownDitherings.Bayer8x8 }, { "Bayer4x4", KnownDitherings.Bayer4x4 }, @@ -25,7 +25,7 @@ public class BinaryDitherTests { "Bayer2x2", KnownDitherings.Bayer2x2 } }; - public static readonly TheoryData ErrorDiffusers = new TheoryData + public static readonly TheoryData ErrorDiffusers = new() { { "Atkinson", KnownDitherings.Atkinson }, { "Burks", KnownDitherings.Burks }, @@ -102,7 +102,7 @@ public void ApplyDitherFilterInBox(TestImageProvider provider) using (Image source = provider.GetImage()) using (Image image = source.Clone()) { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + Rectangle bounds = new(10, 10, image.Width / 2, image.Height / 2); image.Mutate(x => x.BinaryDither(DefaultDitherer, bounds)); image.DebugSave(provider); @@ -119,7 +119,7 @@ public void ApplyDiffusionFilterInBox(TestImageProvider provider using (Image source = provider.GetImage()) using (Image image = source.Clone()) { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + Rectangle bounds = new(10, 10, image.Width / 2, image.Height / 2); image.Mutate(x => x.BinaryDither(DefaultErrorDiffuser, bounds)); image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs index ecbbb21abd..c9d3f69143 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -12,17 +12,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization; public class BinaryThresholdTest { public static readonly TheoryData BinaryThresholdValues - = new TheoryData - { + = new() + { .25F, .75F }; public static readonly string[] CommonTestImages = - { + [ TestImages.Png.Rgb48Bpp, - TestImages.Png.ColorsSaturationLightness, - }; + TestImages.Png.ColorsSaturationLightness + ]; public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; @@ -46,7 +46,7 @@ public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider source = provider.GetImage()) using (Image image = source.Clone()) { - var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); + Rectangle bounds = new(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); image.Mutate(x => x.BinaryThreshold(value, bounds)); image.DebugSave(provider, value); @@ -76,7 +76,7 @@ public void ImageShouldApplyBinarySaturationThresholdInBox(TestImageProv using (Image source = provider.GetImage()) using (Image image = source.Clone()) { - var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); + Rectangle bounds = new(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdMode.Saturation, bounds)); image.DebugSave(provider, value); @@ -96,7 +96,7 @@ public void ImageShouldApplyBinaryMaxChromaThresholdFilter(TestImageProv if (!TestEnvironment.Is64BitProcess && TestEnvironment.IsFramework) { - var comparer = ImageComparer.TolerantPercentage(0.0004F); + ImageComparer comparer = ImageComparer.TolerantPercentage(0.0004F); image.CompareToReferenceOutput(comparer, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo)); } else @@ -114,14 +114,14 @@ public void ImageShouldApplyBinaryMaxChromaThresholdInBox(TestImageProvi using (Image source = provider.GetImage()) using (Image image = source.Clone()) { - var bounds = new Rectangle(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); + Rectangle bounds = new(image.Width / 8, image.Height / 8, 6 * image.Width / 8, 6 * image.Width / 8); image.Mutate(x => x.BinaryThreshold(value, BinaryThresholdMode.MaxChroma, bounds)); image.DebugSave(provider, value); if (!TestEnvironment.Is64BitProcess && TestEnvironment.IsFramework) { - var comparer = ImageComparer.TolerantPercentage(0.0004F); + ImageComparer comparer = ImageComparer.TolerantPercentage(0.0004F); image.CompareToReferenceOutput(comparer, provider, value.ToString("0.00", NumberFormatInfo.InvariantInfo)); } else diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs index 13ddf85c1e..1eec353689 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -12,14 +12,14 @@ public abstract class Basic1ParameterConvolutionTests { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); - public static readonly TheoryData Values = new TheoryData { 3, 5 }; + public static readonly TheoryData Values = new() { 3, 5 }; public static readonly string[] InputImages = - { + [ TestImages.Bmp.Car, TestImages.Png.CalliphoraPartial, TestImages.Png.Blur - }; + ]; [Theory] [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index c94983ecd5..3d4c239c93 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Text.RegularExpressions; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -51,7 +50,7 @@ public void VerifyComplexComponents() List components = new(); foreach (Match match in Regex.Matches(Components10x2, @"\[\[(.*?)\]\]", RegexOptions.Singleline)) { - string[] values = match.Groups[1].Value.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + string[] values = match.Groups[1].Value.Trim().Split([' '], StringSplitOptions.RemoveEmptyEntries); Complex64[] component = values.Select( value => { @@ -115,12 +114,12 @@ public void Serialize(IXunitSerializationInfo info) }; public static readonly string[] TestFiles = - { - TestImages.Png.CalliphoraPartial, + [ + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike, TestImages.Png.BikeGrayscale, - TestImages.Png.Cross, - }; + TestImages.Png.Cross + ]; [Theory] [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32)] @@ -149,7 +148,7 @@ public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImagePro [Theory] [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.AllowAll)] - [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableSSE41)] + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableHWIntrinsic)] public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value, HwIntrinsics intrinsicsFilter) { static void RunTest(string arg1, string arg2) @@ -165,7 +164,7 @@ static void RunTest(string arg1, string arg2) { Size size = x.GetCurrentSize(); Rectangle bounds = new(10, 10, size.Width / 2, size.Height / 2); - x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds); + x.BokehBlur(bounds, value.Radius, value.Components, value.Gamma); }, testOutputDetails: value.ToString(), ImageComparer.TolerantPercentage(0.05f), diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionProcessorHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionProcessorHelpersTest.cs index 574c983710..38c6350042 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionProcessorHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionProcessorHelpersTest.cs @@ -41,6 +41,41 @@ public void VerifyGaussianKernelDecomposition(int radius) } } + /// + /// Verifies that Gaussian sharpening preserves the scalar kernel formula across scalar and SIMD lengths. + /// + /// The kernel radius. + [Theory] + [InlineData(1)] + [InlineData(3)] + [InlineData(9)] + [InlineData(32)] + [InlineData(80)] + public void VerifyGaussianSharpenKernel(int radius) + { + int kernelSize = (radius * 2) + 1; + float sigma = radius / 3F; + float[] expected = new float[kernelSize]; + float sum = 0F; + + for (int i = 0; i < kernelSize; i++) + { + float value = Numerics.Gaussian(i - radius, sigma); + expected[i] = value; + sum += value; + } + + for (int i = 0; i < kernelSize; i++) + { + expected[i] = i == radius ? (2F * sum) - expected[i] : -expected[i]; + expected[i] /= sum; + } + + float[] actual = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, sigma); + + Assert.Equal(expected, actual); + } + [Fact] public void VerifyNonSeparableMatrix() { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs new file mode 100644 index 0000000000..468965f1e2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/ConvolutionTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Extensions.Convolution; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution; + +[GroupOutput("Convolution")] +public class ConvolutionTests +{ + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); + + public static readonly TheoryData> Values = new() + { + // Sharpening kernel. + new float[,] + { + { -1, -1, -1 }, + { -1, 16, -1 }, + { -1, -1, -1 } + } + }; + + public static readonly string[] InputImages = + [ + TestImages.Bmp.Car, + TestImages.Png.CalliphoraPartial, + TestImages.Png.Blur + ]; + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void OnFullImage(TestImageProvider provider, DenseMatrix value) + where TPixel : unmanaged, IPixel + => provider.RunValidatingProcessorTest( + x => x.Convolve(value), + string.Join('_', value.Data), + ValidatorComparer); + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, DenseMatrix value) + where TPixel : unmanaged, IPixel + => provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.Convolve(rect, value), + string.Join('_', value.Data), + ValidatorComparer); +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index d51012f9ec..be3fc1e500 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -17,21 +17,21 @@ public class DetectEdgesTest private static readonly ImageComparer TransparentComparer = ImageComparer.TolerantPercentage(0.5F); - public static readonly string[] TestImages = { Tests.TestImages.Png.Bike }; + public static readonly string[] TestImages = [Tests.TestImages.Png.Bike]; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; public static readonly TheoryData DetectEdgesFilters - = new TheoryData - { + = new() + { { KnownEdgeDetectorKernels.Laplacian3x3, nameof(KnownEdgeDetectorKernels.Laplacian3x3) }, { KnownEdgeDetectorKernels.Laplacian5x5, nameof(KnownEdgeDetectorKernels.Laplacian5x5) }, { KnownEdgeDetectorKernels.LaplacianOfGaussian, nameof(KnownEdgeDetectorKernels.LaplacianOfGaussian) }, }; public static readonly TheoryData DetectEdges2DFilters - = new TheoryData - { + = new() + { { KnownEdgeDetectorKernels.Kayyali, nameof(KnownEdgeDetectorKernels.Kayyali) }, { KnownEdgeDetectorKernels.Prewitt, nameof(KnownEdgeDetectorKernels.Prewitt) }, { KnownEdgeDetectorKernels.RobertsCross, nameof(KnownEdgeDetectorKernels.RobertsCross) }, @@ -40,8 +40,8 @@ public static readonly TheoryData DetectEdges2DFil }; public static readonly TheoryData DetectEdgesCompassFilters - = new TheoryData - { + = new() + { { KnownEdgeDetectorKernels.Kirsch, nameof(KnownEdgeDetectorKernels.Kirsch) }, { KnownEdgeDetectorKernels.Robinson, nameof(KnownEdgeDetectorKernels.Robinson) }, }; @@ -55,7 +55,7 @@ public void DetectEdges_WorksOnWrappedMemoryImage(TestImageProvider { Size size = ctx.GetCurrentSize(); - var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2); + Rectangle bounds = new(10, 10, size.Width / 2, size.Height / 2); ctx.DetectEdges(bounds); }, comparer: OpaqueComparer, @@ -158,7 +158,7 @@ public void DetectEdges_InBox(TestImageProvider provider) { using (Image image = provider.GetImage()) { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + Rectangle bounds = new(10, 10, image.Width / 2, image.Height / 2); image.Mutate(x => x.DetectEdges(bounds)); image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 47ccaa46cd..f9f10cb70f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Convolution; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution; @@ -12,5 +14,33 @@ public class GaussianBlurTest : Basic1ParameterConvolutionTests protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => - ctx.GaussianBlur(value, bounds); + ctx.GaussianBlur(bounds, value); + + [Theory] + [InlineData(BorderWrappingMode.Repeat)] + [InlineData(BorderWrappingMode.Mirror)] + [InlineData(BorderWrappingMode.Bounce)] + [InlineData(BorderWrappingMode.Wrap)] + public void OnImageSmallerThanKernelRadius_LeavesSolidColorUnchanged(BorderWrappingMode mode) + { + // 8 rows against sigma 10 (61-tap kernel, radius 30). A normalized blur of a solid + // image must remain that color no matter how the border sampling folds. + Rgba32 red = new(255, 0, 0); + using Image image = new(130, 8, red); + image.Mutate(x => x.GaussianBlur(image.Bounds, 10F, mode, mode)); + + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < accessor.Height; y++) + { + foreach (Rgba32 pixel in accessor.GetRowSpan(y)) + { + Assert.InRange(pixel.R, 254, 255); + Assert.InRange(pixel.G, 0, 1); + Assert.InRange(pixel.B, 0, 1); + Assert.Equal(255, pixel.A); + } + } + }); + } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 96c58f7010..c6b7c82363 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -12,5 +12,5 @@ public class GaussianSharpenTest : Basic1ParameterConvolutionTests protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => - ctx.GaussianSharpen(value, bounds); + ctx.GaussianSharpen(bounds, value); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/KernelSamplingMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/KernelSamplingMapTests.cs new file mode 100644 index 0000000000..5194fa0010 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/KernelSamplingMapTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution; + +[Trait("Category", "Processors")] +[GroupOutput("Convolution")] +public class KernelSamplingMapTests +{ + public static readonly TheoryData BoundsSmallerThanKernel = new() + { + { BorderWrappingMode.Repeat, 8, 5 }, + { BorderWrappingMode.Mirror, 8, 5 }, + { BorderWrappingMode.Bounce, 8, 5 }, + { BorderWrappingMode.Wrap, 8, 5 }, + { BorderWrappingMode.Repeat, 1, 1 }, + { BorderWrappingMode.Mirror, 1, 1 }, + { BorderWrappingMode.Bounce, 1, 1 }, + { BorderWrappingMode.Wrap, 1, 1 }, + { BorderWrappingMode.Repeat, 30, 30 }, + { BorderWrappingMode.Mirror, 30, 30 }, + { BorderWrappingMode.Bounce, 30, 30 }, + { BorderWrappingMode.Wrap, 30, 30 }, + }; + + [Theory] + [MemberData(nameof(BoundsSmallerThanKernel))] + public void BuildSamplingOffsetMap_BoundsSmallerThanKernelRadius_OffsetsStayInBounds(BorderWrappingMode mode, int width, int height) + { + // A 61-tap kernel has radius 30, so these bounds are covered entirely by the border + // regions and offsets can overshoot the bounds by more than one sampling extent. + const int kernelSize = 61; + Rectangle bounds = new(100, 200, width, height); + + using KernelSamplingMap map = new(Configuration.Default.MemoryAllocator); + map.BuildSamplingOffsetMap(kernelSize, kernelSize, bounds, mode, mode); + + foreach (int x in map.GetColumnOffsetSpan()) + { + Assert.InRange(x, bounds.Left, bounds.Right - 1); + } + + foreach (int y in map.GetRowOffsetSpan()) + { + Assert.InRange(y, bounds.Top, bounds.Bottom - 1); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/MedianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/MedianBlurTest.cs index 7cc0ef9d50..048b843580 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/MedianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/MedianBlurTest.cs @@ -12,5 +12,5 @@ public class MedianBlurTest : Basic1ParameterConvolutionTests protected override void Apply(IImageProcessingContext ctx, int value) => ctx.MedianBlur(value, true); protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => - ctx.MedianBlur(value, true, bounds); + ctx.MedianBlur(bounds, value, true); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index b8e968f73a..25acd3aa67 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -14,10 +14,10 @@ public class DitherTests public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24 | PixelTypes.RgbaVector; - public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial, TestImages.Png.Bike }; + public static readonly string[] CommonTestImages = [TestImages.Png.CalliphoraPartial, TestImages.Png.Bike]; public static readonly TheoryData ErrorDiffusers - = new TheoryData + = new() { { KnownDitherings.Atkinson, nameof(KnownDitherings.Atkinson) }, { KnownDitherings.Burks, nameof(KnownDitherings.Burks) }, @@ -31,7 +31,7 @@ public static readonly TheoryData ErrorDiffusers }; public static readonly TheoryData OrderedDitherers - = new TheoryData + = new() { { KnownDitherings.Bayer2x2, nameof(KnownDitherings.Bayer2x2) }, { KnownDitherings.Bayer4x4, nameof(KnownDitherings.Bayer4x4) }, @@ -41,7 +41,7 @@ public static readonly TheoryData OrderedDitherers }; public static readonly TheoryData DefaultInstanceDitherers - = new TheoryData + = new() { default(ErrorDither), default(OrderedDither) @@ -101,7 +101,7 @@ public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImagePr } // Increased tolerance because of compatibility issues on .NET 4.6.2: - var comparer = ImageComparer.TolerantPercentage(1f); + ImageComparer comparer = ImageComparer.TolerantPercentage(1f); provider.RunValidatingProcessorTest(x => x.Dither(DefaultErrorDiffuser), comparer: comparer); } @@ -186,7 +186,7 @@ public void ShouldThrowForDefaultDitherInstance(IDither dither) { void Command() { - using var image = new Image(10, 10); + using Image image = new(10, 10); image.Mutate(x => x.Dither(dither)); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs index ed3be0f677..ab379ad7ca 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects; public class BackgroundColorTest { public static readonly string[] InputImages = - { - TestImages.Png.Splash, + [ + TestImages.Png.Splash, TestImages.Png.Ducky - }; + ]; [Theory] [WithFileCollection(nameof(InputImages), PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs index 10811a559e..b7e8597859 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -11,17 +11,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects; [GroupOutput("Effects")] public class OilPaintTest { - public static readonly TheoryData OilPaintValues = new TheoryData - { + public static readonly TheoryData OilPaintValues = new() + { { 15, 10 }, { 6, 5 } }; public static readonly string[] InputImages = - { - TestImages.Png.CalliphoraPartial, + [ + TestImages.Png.CalliphoraPartial, TestImages.Bmp.Car - }; + ]; [Theory] [WithFileCollection(nameof(InputImages), nameof(OilPaintValues), PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs index fd732f570f..b88ab37fe7 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs @@ -71,7 +71,7 @@ public void PositionAwareFullImage(TestImageProvider provider) Vector4 v4 = span[i]; float avg = (v4.X + v4.Y + v4.Z) / 3f; - var gray = new Vector4(avg, avg, avg, a); + Vector4 gray = new(avg, avg, avg, a); span[i] = Vector4.Clamp(gray, Vector4.Zero, Vector4.One); } @@ -101,7 +101,7 @@ public void PositionAwareInBox(TestImageProvider provider) Vector4 v4 = span[i]; float avg = (v4.X + v4.Y + v4.Z) / 3f; - var gray = new Vector4(avg, avg, avg, a); + Vector4 gray = new(avg, avg, avg, a); span[i] = Vector4.Clamp(gray, Vector4.Zero, Vector4.One); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs index 29b9524030..fff7ba0182 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects; [GroupOutput("Effects")] public class PixelateTest { - public static readonly TheoryData PixelateValues = new TheoryData { 4, 8 }; + public static readonly TheoryData PixelateValues = new() { 4, 8 }; [Theory] [WithFile(TestImages.Png.Ducky, nameof(PixelateValues), PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs index 7da96d13dd..4663aa868d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs @@ -14,7 +14,7 @@ public class BrightnessTest private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F); public static readonly TheoryData BrightnessValues - = new TheoryData + = new() { .5F, 1.5F diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs index 0727a5b979..1f2afb97f5 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs @@ -14,7 +14,7 @@ public class ColorBlindnessTest private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.03F); public static readonly TheoryData ColorBlindnessFilters - = new TheoryData + = new() { ColorBlindnessMode.Achromatomaly, ColorBlindnessMode.Achromatopsia, diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs index 5e2a7f55d1..6ff10bbd8e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters; public class ContrastTest { public static readonly TheoryData ContrastValues - = new TheoryData + = new() { .5F, 1.5F diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs index 50a2621524..15ef7f7c37 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters; public class GrayscaleTest { public static readonly TheoryData GrayscaleModeTypes - = new TheoryData + = new() { GrayscaleMode.Bt601, GrayscaleMode.Bt709 diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs index 65ac4245d7..1838cc33db 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters; public class HueTest { public static readonly TheoryData HueValues - = new TheoryData + = new() { 180, -180 diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs index 39e69aa4a5..49e2c85467 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -14,7 +14,7 @@ public class LightnessTest private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F); public static readonly TheoryData LightnessValues - = new TheoryData + = new() { .5F, 1.5F diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs index 10ddf3a47d..8a1bbfe3f9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters; public class OpacityTest { public static readonly TheoryData AlphaValues - = new TheoryData + = new() { 20 / 100F, 80 / 100F diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs index 8632d46259..642c798196 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters; public class SaturateTest { public static readonly TheoryData SaturationValues - = new TheoryData + = new() { .5F, 1.5F, diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs index cf2b7d4dad..3005867ca8 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays; [GroupOutput("Overlays")] public abstract class OverlayTestBase { - public static string[] ColorNames = { "Blue", "White" }; + public static string[] ColorNames = ["Blue", "White"]; - public static string[] InputImages = { TestImages.Png.Ducky, TestImages.Png.Splash }; + public static string[] InputImages = [TestImages.Png.Ducky, TestImages.Png.Splash]; private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05f); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/HexadecatreeQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/HexadecatreeQuantizerTests.cs new file mode 100644 index 0000000000..b154551e24 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/HexadecatreeQuantizerTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization; + +[Trait("Category", "Processors")] +public class HexadecatreeQuantizerTests +{ + [Fact] + public void HexadecatreeQuantizerConstructor() + { + QuantizerOptions expected = new() { MaxColors = 128 }; + HexadecatreeQuantizer quantizer = new(expected); + + Assert.Equal(expected.MaxColors, quantizer.Options.MaxColors); + Assert.Equal(QuantizerConstants.DefaultDither, quantizer.Options.Dither); + + expected = new QuantizerOptions { Dither = null }; + quantizer = new HexadecatreeQuantizer(expected); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.Options.MaxColors); + Assert.Null(quantizer.Options.Dither); + + expected = new QuantizerOptions { Dither = KnownDitherings.Atkinson }; + quantizer = new HexadecatreeQuantizer(expected); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.Options.MaxColors); + Assert.Equal(KnownDitherings.Atkinson, quantizer.Options.Dither); + + expected = new QuantizerOptions { Dither = KnownDitherings.Atkinson, MaxColors = 0 }; + quantizer = new HexadecatreeQuantizer(expected); + Assert.Equal(QuantizerConstants.MinColors, quantizer.Options.MaxColors); + Assert.Equal(KnownDitherings.Atkinson, quantizer.Options.Dither); + } + + [Fact] + public void HexadecatreeQuantizerCanCreateFrameQuantizer() + { + HexadecatreeQuantizer quantizer = new(); + IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.NotNull(frameQuantizer.Options); + Assert.Equal(QuantizerConstants.DefaultDither, frameQuantizer.Options.Dither); + frameQuantizer.Dispose(); + + quantizer = new HexadecatreeQuantizer(new QuantizerOptions { Dither = null }); + frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.Null(frameQuantizer.Options.Dither); + frameQuantizer.Dispose(); + + quantizer = new HexadecatreeQuantizer(new QuantizerOptions { Dither = KnownDitherings.Atkinson }); + frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); + Assert.NotNull(frameQuantizer); + Assert.Equal(KnownDitherings.Atkinson, frameQuantizer.Options.Dither); + frameQuantizer.Dispose(); + } + + [Fact] + public void PaletteUsesNormalizedTransparencyThreshold() + { + using HexadecatreeQuantizer.Hexadecatree tree = new(Configuration.Default, 8, 2, .5F); + tree.AddColors([new Rgba32(255, 255, 255, 127)]); + + Span palette = stackalloc Rgba32[2]; + short paletteIndex = 0; + tree.Palettize(palette, ref paletteIndex); + + Assert.Equal(1, paletteIndex); + Assert.Equal(default, palette[0]); + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs deleted file mode 100644 index c6880d3a81..0000000000 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Quantization; - -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization; - -[Trait("Category", "Processors")] -public class OctreeQuantizerTests -{ - [Fact] - public void OctreeQuantizerConstructor() - { - var expected = new QuantizerOptions { MaxColors = 128 }; - var quantizer = new OctreeQuantizer(expected); - - Assert.Equal(expected.MaxColors, quantizer.Options.MaxColors); - Assert.Equal(QuantizerConstants.DefaultDither, quantizer.Options.Dither); - - expected = new QuantizerOptions { Dither = null }; - quantizer = new OctreeQuantizer(expected); - Assert.Equal(QuantizerConstants.MaxColors, quantizer.Options.MaxColors); - Assert.Null(quantizer.Options.Dither); - - expected = new QuantizerOptions { Dither = KnownDitherings.Atkinson }; - quantizer = new OctreeQuantizer(expected); - Assert.Equal(QuantizerConstants.MaxColors, quantizer.Options.MaxColors); - Assert.Equal(KnownDitherings.Atkinson, quantizer.Options.Dither); - - expected = new QuantizerOptions { Dither = KnownDitherings.Atkinson, MaxColors = 0 }; - quantizer = new OctreeQuantizer(expected); - Assert.Equal(QuantizerConstants.MinColors, quantizer.Options.MaxColors); - Assert.Equal(KnownDitherings.Atkinson, quantizer.Options.Dither); - } - - [Fact] - public void OctreeQuantizerCanCreateFrameQuantizer() - { - var quantizer = new OctreeQuantizer(); - IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); - - Assert.NotNull(frameQuantizer); - Assert.NotNull(frameQuantizer.Options); - Assert.Equal(QuantizerConstants.DefaultDither, frameQuantizer.Options.Dither); - frameQuantizer.Dispose(); - - quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }); - frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); - - Assert.NotNull(frameQuantizer); - Assert.Null(frameQuantizer.Options.Dither); - frameQuantizer.Dispose(); - - quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = KnownDitherings.Atkinson }); - frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); - Assert.NotNull(frameQuantizer); - Assert.Equal(KnownDitherings.Atkinson, frameQuantizer.Options.Dither); - frameQuantizer.Dispose(); - } -} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs index 91cf90bc46..07e9a4b0d6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -10,13 +11,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization; [Trait("Category", "Processors")] public class PaletteQuantizerTests { - private static readonly Color[] Palette = { Color.Red, Color.Green, Color.Blue }; + private static readonly Color[] Palette = [Color.Red, Color.Green, Color.Blue]; [Fact] public void PaletteQuantizerConstructor() { - var expected = new QuantizerOptions { MaxColors = 128 }; - var quantizer = new PaletteQuantizer(Palette, expected); + QuantizerOptions expected = new() { MaxColors = 128 }; + PaletteQuantizer quantizer = new(Palette, expected); Assert.Equal(expected.MaxColors, quantizer.Options.MaxColors); Assert.Equal(QuantizerConstants.DefaultDither, quantizer.Options.Dither); @@ -40,7 +41,7 @@ public void PaletteQuantizerConstructor() [Fact] public void PaletteQuantizerCanCreateFrameQuantizer() { - var quantizer = new PaletteQuantizer(Palette); + PaletteQuantizer quantizer = new(Palette); IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); @@ -75,4 +76,161 @@ public void KnownQuantizersWernerTests() IQuantizer quantizer = KnownQuantizers.Werner; Assert.Equal(QuantizerConstants.DefaultDither, quantizer.Options.Dither); } + + [Fact] + public void ExactColorMatchingMatchesUncachedAfterCacheOverflow() + { + Rgba32[] palette = + [ + new Rgba32(0, 0, 0), + new Rgba32(7, 0, 0) + ]; + + using PixelMap exact = CreatePixelMap(palette); + using PixelMap cachedExact = CreatePixelMap(palette); + + for (int i = 0; i < AccurateCache.Capacity; i++) + { + cachedExact.GetClosestColor(CreateOverflowFillerColor(i), out _); + } + + Rgba32 first = new(1, 0, 0); + Rgba32 second = new(6, 0, 0); + + AssertMatchesUncached(exact, cachedExact, first); + AssertMatchesUncached(exact, cachedExact, second); + } + + [Fact] + public void ExactColorMatchingMatchesUncachedAcrossManyProbeBinsAfterRepeatedEviction() + { + Rgba32[] palette = CreateGrayscalePalette(256); + + using PixelMap exact = CreatePixelMap(palette); + using PixelMap cachedExact = CreatePixelMap(palette); + + for (int i = 0; i < AccurateCache.Capacity * 2; i++) + { + cachedExact.GetClosestColor(CreateEvictionFillerColor(i), out _); + } + + for (int i = 0; i < AccurateCache.Capacity; i++) + { + AssertMatchesUncached(exact, cachedExact, CreateEvictionProbeColor(i)); + } + } + + [Fact] + public void ExactColorMatchingMatchesUncachedForDitherStressColorSequence() + { + Rgba32[] palette = CreateGrayscalePalette(16); + + using Image source = CreateDitherStressImage(); + using PixelMap exact = CreatePixelMap(palette); + using PixelMap cachedExact = CreatePixelMap(palette); + + for (int y = 0; y < source.Height; y++) + { + for (int x = 0; x < source.Width; x++) + { + AssertMatchesUncached(exact, cachedExact, source[x, y]); + } + } + } + + // Split the first 512 integers across R and G so the warmup loop produces 512 distinct exact colors: + // the low 8 bits go into R, and the ninth bit spills into G once R wraps after 255. + // Keeping B fixed and G offset away from zero also avoids accidentally probing the red-axis test colors below. + private static Rgba32 CreateOverflowFillerColor(int i) + => new((byte)i, (byte)(16 + (i >> 8)), 32); + + // Treat i as three packed 5-bit coordinates and expand each coordinate back to an 8-bit channel by + // shifting left by 3. That lands on the lower edge of each 5-bit coarse bucket, giving the test a + // deterministic way to fill many distinct coarse buckets before probing nearby exact colors. + private static Rgba32 CreateEvictionFillerColor(int i) + { + byte r = (byte)((i & 31) << 3); + byte g = (byte)(((i >> 5) & 31) << 3); + byte b = (byte)(((i >> 10) & 31) << 3); + return new(r, g, b); + } + + // Reconstruct the same 5-bit RGB bucket coordinates used by CreateEvictionFillerColor, then set the + // low 3 bits in each channel to 0b111. That keeps the probe inside the same coarse bucket while making + // it a different exact color, which is the shape that used to expose coarse-fallback false hits. + private static Rgba32 CreateEvictionProbeColor(int i) + { + byte r = (byte)(((i & 31) << 3) | 0x07); + byte g = (byte)((((i >> 5) & 31) << 3) | 0x07); + byte b = (byte)((((i >> 10) & 31) << 3) | 0x07); + return new(r, g, b); + } + + private static PixelMap CreatePixelMap(Rgba32[] palette) + where TCache : struct, IColorIndexCache + => new EuclideanPixelMap(Configuration.Default, palette); + + private static void AssertMatchesUncached(PixelMap exact, PixelMap cachedExact, Rgba32 color) + { + int exactIndex = exact.GetClosestColor(color, out Rgba32 exactMatch); + int cachedIndex = cachedExact.GetClosestColor(color, out Rgba32 cachedMatch); + + Assert.Equal(exactIndex, cachedIndex); + Assert.Equal(exactMatch, cachedMatch); + } + + private static Rgba32[] CreateGrayscalePalette(int count) + { + Rgba32[] palette = new Rgba32[count]; + for (int i = 0; i < count; i++) + { + byte value = count == 1 ? (byte)0 : (byte)((i * 255) / (count - 1)); + palette[i] = new Rgba32(value, value, value); + } + + return palette; + } + + // Generate a deterministic pseudo-image where each channel uses a different x/y slope. + // Neighboring pixels stay correlated, like real image content, but the combined RGB values + // churn heavily enough that exact repeats are rare. That makes this a useful stress input + // for verifying cached exact matching against an uncached baseline under dither-like access. + private static Image CreateDitherStressImage() + { + Image image = new(192, 96); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + image[x, y] = new Rgba32( + (byte)((x * 17) + (y * 13)), + (byte)((x * 29) + (y * 7)), + (byte)((x * 11) + (y * 23))); + } + } + + return image; + } + + private readonly struct UncachedCache : IColorIndexCache + { + public static UncachedCache Create(MemoryAllocator allocator) => default; + + public bool TryAdd(Rgba32 color, short value) => true; + + public bool TryGetValue(Rgba32 color, out short value) + { + value = -1; + return false; + } + + public void Clear() + { + } + + public void Dispose() + { + } + } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs index b8b4615b94..00e09d83b0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs @@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization; public class QuantizerTests { public static readonly string[] CommonTestImages = - { + [ TestImages.Png.CalliphoraPartial, TestImages.Png.Bike - }; + ]; private static readonly QuantizerOptions NoDitherOptions = new() { Dither = null }; private static readonly QuantizerOptions DiffuserDitherOptions = new() { Dither = KnownDitherings.FloydSteinberg }; @@ -74,15 +74,15 @@ public static readonly TheoryData Quantizers = new() { // Known uses error diffusion by default. - KnownQuantizers.Octree, + KnownQuantizers.Hexadecatree, KnownQuantizers.WebSafe, KnownQuantizers.Werner, KnownQuantizers.Wu, - new OctreeQuantizer(NoDitherOptions), + new HexadecatreeQuantizer(NoDitherOptions), new WebSafePaletteQuantizer(NoDitherOptions), new WernerPaletteQuantizer(NoDitherOptions), new WuQuantizer(NoDitherOptions), - new OctreeQuantizer(OrderedDitherOptions), + new HexadecatreeQuantizer(OrderedDitherOptions), new WebSafePaletteQuantizer(OrderedDitherOptions), new WernerPaletteQuantizer(OrderedDitherOptions), new WuQuantizer(OrderedDitherOptions) @@ -91,52 +91,52 @@ public static readonly TheoryData Quantizers public static readonly TheoryData DitherScaleQuantizers = new() { - new OctreeQuantizer(Diffuser0_ScaleDitherOptions), + new HexadecatreeQuantizer(Diffuser0_ScaleDitherOptions), new WebSafePaletteQuantizer(Diffuser0_ScaleDitherOptions), new WernerPaletteQuantizer(Diffuser0_ScaleDitherOptions), new WuQuantizer(Diffuser0_ScaleDitherOptions), - new OctreeQuantizer(Diffuser0_25_ScaleDitherOptions), + new HexadecatreeQuantizer(Diffuser0_25_ScaleDitherOptions), new WebSafePaletteQuantizer(Diffuser0_25_ScaleDitherOptions), new WernerPaletteQuantizer(Diffuser0_25_ScaleDitherOptions), new WuQuantizer(Diffuser0_25_ScaleDitherOptions), - new OctreeQuantizer(Diffuser0_5_ScaleDitherOptions), + new HexadecatreeQuantizer(Diffuser0_5_ScaleDitherOptions), new WebSafePaletteQuantizer(Diffuser0_5_ScaleDitherOptions), new WernerPaletteQuantizer(Diffuser0_5_ScaleDitherOptions), new WuQuantizer(Diffuser0_5_ScaleDitherOptions), - new OctreeQuantizer(Diffuser0_75_ScaleDitherOptions), + new HexadecatreeQuantizer(Diffuser0_75_ScaleDitherOptions), new WebSafePaletteQuantizer(Diffuser0_75_ScaleDitherOptions), new WernerPaletteQuantizer(Diffuser0_75_ScaleDitherOptions), new WuQuantizer(Diffuser0_75_ScaleDitherOptions), - new OctreeQuantizer(DiffuserDitherOptions), + new HexadecatreeQuantizer(DiffuserDitherOptions), new WebSafePaletteQuantizer(DiffuserDitherOptions), new WernerPaletteQuantizer(DiffuserDitherOptions), new WuQuantizer(DiffuserDitherOptions), - new OctreeQuantizer(Ordered0_ScaleDitherOptions), + new HexadecatreeQuantizer(Ordered0_ScaleDitherOptions), new WebSafePaletteQuantizer(Ordered0_ScaleDitherOptions), new WernerPaletteQuantizer(Ordered0_ScaleDitherOptions), new WuQuantizer(Ordered0_ScaleDitherOptions), - new OctreeQuantizer(Ordered0_25_ScaleDitherOptions), + new HexadecatreeQuantizer(Ordered0_25_ScaleDitherOptions), new WebSafePaletteQuantizer(Ordered0_25_ScaleDitherOptions), new WernerPaletteQuantizer(Ordered0_25_ScaleDitherOptions), new WuQuantizer(Ordered0_25_ScaleDitherOptions), - new OctreeQuantizer(Ordered0_5_ScaleDitherOptions), + new HexadecatreeQuantizer(Ordered0_5_ScaleDitherOptions), new WebSafePaletteQuantizer(Ordered0_5_ScaleDitherOptions), new WernerPaletteQuantizer(Ordered0_5_ScaleDitherOptions), new WuQuantizer(Ordered0_5_ScaleDitherOptions), - new OctreeQuantizer(Ordered0_75_ScaleDitherOptions), + new HexadecatreeQuantizer(Ordered0_75_ScaleDitherOptions), new WebSafePaletteQuantizer(Ordered0_75_ScaleDitherOptions), new WernerPaletteQuantizer(Ordered0_75_ScaleDitherOptions), new WuQuantizer(Ordered0_75_ScaleDitherOptions), - new OctreeQuantizer(OrderedDitherOptions), + new HexadecatreeQuantizer(OrderedDitherOptions), new WebSafePaletteQuantizer(OrderedDitherOptions), new WernerPaletteQuantizer(OrderedDitherOptions), new WuQuantizer(OrderedDitherOptions), diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs index ab4304d898..2d270b2a47 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs @@ -13,8 +13,8 @@ public class WuQuantizerTests [Fact] public void WuQuantizerConstructor() { - var expected = new QuantizerOptions { MaxColors = 128 }; - var quantizer = new WuQuantizer(expected); + QuantizerOptions expected = new() { MaxColors = 128 }; + WuQuantizer quantizer = new(expected); Assert.Equal(expected.MaxColors, quantizer.Options.MaxColors); Assert.Equal(QuantizerConstants.DefaultDither, quantizer.Options.Dither); @@ -38,7 +38,7 @@ public void WuQuantizerConstructor() [Fact] public void WuQuantizerCanCreateFrameQuantizer() { - var quantizer = new WuQuantizer(); + WuQuantizer quantizer = new(); IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 05604ac6e6..941d78c436 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 static readonly TheoryData TransformVa { 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 static readonly TheoryData TransformVa }; public static readonly TheoryData Transform_DoesNotCreateEdgeArtifacts_ResamplerNames = - new TheoryData - { + new() + { nameof(KnownResamplers.NearestNeighbor), nameof(KnownResamplers.Triangle), nameof(KnownResamplers.Bicubic), @@ -75,16 +75,14 @@ public void Transform_DoesNotCreateEdgeArtifacts(TestImageProvider { 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 void Transform_RotateScaleTranslate( 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 void Transform_RotateScaleTranslate( 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 void Transform_RotateScale_ManuallyCentered(TestImageProvider(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 void Transform_FromSourceRectangle1(TestImageProvider pro 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 void Transform_WithSampler(TestImageProvider provider, st 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 void WorksWithDiscoBuffers(TestImageProvider provider, in [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); @@ -246,7 +234,23 @@ public void Issue2753(TestImageProvider provider) image.DebugSave(provider); Assert.Equal(4, image.Width); - Assert.Equal(8, image.Height); + Assert.Equal(7, image.Height); + } + + [Theory] + [WithFile(TestImages.Png.Issue3000, PixelTypes.Rgba32, 3, 3)] + [WithFile(TestImages.Png.Issue3000, PixelTypes.Rgba32, 4, 4)] + public void Issue3000(TestImageProvider provider, float x, float y) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Mutate(c => c + .Transform(new AffineTransformBuilder().AppendRotationDegrees(90, new Vector2(x, y)))); + + string details = $"p-{x}-{y}"; + image.DebugSave(provider, testOutputDetails: details); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: details); } [Theory] @@ -272,49 +276,56 @@ public void Transform_With_Custom_Dimensions(TestImageProvider p { 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); image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); } - [Fact] - public void TransformRotationDoesNotOffset() + [Theory] + [WithSolidFilledImages(100, 100, "DimGray", PixelTypes.Rgba32)] + public void TransformRotationDoesNotOffset(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - Rgba32 marker = Color.Aqua; + Rgba32 background = Color.DimGray.ToPixel(); + TPixel marker = Color.Aqua.ToPixel(); + + using Image canvas = provider.GetImage(); - using Image img = new(100, 100, Color.DimGray); + using Image img = canvas.Clone(); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, Color.DimGray); + img.DebugSave(provider, "Rotate180"); + + using Image img2 = canvas.Clone(); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, Color.DimGray); + img.DebugSave(provider, "AffineRotate180NN"); + + using Image img3 = canvas.Clone(); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); + img3.DebugSave(provider, "AffineRotate180Bicubic"); + ImageComparer.Exact.VerifySimilarity(img, img2); ImageComparer.Exact.VerifySimilarity(img, img3); } 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); } @@ -323,11 +334,10 @@ private static void VerifyAllPixelsAreWhiteOrTransparent(Image i 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/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs index 6a61538ea1..ee21920d4d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -17,8 +17,8 @@ public class AutoOrientTests public static readonly TheoryData InvalidOrientationValues = new() { - { ExifDataType.Byte, new byte[] { 1 } }, - { ExifDataType.SignedByte, new byte[] { 2 } }, + { ExifDataType.Byte, [1] }, + { ExifDataType.SignedByte, [2] }, { ExifDataType.SignedShort, BitConverter.GetBytes((short)3) }, { ExifDataType.Long, BitConverter.GetBytes(4U) }, { ExifDataType.SignedLong, BitConverter.GetBytes(5) } @@ -57,7 +57,7 @@ public void AutoOrient_WorksForAllExifOrientations(TestImageProvider(TestImageProvider provider, ExifDataType dataType, byte[] orientation) where TPixel : unmanaged, IPixel { - var profile = new ExifProfile(); + ExifProfile profile = new(); profile.SetValue(ExifTag.JPEGTables, orientation); byte[] bytes = profile.ToByteArray(); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs index 18a7a9bd09..56e9a5201b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; @@ -17,12 +19,36 @@ public class CropTest public void Crop(TestImageProvider provider, int x, int y, int w, int h) where TPixel : unmanaged, IPixel { - var rect = new Rectangle(x, y, w, h); + Rectangle rect = new(x, y, w, h); FormattableString info = $"X{x}Y{y}.W{w}H{h}"; provider.RunValidatingProcessorTest( ctx => ctx.Crop(rect), info, - appendPixelTypeToFileName: false, - comparer: ImageComparer.Exact); + comparer: ImageComparer.Exact, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void CropUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 50, 50]); + + image.Mutate(ctx => ctx.Crop(Rectangle.FromLTRB(20, 20, 50, 50))); + + // The new subject area is now relative to the cropped area. + // overhanging pixels are constrained to the dimensions of the image. + Assert.Equal( + [0, 0], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + Assert.Equal( + [0, 0, 30, 30], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs index ca4b00d8aa..ffa9be6ea7 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs @@ -10,14 +10,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; [GroupOutput("Transforms")] public class EntropyCropTest { - public static readonly TheoryData EntropyCropValues = new TheoryData { .25F, .75F }; + public static readonly TheoryData EntropyCropValues = new() { .25F, .75F }; public static readonly string[] InputImages = - { - TestImages.Png.Ducky, + [ + TestImages.Png.Ducky, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK - }; + ]; [Theory] [WithFileCollection(nameof(InputImages), nameof(EntropyCropValues), PixelTypes.Rgba32)] @@ -35,8 +35,8 @@ public void Entropy_WillNotCropWhiteImage(TestImageProvider prov { // arrange using Image image = provider.GetImage(); - var expectedHeight = image.Height; - var expectedWidth = image.Width; + int expectedHeight = image.Height; + int expectedWidth = image.Width; // act image.Mutate(img => img.EntropyCrop()); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs index 717582274f..9aa04e370a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; @@ -12,12 +14,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; public class FlipTests { public static readonly TheoryData FlipValues = - new TheoryData - { - FlipMode.None, - FlipMode.Vertical, - FlipMode.Horizontal, - }; + new() + { + FlipMode.None, + FlipMode.Vertical, + FlipMode.Horizontal + }; [Theory] [WithTestPatternImages(nameof(FlipValues), 20, 37, PixelTypes.Rgba32)] @@ -25,23 +27,77 @@ public class FlipTests [WithTestPatternImages(nameof(FlipValues), 17, 32, PixelTypes.Rgba32)] public void Flip(TestImageProvider provider, FlipMode flipMode) where TPixel : unmanaged, IPixel - { - provider.RunValidatingProcessorTest( + => provider.RunValidatingProcessorTest( ctx => ctx.Flip(flipMode), testOutputDetails: flipMode, appendPixelTypeToFileName: false); - } [Theory] [WithTestPatternImages(nameof(FlipValues), 53, 37, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(FlipValues), 17, 32, PixelTypes.Rgba32)] public void Flip_WorksOnWrappedMemoryImage(TestImageProvider provider, FlipMode flipMode) where TPixel : unmanaged, IPixel - { - provider.RunValidatingProcessorTestOnWrappedMemoryImage( + => provider.RunValidatingProcessorTestOnWrappedMemoryImage( ctx => ctx.Flip(flipMode), testOutputDetails: flipMode, useReferenceOutputFrom: nameof(this.Flip), appendPixelTypeToFileName: false); + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FlipVerticalUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 50, 50]); + + image.Mutate(ctx => ctx.Flip(FlipMode.Vertical)); + + // The subject location is a single coordinate, so a vertical flip simply reflects its Y position: + // newY = imageHeight - originalY - 1 + // This mirrors the point vertically around the image's horizontal axis, preserving its X coordinate. + Assert.Equal( + [5, 84], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + // The subject area is now inverted because a vertical flip reflects the image across + // the horizontal axis passing through the image center. + // The Y-coordinate of the top edge is recalculated as: + // newY = imageHeight - originalY - height - 1 + Assert.Equal( + [5, 34, 50, 50], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FlipHorizontalUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 50, 50]); + + image.Mutate(ctx => ctx.Flip(FlipMode.Horizontal)); + + // The subject location is a single coordinate, so a horizontal flip simply reflects its X position: + // newX = imageWidth - originalX - 1 + // This mirrors the point horizontally around the image's vertical axis, preserving its Y coordinate. + Assert.Equal( + [94, 15], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + // The subject area is now inverted because a horizontal flip reflects the image across + // the vertical axis passing through the image center. + // The X-coordinate of the left edge is recalculated as: + // newX = imageWidth - originalX - width - 1 + Assert.Equal( + [44, 15, 50, 50], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs index 8949049fbc..0b6f5702f3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -10,9 +10,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; public class PadTest { public static readonly string[] CommonTestImages = - { + [ TestImages.Png.CalliphoraPartial, TestImages.Png.Bike - }; + ]; [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs new file mode 100644 index 0000000000..c65f136d66 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs @@ -0,0 +1,277 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Reflection; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using Xunit.Abstractions; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; + +[Trait("Category", "Processors")] +public class ProjectiveTransformTests +{ + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.03f, 3); + private static readonly ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.5f, 3); + + private ITestOutputHelper Output { get; } + + public static readonly TheoryData ResamplerNames = new() + { + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), + nameof(KnownResamplers.CatmullRom), + nameof(KnownResamplers.Hermite), + nameof(KnownResamplers.Lanczos2), + nameof(KnownResamplers.Lanczos3), + nameof(KnownResamplers.Lanczos5), + nameof(KnownResamplers.Lanczos8), + nameof(KnownResamplers.MitchellNetravali), + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Robidoux), + nameof(KnownResamplers.RobidouxSharp), + nameof(KnownResamplers.Spline), + nameof(KnownResamplers.Triangle), + nameof(KnownResamplers.Welch), + }; + + public static readonly TheoryData TaperMatrixData = new() + { + { TaperSide.Bottom, TaperCorner.Both }, + { TaperSide.Bottom, TaperCorner.LeftOrTop }, + { TaperSide.Bottom, TaperCorner.RightOrBottom }, + { TaperSide.Top, TaperCorner.Both }, + { TaperSide.Top, TaperCorner.LeftOrTop }, + { TaperSide.Top, TaperCorner.RightOrBottom }, + { TaperSide.Left, TaperCorner.Both }, + { TaperSide.Left, TaperCorner.LeftOrTop }, + { TaperSide.Left, TaperCorner.RightOrBottom }, + { TaperSide.Right, TaperCorner.Both }, + { TaperSide.Right, TaperCorner.LeftOrTop }, + { TaperSide.Right, TaperCorner.RightOrBottom }, + }; + + public static readonly TheoryData QuadDistortionData = new() + { + { new PointF(0, 0), new PointF(150, 0), new PointF(150, 150), new PointF(0, 150) }, // source == destination + { new PointF(25, 50), new PointF(210, 25), new PointF(140, 210), new PointF(15, 125) }, // Distortion + { new PointF(-50, -50), new PointF(200, -50), new PointF(200, 200), new PointF(-50, 200) }, // Scaling + { new PointF(150, 0), new PointF(150, 150), new PointF(0, 150), new PointF(0, 0) }, // Rotation + }; + + public ProjectiveTransformTests(ITestOutputHelper output) => this.Output = output; + + [Theory] + [WithTestPatternImages(nameof(ResamplerNames), 150, 150, PixelTypes.Rgba32)] + public void Transform_WithSampler(TestImageProvider provider, string resamplerName) + where TPixel : unmanaged, IPixel + { + IResampler sampler = GetResampler(resamplerName); + using Image image = provider.GetImage(); + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendTaper(TaperSide.Right, TaperCorner.Both, .5F); + + image.Mutate(i => i.Transform(builder, sampler)); + + image.DebugSave(provider, resamplerName); + image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); + } + + [Theory] + [WithSolidFilledImages(nameof(TaperMatrixData), 30, 30, nameof(Color.Red), PixelTypes.Rgba32)] + public void Transform_WithTaperMatrix(TestImageProvider provider, TaperSide taperSide, TaperCorner taperCorner) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendTaper(taperSide, taperCorner, .5F); + + image.Mutate(i => i.Transform(builder)); + + FormattableString testOutputDetails = $"{taperSide}-{taperCorner}"; + image.DebugSave(provider, testOutputDetails); + image.CompareFirstFrameToReferenceOutput(TolerantComparer, provider, testOutputDetails); + } + + [Theory] + [WithTestPatternImages(nameof(QuadDistortionData), 150, 150, PixelTypes.Rgba32)] + public void Transform_WithQuadDistortion(TestImageProvider provider, PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendQuadDistortion(topLeft, topRight, bottomRight, bottomLeft); + + image.Mutate(i => i.Transform(builder)); + + FormattableString testOutputDetails = $"{topLeft}-{topRight}-{bottomRight}-{bottomLeft}"; + image.DebugSave(provider, testOutputDetails); + image.CompareFirstFrameToReferenceOutput(TolerantComparer, provider, testOutputDetails); + } + + [Theory] + [WithSolidFilledImages(100, 100, 0, 0, 255, PixelTypes.Rgba32)] + public void RawTransformMatchesDocumentedExample(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // Printing some extra output to help investigating rounding errors: + this.Output.WriteLine($"Vector.IsHardwareAccelerated: {Vector.IsHardwareAccelerated}"); + + // This test matches the output described in the example at + // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/non-affine + using Image image = provider.GetImage(); + Matrix4x4 matrix = Matrix4x4.Identity; + matrix.M14 = 0.01F; + + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendMatrix(matrix); + + image.Mutate(i => i.Transform(builder)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(TolerantComparer, provider); + } + + [Theory] + [WithSolidFilledImages(290, 154, 0, 0, 255, PixelTypes.Rgba32)] + public void PerspectiveTransformMatchesCSS(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + // https://jsfiddle.net/dFrHS/545/ + // https://github.com/SixLabors/ImageSharp/issues/787 + using Image image = provider.GetImage(); +#pragma warning disable SA1117 // Parameters should be on same line or separate lines + Matrix4x4 matrix = new( + 0.260987f, -0.434909f, 0, -0.0022184f, + 0.373196f, 0.949882f, 0, -0.000312129f, + 0, 0, 1, 0, + 52, 165, 0, 1); +#pragma warning restore SA1117 // Parameters should be on same line or separate lines + + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendMatrix(matrix); + + image.Mutate(i => i.Transform(builder)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(TolerantComparer, provider); + } + + [Fact] + public void Issue1911() + { + using Image image = new(100, 100); + image.Mutate(x => x = x.Transform(new Rectangle(0, 0, 99, 100), Matrix4x4.Identity, new Size(99, 100), KnownResamplers.Lanczos2)); + + Assert.Equal(99, image.Width); + Assert.Equal(100, image.Height); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void Identity(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + Matrix4x4 m = Matrix4x4.Identity; + Rectangle r = new(25, 25, 50, 50); + image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0.0001F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 57F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0F)] + public void Transform_With_Custom_Dimensions(TestImageProvider provider, float radians) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + Matrix4x4 m = Matrix4x4.CreateRotationX(radians, new Vector3(50, 50, 1F)) * Matrix4x4.CreateRotationY(radians, new Vector3(50, 50, 1F)); + 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); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); + } + + [Fact] + public void TransformRotationDoesNotOffset() + { + Rgba32 background = Color.DimGray.ToPixel(); + Rgba32 marker = Color.Aqua.ToPixel(); + + 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, background); + img2[0, 0] = marker; + + img2.Mutate( + c => + c.Transform(new ProjectiveTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); + + using Image img3 = new(100, 100, background); + img3[0, 0] = marker; + + img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); + + ImageComparer.Exact.VerifySimilarity(img, img2); + ImageComparer.Exact.VerifySimilarity(img, img3); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TransformUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 50, 50]); + + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendRotationDegrees(180); + + image.Mutate(ctx => ctx.Transform(builder)); + + // A 180-degree rotation inverts both axes around the image center. + // The subject location (5, 15) becomes (imageWidth - 5, imageHeight - 15) = (95, 85) + Assert.Equal( + [95, 85], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + // The subject area is also mirrored around the center. + // New X = imageWidth - originalX - width + // New Y = imageHeight - originalY - height + // (5, 15, 50, 50) becomes (45, 35, 50, 50) + Assert.Equal( + [45, 35, 50, 50], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); + } + + private static IResampler GetResampler(string name) + { + PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); + + if (property is null) + { + throw new InvalidOperationException($"No resampler named {name}"); + } + + return (IResampler)property.GetValue(null); + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs index 84d834cc50..e3e7118c0c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -34,8 +34,8 @@ public void CalculateResizeWorkerHeightInWindowBands( [Fact] public void CalculateMinRectangleWhenSourceIsSmallerThanTarget() { - var sourceSize = new Size(200, 100); - var target = new Size(400, 200); + Size sourceSize = new(200, 100); + Size target = new(400, 200); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, @@ -52,11 +52,11 @@ public void CalculateMinRectangleWhenSourceIsSmallerThanTarget() [Fact] public void MaxSizeAndRectangleAreCorrect() { - var sourceSize = new Size(5072, 6761); - var target = new Size(0, 450); + Size sourceSize = new(5072, 6761); + Size target = new(0, 450); - var expectedSize = new Size(338, 450); - var expectedRectangle = new Rectangle(Point.Empty, expectedSize); + Size expectedSize = new(338, 450); + Rectangle expectedRectangle = new(Point.Empty, expectedSize); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, @@ -73,11 +73,11 @@ public void MaxSizeAndRectangleAreCorrect() [Fact] public void CropSizeAndRectangleAreCorrect() { - var sourceSize = new Size(100, 100); - var target = new Size(25, 50); + Size sourceSize = new(100, 100); + Size target = new(25, 50); - var expectedSize = new Size(25, 50); - var expectedRectangle = new Rectangle(-12, 0, 50, 50); + Size expectedSize = new(25, 50); + Rectangle expectedRectangle = new(-12, 0, 50, 50); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, @@ -94,11 +94,11 @@ public void CropSizeAndRectangleAreCorrect() [Fact] public void BoxPadSizeAndRectangleAreCorrect() { - var sourceSize = new Size(100, 100); - var target = new Size(120, 110); + Size sourceSize = new(100, 100); + Size target = new(120, 110); - var expectedSize = new Size(120, 110); - var expectedRectangle = new Rectangle(10, 5, 100, 100); + Size expectedSize = new(120, 110); + Rectangle expectedRectangle = new(10, 5, 100, 100); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, @@ -115,11 +115,11 @@ public void BoxPadSizeAndRectangleAreCorrect() [Fact] public void PadSizeAndRectangleAreCorrect() { - var sourceSize = new Size(100, 100); - var target = new Size(120, 110); + Size sourceSize = new(100, 100); + Size target = new(120, 110); - var expectedSize = new Size(120, 110); - var expectedRectangle = new Rectangle(5, 0, 110, 110); + Size expectedSize = new(120, 110); + Rectangle expectedRectangle = new(5, 0, 110, 110); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, @@ -136,11 +136,11 @@ public void PadSizeAndRectangleAreCorrect() [Fact] public void StretchSizeAndRectangleAreCorrect() { - var sourceSize = new Size(100, 100); - var target = new Size(57, 32); + Size sourceSize = new(100, 100); + Size target = new(57, 32); - var expectedSize = new Size(57, 32); - var expectedRectangle = new Rectangle(Point.Empty, expectedSize); + Size expectedSize = new(57, 32); + Rectangle expectedRectangle = new(Point.Empty, expectedSize); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs index 290a3b37ac..e38bf64c1b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs @@ -16,9 +16,7 @@ internal class ReferenceKernelMap private readonly ReferenceKernel[] kernels; public ReferenceKernelMap(ReferenceKernel[] kernels) - { - this.kernels = kernels; - } + => this.kernels = kernels; public int DestinationSize => this.kernels.Length; @@ -28,18 +26,18 @@ public static ReferenceKernelMap Calculate(in TResampler sampler, in where TResampler : struct, IResampler { double ratio = (double)sourceSize / destinationSize; - double scale = ratio; + double scaleD = ratio; - if (scale < 1F) + if (scaleD < 1) { - scale = 1F; + scaleD = 1; } TolerantMath tolerantMath = TolerantMath.Default; - double radius = tolerantMath.Ceiling(scale * sampler.Radius); + double radius = tolerantMath.Ceiling(scaleD * sampler.Radius); - var result = new List(); + List result = []; for (int i = 0; i < destinationSize; i++) { @@ -58,15 +56,14 @@ public static ReferenceKernelMap Calculate(in TResampler sampler, in right = sourceSize - 1; } - double sum = 0; + float sum = 0; - double[] values = new double[right - left + 1]; + float[] values = new float[right - left + 1]; for (int j = left; j <= right; j++) { - double weight = sampler.GetValue((float)((j - center) / scale)); + float weight = sampler.GetValue((float)((j - center) / scaleD)); sum += weight; - values[j - left] = weight; } @@ -78,16 +75,14 @@ public static ReferenceKernelMap Calculate(in TResampler sampler, in } } - float[] floatVals = values.Select(v => (float)v).ToArray(); - - result.Add(new ReferenceKernel(left, floatVals)); + result.Add(new ReferenceKernel(left, values)); } - return new ReferenceKernelMap(result.ToArray()); + return new ReferenceKernelMap([.. result]); } } - internal struct ReferenceKernel + internal readonly struct ReferenceKernel { public ReferenceKernel(int left, float[] values) { @@ -102,8 +97,6 @@ public ReferenceKernel(int left, float[] values) public int Length => this.Values.Length; public static implicit operator ReferenceKernel(ResizeKernel orig) - { - return new ReferenceKernel(orig.StartIndex, orig.Values.ToArray()); - } + => new(orig.StartIndex, orig.Values.ToArray()); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index c6da46ee2f..5f9a8055ff 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -13,82 +13,78 @@ public partial class ResizeKernelMapTests { private ITestOutputHelper Output { get; } - public ResizeKernelMapTests(ITestOutputHelper output) - { - this.Output = output; - } + public ResizeKernelMapTests(ITestOutputHelper output) => this.Output = output; /// /// resamplerName, srcSize, destSize /// public static readonly TheoryData KernelMapData - = new TheoryData - { - { KnownResamplers.Bicubic, 15, 10 }, - { KnownResamplers.Bicubic, 10, 15 }, - { KnownResamplers.Bicubic, 20, 20 }, - { KnownResamplers.Bicubic, 50, 40 }, - { KnownResamplers.Bicubic, 40, 50 }, - { KnownResamplers.Bicubic, 500, 200 }, - { KnownResamplers.Bicubic, 200, 500 }, - { KnownResamplers.Bicubic, 3032, 400 }, - { KnownResamplers.Bicubic, 10, 25 }, - { KnownResamplers.Lanczos3, 16, 12 }, - { KnownResamplers.Lanczos3, 12, 16 }, - { KnownResamplers.Lanczos3, 12, 9 }, - { KnownResamplers.Lanczos3, 9, 12 }, - { KnownResamplers.Lanczos3, 6, 8 }, - { KnownResamplers.Lanczos3, 8, 6 }, - { KnownResamplers.Lanczos3, 20, 12 }, - { KnownResamplers.Lanczos3, 5, 25 }, - { KnownResamplers.Lanczos3, 5, 50 }, - { KnownResamplers.Lanczos3, 25, 5 }, - { KnownResamplers.Lanczos3, 50, 5 }, - { KnownResamplers.Lanczos3, 49, 5 }, - { KnownResamplers.Lanczos3, 31, 5 }, - { KnownResamplers.Lanczos8, 500, 200 }, - { KnownResamplers.Lanczos8, 100, 10 }, - { KnownResamplers.Lanczos8, 100, 80 }, - { KnownResamplers.Lanczos8, 10, 100 }, - - // Resize_WorksWithAllResamplers_Rgba32_CalliphoraPartial_Box-0.5: - { KnownResamplers.Box, 378, 149 }, - { KnownResamplers.Box, 349, 174 }, - - // Accuracy-related regression-test cases cherry-picked from GeneratedImageResizeData - { KnownResamplers.Box, 201, 100 }, - { KnownResamplers.Box, 199, 99 }, - { KnownResamplers.Box, 10, 299 }, - { KnownResamplers.Box, 299, 10 }, - { KnownResamplers.Box, 301, 300 }, - { KnownResamplers.Box, 1180, 480 }, - { KnownResamplers.Lanczos2, 3264, 3032 }, - { KnownResamplers.Bicubic, 1280, 2240 }, - { KnownResamplers.Bicubic, 1920, 1680 }, - { KnownResamplers.Bicubic, 3072, 2240 }, - { KnownResamplers.Welch, 300, 2008 }, - - // ResizeKernel.Length -related regression tests cherry-picked from GeneratedImageResizeData - { KnownResamplers.Bicubic, 10, 50 }, - { KnownResamplers.Bicubic, 49, 301 }, - { KnownResamplers.Bicubic, 301, 49 }, - { KnownResamplers.Bicubic, 1680, 1200 }, - { KnownResamplers.Box, 13, 299 }, - { KnownResamplers.Lanczos5, 3032, 600 }, - - // Large number. https://github.com/SixLabors/ImageSharp/issues/1616 - { KnownResamplers.Bicubic, 207773, 51943 } - }; - - public static TheoryData GeneratedImageResizeData = - GenerateImageResizeData(); + = new() + { + { KnownResamplers.Bicubic, 15, 10 }, + { KnownResamplers.Bicubic, 10, 15 }, + { KnownResamplers.Bicubic, 20, 20 }, + { KnownResamplers.Bicubic, 50, 40 }, + { KnownResamplers.Bicubic, 40, 50 }, + { KnownResamplers.Bicubic, 500, 200 }, + { KnownResamplers.Bicubic, 200, 500 }, + { KnownResamplers.Bicubic, 3032, 400 }, + { KnownResamplers.Bicubic, 10, 25 }, + { KnownResamplers.Lanczos3, 16, 12 }, + { KnownResamplers.Lanczos3, 12, 16 }, + { KnownResamplers.Lanczos3, 12, 9 }, + { KnownResamplers.Lanczos3, 9, 12 }, + { KnownResamplers.Lanczos3, 6, 8 }, + { KnownResamplers.Lanczos3, 8, 6 }, + { KnownResamplers.Lanczos3, 20, 12 }, + { KnownResamplers.Lanczos3, 5, 25 }, + { KnownResamplers.Lanczos3, 5, 50 }, + { KnownResamplers.Lanczos3, 25, 5 }, + { KnownResamplers.Lanczos3, 50, 5 }, + { KnownResamplers.Lanczos3, 49, 5 }, + { KnownResamplers.Lanczos3, 31, 5 }, + { KnownResamplers.Lanczos8, 500, 200 }, + { KnownResamplers.Lanczos8, 100, 10 }, + { KnownResamplers.Lanczos8, 100, 80 }, + { KnownResamplers.Lanczos8, 10, 100 }, + + // Resize_WorksWithAllResamplers_Rgba32_CalliphoraPartial_Box-0.5: + { KnownResamplers.Box, 378, 149 }, + { KnownResamplers.Box, 349, 174 }, + + // Accuracy-related regression-test cases cherry-picked from GeneratedImageResizeData + { KnownResamplers.Box, 201, 100 }, + { KnownResamplers.Box, 199, 99 }, + { KnownResamplers.Box, 10, 299 }, + { KnownResamplers.Box, 299, 10 }, + { KnownResamplers.Box, 301, 300 }, + { KnownResamplers.Box, 1180, 480 }, + { KnownResamplers.Lanczos2, 3264, 3032 }, + { KnownResamplers.Bicubic, 1280, 2240 }, + { KnownResamplers.Bicubic, 1920, 1680 }, + { KnownResamplers.Bicubic, 3072, 2240 }, + { KnownResamplers.Welch, 300, 2008 }, + + // ResizeKernel.Length -related regression tests cherry-picked from GeneratedImageResizeData + { KnownResamplers.Bicubic, 10, 50 }, + { KnownResamplers.Bicubic, 49, 301 }, + { KnownResamplers.Bicubic, 301, 49 }, + { KnownResamplers.Bicubic, 1680, 1200 }, + { KnownResamplers.Box, 13, 299 }, + { KnownResamplers.Lanczos5, 3032, 600 }, + + // Large number. https://github.com/SixLabors/ImageSharp/issues/1616 + { KnownResamplers.Bicubic, 207773, 51943 } + }; + + public static TheoryData GeneratedImageResizeData = GenerateImageResizeData(); [Theory(Skip = "Only for debugging and development")] [MemberData(nameof(KernelMapData))] public void PrintNonNormalizedKernelMap(TResampler resampler, int srcSize, int destSize) where TResampler : struct, IResampler { - var kernelMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize, false); + ReferenceKernelMap kernelMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize, false); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); } @@ -97,9 +93,7 @@ public void PrintNonNormalizedKernelMap(TResampler resampler, int sr [MemberData(nameof(KernelMapData))] public void KernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize) where TResampler : struct, IResampler - { - this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize); - } + => this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize); // Comprehensive but expensive tests, for ResizeKernelMap. // Enabling them can kill you, but sometimes you have to wear the burden! @@ -116,16 +110,16 @@ public void KernelMapContentIsCorrect_ExtendedGeneratedValues(string resamplerNa private void VerifyKernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize) where TResampler : struct, IResampler { - var referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize); - var kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); + ReferenceKernelMap referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize); + ResizeKernelMap kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); #if DEBUG this.Output.WriteLine(kernelMap.Info); this.Output.WriteLine($"Expected KernelMap:\n{PrintKernelMap(referenceMap)}\n"); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); #endif - var comparer = new ApproximateFloatComparer(1e-6f); + ApproximateFloatComparer comparer = new ApproximateFloatComparer(1e-6f); for (int i = 0; i < kernelMap.DestinationLength; i++) { ResizeKernel kernel = kernelMap.GetKernel((uint)i); @@ -139,7 +133,23 @@ private void VerifyKernelMapContentIsCorrect(TResampler resampler, i referenceKernel.Left == kernel.StartIndex, $"referenceKernel.Left != kernel.Left: {referenceKernel.Left} != {kernel.StartIndex}"); float[] expectedValues = referenceKernel.Values; - Span actualValues = kernel.Values; + Span actualValues; + + if (ResizeKernel.IsHardwareAccelerated) + { + Assert.Equal(expectedValues.Length, kernel.Values.Length / 4); + + actualValues = new float[expectedValues.Length]; + + for (int j = 0; j < expectedValues.Length; j++) + { + actualValues[j] = kernel.Values[j * 4]; + } + } + else + { + actualValues = kernel.Values; + } Assert.Equal(expectedValues.Length, actualValues.Length); @@ -163,7 +173,7 @@ private static string PrintKernelMap( Func getDestinationSize, Func getKernel) { - var bld = new StringBuilder(); + StringBuilder bld = new(); if (kernelMap is ResizeKernelMap actualMap) { @@ -193,19 +203,20 @@ private static string PrintKernelMap( private static TheoryData GenerateImageResizeData() { - var result = new TheoryData(); + TheoryData result = new(); string[] resamplerNames = TestUtils.GetAllResamplerNames(false); int[] dimensionVals = - { - // Arbitrary, small dimensions: - 9, 10, 11, 13, 49, 50, 53, 99, 100, 199, 200, 201, 299, 300, 301, + [ + + // Arbitrary, small dimensions: + 9, 10, 11, 13, 49, 50, 53, 99, 100, 199, 200, 201, 299, 300, 301, - // Typical image sizes: - 640, 480, 800, 600, 1024, 768, 1280, 960, 1536, 1180, 1600, 1200, 2048, 1536, 2240, 1680, 2560, - 1920, 3032, 2008, 3072, 2304, 3264, 2448 - }; + // Typical image sizes: + 640, 480, 800, 600, 1024, 768, 1280, 960, 1536, 1180, 1600, 1200, 2048, 1536, 2240, 1680, 2560, + 1920, 3032, 2008, 3072, 2304, 3264, 2448 + ]; IOrderedEnumerable<(int S, int D)> source2Dest = dimensionVals .SelectMany(s => dimensionVals.Select(d => (s, d))) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index d1b005ee4e..4214cc5e02 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -3,10 +3,13 @@ using System.Numerics; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorProfiles.Companding; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.Memory; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; // ReSharper disable InconsistentNaming @@ -20,15 +23,15 @@ public class ResizeTests public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames(); - public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; + public static readonly string[] CommonTestImages = [TestImages.Png.CalliphoraPartial]; public static readonly string[] SmokeTestResamplerNames = - { + [ nameof(KnownResamplers.NearestNeighbor), nameof(KnownResamplers.Bicubic), nameof(KnownResamplers.Box), nameof(KnownResamplers.Lanczos5), - }; + ]; private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); @@ -78,7 +81,7 @@ public void Resize_BasicSmall(TestImageProvider provider, int wN // resizing: (15, 12) -> (10, 6) // kernel dimensions: (3, 4) using Image image = provider.GetImage(); - Size destSize = new Size(image.Width * wN / wD, image.Height * hN / hD); + Size destSize = new(image.Width * wN / wD, image.Height * hN / hD); image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false)); FormattableString outputInfo = $"({wN}÷{wD},{hN}÷{hD})"; image.DebugSave(provider, outputInfo, appendPixelTypeToFileName: false); @@ -106,7 +109,7 @@ public void WorkingBufferSizeHintInBytes_IsAppliedCorrectly( Configuration configuration = Configuration.CreateDefaultInstance(); int workingBufferSizeHintInBytes = workingBufferLimitInRows * destSize.Width * SizeOfVector4; - TestMemoryAllocator allocator = new TestMemoryAllocator(); + TestMemoryAllocator allocator = new(); allocator.EnableNonThreadSafeLogging(); configuration.MemoryAllocator = allocator; configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes; @@ -211,7 +214,7 @@ public void Resize_PremultiplyAlpha(TestImageProvider provider, provider.RunValidatingProcessorTest( x => { - ResizeOptions resizeOptions = new ResizeOptions() + ResizeOptions resizeOptions = new() { Size = x.GetCurrentSize() / 2, Mode = ResizeMode.Crop, @@ -226,6 +229,72 @@ public void Resize_PremultiplyAlpha(TestImageProvider provider, appendSourceFileOrDescription: false); } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void Resize_AppliesAlphaOptionsForUnassociatedPixels(bool compand, bool premultiplyAlpha) + => AssertResizeAlphaOptions(compand, premultiplyAlpha); + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void Resize_AppliesAlphaOptionsForAssociatedPixels(bool compand, bool premultiplyAlpha) + => AssertResizeAlphaOptions(compand, premultiplyAlpha); + + private static void AssertResizeAlphaOptions(bool compand, bool premultiplyAlpha) + where TPixel : unmanaged, IPixel + { + using Image image = new(2, 1); + image[0, 0] = TPixel.FromUnassociatedScaledVector4(new Vector4(0.9F, 0.2F, 0.1F, 0.8F)); + image[1, 0] = TPixel.FromUnassociatedScaledVector4(new Vector4(0.1F, 0.8F, 0.6F, 0.2F)); + + // Read the stored pixels back through the scalar contract so the oracle includes the source format's actual quantization. + Vector4 expectedVector = image[0, 0].ToUnassociatedScaledVector4(); + Vector4 second = image[1, 0].ToUnassociatedScaledVector4(); + + if (compand) + { + expectedVector = SRgbCompanding.Expand(expectedVector); + second = SRgbCompanding.Expand(second); + } + + if (premultiplyAlpha) + { + Numerics.Premultiply(ref expectedVector); + Numerics.Premultiply(ref second); + } + + expectedVector = (expectedVector + second) / 2F; + + if (premultiplyAlpha) + { + Numerics.UnPremultiply(ref expectedVector); + } + + if (compand) + { + expectedVector = SRgbCompanding.Compress(expectedVector); + } + + TPixel expected = TPixel.FromUnassociatedScaledVector4(expectedVector); + ResizeOptions options = new() + { + Size = new Size(1, 1), + Mode = ResizeMode.Stretch, + Sampler = KnownResamplers.Box, + Compand = compand, + PremultiplyAlpha = premultiplyAlpha + }; + + image.Mutate(context => context.Resize(options)); + + Assert.Equal(expected, image[0, 0]); + } + [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void Resize_IsAppliedToAllFrames(TestImageProvider provider) @@ -242,9 +311,7 @@ public void Resize_IsAppliedToAllFrames(TestImageProvider provid [WithTestPatternImages(50, 50, CommonNonDefaultPixelTypes)] public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : unmanaged, IPixel - { - provider.RunValidatingProcessorTest(x => x.Resize(x.GetCurrentSize() / 2), comparer: ValidatorComparer); - } + => provider.RunValidatingProcessorTest(x => x.Resize(x.GetCurrentSize() / 2), comparer: ValidatorComparer); [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] @@ -257,7 +324,7 @@ public void Resize_ThrowsForWrappedMemoryImage(TestImageProvider using Image image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height); Assert.ThrowsAny( - () => { image1.Mutate(x => x.Resize(image0.Width / 2, image0.Height / 2, true)); }); + () => image1.Mutate(x => x.Resize(image0.Width / 2, image0.Height / 2, true))); } [Theory] @@ -365,12 +432,12 @@ public void ResizeFromSourceRectangle(TestImageProvider provider where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - Rectangle sourceRectangle = new Rectangle( + Rectangle sourceRectangle = new( image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); - Rectangle destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + Rectangle destRectangle = new(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); image.Mutate( x => x.Resize( @@ -437,7 +504,7 @@ public void ResizeWithBoxPadMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(image.Width + 200, image.Height + 200), Mode = ResizeMode.BoxPad, @@ -456,7 +523,7 @@ public void ResizeWithCropHeightMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions { Size = new Size(image.Width, image.Height / 2) }; + ResizeOptions options = new() { Size = new Size(image.Width, image.Height / 2) }; image.Mutate(x => x.Resize(options)); @@ -470,7 +537,7 @@ public void ResizeWithCropWidthMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions { Size = new Size(image.Width / 2, image.Height) }; + ResizeOptions options = new() { Size = new Size(image.Width / 2, image.Height) }; image.Mutate(x => x.Resize(options)); @@ -484,7 +551,7 @@ public void CanResizeLargeImageWithCropMode(TestImageProvider pr where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(480, 600), Mode = ResizeMode.Crop @@ -502,7 +569,7 @@ public void ResizeWithMaxMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions { Size = new Size(300, 300), Mode = ResizeMode.Max }; + ResizeOptions options = new() { Size = new Size(300, 300), Mode = ResizeMode.Max }; image.Mutate(x => x.Resize(options)); @@ -516,7 +583,7 @@ public void ResizeWithMinMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions + ResizeOptions options = new() { Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), Mode = ResizeMode.Min @@ -534,7 +601,7 @@ public void ResizeWithPadMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(image.Width + 200, image.Height), Mode = ResizeMode.Pad, @@ -553,7 +620,7 @@ public void ResizeWithStretchMode(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - ResizeOptions options = new ResizeOptions + ResizeOptions options = new() { Size = new Size(image.Width / 2, image.Height), Mode = ResizeMode.Stretch @@ -579,6 +646,7 @@ public void CanResizeExifIssueImages(TestImageProvider provider) } using Image image = provider.GetImage(); + // Don't bother saving, we're testing the EXIF metadata updates. image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); } @@ -586,8 +654,8 @@ public void CanResizeExifIssueImages(TestImageProvider provider) [Fact] public void Issue1195() { - using Image image = new Image(2, 300); - Size size = new Size(50, 50); + using Image image = new(2, 300); + Size size = new(50, 50); image.Mutate(x => x .Resize( new ResizeOptions @@ -605,8 +673,8 @@ public void Issue1195() [InlineData(3, 7)] public void Issue1342(int width, int height) { - using Image image = new Image(1, 1); - Size size = new Size(width, height); + using Image image = new(1, 1); + Size size = new(width, height); image.Mutate(x => x .Resize( new ResizeOptions @@ -630,4 +698,42 @@ public void Issue1625_LimitedAllocator(TestImageProvider provide appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } + + [Theory] + [InlineData(1024)] + [InlineData(2048)] + public void Issue3156_CanResizeLargeSquareToSinglePixel(int length) + { + Rgba32 white = Color.White.ToPixel(); + using Image image = new(length, length, white); + + image.Mutate(x => x.Resize(1, 1)); + + Assert.Equal(new Size(1, 1), image.Size); + Assert.Equal(white, image[0, 0]); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void ResizeUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 20, 20]); + + image.Mutate(ctx => ctx.Resize(new Size(image.Width / 2, image.Height / 2))); + + // The transform operates in pixel space, so the resulting values correspond to the + // scaled pixel centers, producing whole-number coordinates after resizing. + Assert.Equal( + [2, 7], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + Assert.Equal( + [2, 7, 10, 10], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); + } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs index 55d26974aa..523fd2bdd1 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; [Trait("Category", "Processors")] public class RotateFlipTests { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + public static readonly string[] FlipFiles = [TestImages.Bmp.F]; public static readonly TheoryData RotateFlipValues - = new TheoryData - { + = new() + { { RotateMode.None, FlipMode.Vertical }, { RotateMode.None, FlipMode.Horizontal }, { RotateMode.Rotate90, FlipMode.None }, diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs index b150da1d8e..5eee7313d9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; @@ -11,35 +13,59 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; public class RotateTests { public static readonly TheoryData RotateAngles - = new TheoryData - { - 50, -50, 170, -170 - }; + = new() + { + 50, -50, 170, -170 + }; public static readonly TheoryData RotateEnumValues - = new TheoryData - { - RotateMode.None, - RotateMode.Rotate90, - RotateMode.Rotate180, - RotateMode.Rotate270 - }; + = new() + { + RotateMode.None, + RotateMode.Rotate90, + RotateMode.Rotate180, + RotateMode.Rotate270 + }; [Theory] [WithTestPatternImages(nameof(RotateAngles), 100, 50, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(RotateAngles), 50, 100, PixelTypes.Rgba32)] public void Rotate_WithAngle(TestImageProvider provider, float value) where TPixel : unmanaged, IPixel - { - provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); - } + => provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); [Theory] [WithTestPatternImages(nameof(RotateEnumValues), 100, 50, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(RotateEnumValues), 50, 100, PixelTypes.Rgba32)] public void Rotate_WithRotateTypeEnum(TestImageProvider provider, RotateMode value) where TPixel : unmanaged, IPixel + => provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void RotateUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 50, 50]); + + image.Mutate(ctx => ctx.Rotate(180)); + + // A 180-degree rotation inverts both axes around the image center. + // The subject location (5, 15) becomes (imageWidth - 5, imageHeight - 15) = (95, 85) + Assert.Equal( + [95, 85], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + // The subject area is also mirrored around the center. + // New X = imageWidth - originalX - width + // New Y = imageHeight - originalY - height + // (5, 15, 50, 50) becomes (45, 35, 50, 50) + Assert.Equal( + [45, 35, 50, 50], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs index 1e217ae24a..84401e8468 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTests.cs @@ -14,9 +14,9 @@ public class SkewTests { private const PixelTypes CommonPixelTypes = PixelTypes.Bgra32 | PixelTypes.Rgb24; - public static readonly string[] ResamplerNames = new[] - { - nameof(KnownResamplers.Bicubic), + public static readonly string[] ResamplerNames = + [ + nameof(KnownResamplers.Bicubic), nameof(KnownResamplers.Box), nameof(KnownResamplers.CatmullRom), nameof(KnownResamplers.Hermite), @@ -30,11 +30,11 @@ public class SkewTests nameof(KnownResamplers.RobidouxSharp), nameof(KnownResamplers.Spline), nameof(KnownResamplers.Triangle), - nameof(KnownResamplers.Welch), - }; + nameof(KnownResamplers.Welch) + ]; - public static readonly TheoryData SkewValues = new TheoryData - { + public static readonly TheoryData SkewValues = new() + { { 20, 10 }, { -20, -10 } }; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs index de02502e2b..33e22d3644 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; @@ -12,17 +14,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; [GroupOutput("Transforms")] public class SwizzleTests { - private struct InvertXAndYSwizzler : ISwizzler + private readonly struct SwapXAndYSwizzler : ISwizzler { - public InvertXAndYSwizzler(Size sourceSize) - { - this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width); - } + public SwapXAndYSwizzler(Size sourceSize) + => this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width); public Size DestinationSize { get; } - public Point Transform(Point point) - => new Point(point.Y, point.X); + public Point Transform(Point point) => new(point.Y, point.X); } [Theory] @@ -35,15 +34,15 @@ public void InvertXAndYSwizzle(TestImageProvider provider) using Image expectedImage = provider.GetImage(); using Image image = provider.GetImage(); - image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height)))); + image.Mutate(ctx => ctx.Swizzle(new SwapXAndYSwizzler(new Size(image.Width, image.Height)))); image.DebugSave( provider, - nameof(InvertXAndYSwizzler), + nameof(SwapXAndYSwizzler), appendPixelTypeToFileName: false, appendSourceFileOrDescription: true); - image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height)))); + image.Mutate(ctx => ctx.Swizzle(new SwapXAndYSwizzler(new Size(image.Width, image.Height)))); image.DebugSave( provider, @@ -53,4 +52,26 @@ public void InvertXAndYSwizzle(TestImageProvider provider) ImageComparer.Exact.VerifySimilarity(expectedImage, image); } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void SwizzleUpdatesSubject(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Metadata.ExifProfile = new(); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectLocation, [5, 15]); + image.Metadata.ExifProfile.SetValue(ExifTag.SubjectArea, [5, 15, 20, 20]); + + image.Mutate(ctx => ctx.Swizzle(new SwapXAndYSwizzler(new Size(image.Width, image.Height)))); + + Assert.Equal( + [15, 5], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); + + Assert.Equal( + [15, 5, 20, 20], + image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); + } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs index 4b1ca92dd6..d0e4cf5663 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs @@ -8,8 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms; public class AffineTransformBuilderTests : TransformBuilderTestBase { - protected override AffineTransformBuilder CreateBuilder() - => new AffineTransformBuilder(); + protected override AffineTransformBuilder CreateBuilder() => new(); protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees) => builder.AppendRotationDegrees(degrees); @@ -59,6 +58,9 @@ protected override void PrependSkewRadians(AffineTransformBuilder builder, float protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); + protected override void ClearBuilder(AffineTransformBuilder builder) + => builder.Clear(); + protected override Vector2 Execute( AffineTransformBuilder builder, Rectangle rectangle, diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 30c89ec339..c56df3152b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -25,7 +25,7 @@ public void CropWidthHeightCropProcessorWithRectangleSet(int width, int height) [InlineData(12, 123, 6, 2)] public void CropRectangleCropProcessorWithRectangleSet(int x, int y, int width, int height) { - var cropRectangle = new Rectangle(x, y, width, height); + Rectangle cropRectangle = new(x, y, width, height); this.operations.Crop(cropRectangle); CropProcessor processor = this.Verify(); @@ -35,7 +35,7 @@ public void CropRectangleCropProcessorWithRectangleSet(int x, int y, int width, [Fact] public void CropRectangleWithInvalidBoundsThrowsException() { - var cropRectangle = Rectangle.Inflate(this.SourceBounds(), 5, 5); + Rectangle cropRectangle = Rectangle.Inflate(this.SourceBounds(), 5, 5); Assert.Throws(() => this.operations.Crop(cropRectangle)); } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs index 7d0c0dc58f..dcc6533163 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms; [Trait("Category", "Processors")] public class ProjectiveTransformBuilderTests : TransformBuilderTestBase { - protected override ProjectiveTransformBuilder CreateBuilder() - => new ProjectiveTransformBuilder(); + protected override ProjectiveTransformBuilder CreateBuilder() => new(); protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees) => builder.AppendRotationDegrees(degrees); @@ -56,6 +55,9 @@ protected override void PrependTranslation(ProjectiveTransformBuilder builder, P protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) => builder.PrependRotationRadians(radians, origin); + protected override void ClearBuilder(ProjectiveTransformBuilder builder) + => builder.Clear(); + protected override Vector2 Execute( ProjectiveTransformBuilder builder, Rectangle rectangle, diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs deleted file mode 100644 index 128df01a06..0000000000 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Reflection; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using Xunit.Abstractions; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Processing.Transforms; - -[Trait("Category", "Processors")] -public class ProjectiveTransformTests -{ - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.03f, 3); - private static readonly ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.5f, 3); - - private ITestOutputHelper Output { get; } - - public static readonly TheoryData ResamplerNames = new TheoryData - { - nameof(KnownResamplers.Bicubic), - nameof(KnownResamplers.Box), - nameof(KnownResamplers.CatmullRom), - nameof(KnownResamplers.Hermite), - nameof(KnownResamplers.Lanczos2), - nameof(KnownResamplers.Lanczos3), - nameof(KnownResamplers.Lanczos5), - nameof(KnownResamplers.Lanczos8), - nameof(KnownResamplers.MitchellNetravali), - nameof(KnownResamplers.NearestNeighbor), - nameof(KnownResamplers.Robidoux), - nameof(KnownResamplers.RobidouxSharp), - nameof(KnownResamplers.Spline), - nameof(KnownResamplers.Triangle), - nameof(KnownResamplers.Welch), - }; - - public static readonly TheoryData TaperMatrixData = new TheoryData - { - { TaperSide.Bottom, TaperCorner.Both }, - { TaperSide.Bottom, TaperCorner.LeftOrTop }, - { TaperSide.Bottom, TaperCorner.RightOrBottom }, - { TaperSide.Top, TaperCorner.Both }, - { TaperSide.Top, TaperCorner.LeftOrTop }, - { TaperSide.Top, TaperCorner.RightOrBottom }, - { TaperSide.Left, TaperCorner.Both }, - { TaperSide.Left, TaperCorner.LeftOrTop }, - { TaperSide.Left, TaperCorner.RightOrBottom }, - { TaperSide.Right, TaperCorner.Both }, - { TaperSide.Right, TaperCorner.LeftOrTop }, - { TaperSide.Right, TaperCorner.RightOrBottom }, - }; - - public ProjectiveTransformTests(ITestOutputHelper output) => this.Output = output; - - [Theory] - [WithTestPatternImages(nameof(ResamplerNames), 150, 150, PixelTypes.Rgba32)] - public void Transform_WithSampler(TestImageProvider provider, string resamplerName) - where TPixel : unmanaged, IPixel - { - IResampler sampler = GetResampler(resamplerName); - using (Image image = provider.GetImage()) - { - ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() - .AppendTaper(TaperSide.Right, TaperCorner.Both, .5F); - - image.Mutate(i => i.Transform(builder, sampler)); - - image.DebugSave(provider, resamplerName); - image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); - } - } - - [Theory] - [WithSolidFilledImages(nameof(TaperMatrixData), 30, 30, nameof(Color.Red), PixelTypes.Rgba32)] - public void Transform_WithTaperMatrix(TestImageProvider provider, TaperSide taperSide, TaperCorner taperCorner) - where TPixel : unmanaged, IPixel - { - using (Image image = provider.GetImage()) - { - ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() - .AppendTaper(taperSide, taperCorner, .5F); - - image.Mutate(i => i.Transform(builder)); - - FormattableString testOutputDetails = $"{taperSide}-{taperCorner}"; - image.DebugSave(provider, testOutputDetails); - image.CompareFirstFrameToReferenceOutput(TolerantComparer, provider, testOutputDetails); - } - } - - [Theory] - [WithSolidFilledImages(100, 100, 0, 0, 255, PixelTypes.Rgba32)] - public void RawTransformMatchesDocumentedExample(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - // Printing some extra output to help investigating rounding errors: - this.Output.WriteLine($"Vector.IsHardwareAccelerated: {Vector.IsHardwareAccelerated}"); - - // This test matches the output described in the example at - // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/non-affine - using (Image image = provider.GetImage()) - { - Matrix4x4 matrix = Matrix4x4.Identity; - matrix.M14 = 0.01F; - - ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() - .AppendMatrix(matrix); - - image.Mutate(i => i.Transform(builder)); - - image.DebugSave(provider); - image.CompareToReferenceOutput(TolerantComparer, provider); - } - } - - [Theory] - [WithSolidFilledImages(290, 154, 0, 0, 255, PixelTypes.Rgba32)] - public void PerspectiveTransformMatchesCSS(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - // https://jsfiddle.net/dFrHS/545/ - // https://github.com/SixLabors/ImageSharp/issues/787 - using (Image image = provider.GetImage()) - { -#pragma warning disable SA1117 // Parameters should be on same line or separate lines - Matrix4x4 matrix = new( - 0.260987f, -0.434909f, 0, -0.0022184f, - 0.373196f, 0.949882f, 0, -0.000312129f, - 0, 0, 1, 0, - 52, 165, 0, 1); -#pragma warning restore SA1117 // Parameters should be on same line or separate lines - - ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() - .AppendMatrix(matrix); - - image.Mutate(i => i.Transform(builder)); - - image.DebugSave(provider); - image.CompareToReferenceOutput(TolerantComparer, provider); - } - } - - [Fact] - public void Issue1911() - { - using var image = new Image(100, 100); - image.Mutate(x => x = x.Transform(new Rectangle(0, 0, 99, 100), Matrix4x4.Identity, new Size(99, 100), KnownResamplers.Lanczos2)); - - Assert.Equal(99, image.Width); - Assert.Equal(100, image.Height); - } - - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void Identity(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - - Matrix4x4 m = Matrix4x4.Identity; - Rectangle r = new(25, 25, 50, 50); - image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); - } - - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0.0001F)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 57F)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0F)] - public void Transform_With_Custom_Dimensions(TestImageProvider provider, float radians) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - - Matrix4x4 m = Matrix4x4.CreateRotationX(radians, new Vector3(50, 50, 1F)) * Matrix4x4.CreateRotationY(radians, new Vector3(50, 50, 1F)); - 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); - image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); - } - - [Fact] - public void TransformRotationDoesNotOffset() - { - Rgba32 marker = Color.Aqua; - - using Image img = new(100, 100, Color.DimGray); - img[0, 0] = marker; - - img.Mutate(c => c.Rotate(180)); - - Assert.Equal(marker, img[99, 99]); - - using Image img2 = new(100, 100, Color.DimGray); - img2[0, 0] = marker; - - img2.Mutate( - c => - c.Transform(new ProjectiveTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - - using Image img3 = new(100, 100, Color.DimGray); - img3[0, 0] = marker; - - img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); - - ImageComparer.Exact.VerifySimilarity(img, img2); - ImageComparer.Exact.VerifySimilarity(img, img3); - } - - private static IResampler GetResampler(string name) - { - PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); - - if (property is null) - { - throw new Exception($"No resampler named {name}"); - } - - return (IResampler)property.GetValue(null); - } -} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index f6c93ffd0e..3f2d8e0593 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -64,7 +64,7 @@ public void ResizeWithOptions() bool compand = true; ResizeMode mode = ResizeMode.Stretch; - var resizeOptions = new ResizeOptions + ResizeOptions resizeOptions = new() { Size = new Size(width, height), Sampler = sampler, @@ -93,7 +93,7 @@ public void HwIntrinsics_Resize() { static void RunTest() { - using var image = new Image(50, 50); + using Image image = new(50, 50); image.Mutate(img => img.Resize(25, 25)); Assert.Equal(25, image.Width); @@ -102,6 +102,6 @@ static void RunTest() FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableFMA); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs index 432bb5cacb..8475ce5abb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -18,8 +18,7 @@ public InvertXAndYSwizzler(Size sourceSize) public Size DestinationSize { get; } - public Point Transform(Point point) - => new Point(point.Y, point.X); + public Point Transform(Point point) => new(point.Y, point.X); } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index f046c2503c..12f8326fc7 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms; [Trait("Category", "Processors")] public abstract class TransformBuilderTestBase { - private static readonly ApproximateFloatComparer Comparer = new ApproximateFloatComparer(1e-6f); + private static readonly ApproximateFloatComparer Comparer = new(1e-6f); public static readonly TheoryData ScaleTranslate_Data = - new TheoryData - { + new() + { // scale, translate, source, expectedDest { Vector2.One, Vector2.Zero, Vector2.Zero, Vector2.Zero }, { Vector2.One, Vector2.Zero, new Vector2(10, 20), new Vector2(10, 20) }, @@ -29,7 +29,7 @@ public void _1Scale_2Translate(Vector2 scale, Vector2 translate, Vector2 source, #pragma warning restore SA1300 // Element should begin with upper-case letter { // These operations should be size-agnostic: - var size = new Size(123, 321); + Size size = new(123, 321); TBuilder builder = this.CreateBuilder(); this.AppendScale(builder, new SizeF(scale)); @@ -40,8 +40,8 @@ public void _1Scale_2Translate(Vector2 scale, Vector2 translate, Vector2 source, } public static readonly TheoryData TranslateScale_Data = - new TheoryData - { + new() + { // translate, scale, source, expectedDest { Vector2.Zero, Vector2.One, Vector2.Zero, Vector2.Zero }, { Vector2.Zero, Vector2.One, new Vector2(10, 20), new Vector2(10, 20) }, @@ -55,7 +55,7 @@ public void _1Translate_2Scale(Vector2 translate, Vector2 scale, Vector2 source, #pragma warning restore SA1300 // Element should begin with upper-case letter { // Translate ans scale are size-agnostic: - var size = new Size(456, 432); + Size size = new(456, 432); TBuilder builder = this.CreateBuilder(); this.AppendTranslation(builder, translate); @@ -70,7 +70,7 @@ public void _1Translate_2Scale(Vector2 translate, Vector2 scale, Vector2 source, [InlineData(-20, 10)] public void LocationOffsetIsPrepended(int locationX, int locationY) { - var rectangle = new Rectangle(locationX, locationY, 10, 10); + Rectangle rectangle = new(locationX, locationY, 10, 10); TBuilder builder = this.CreateBuilder(); this.AppendScale(builder, new SizeF(2, 2)); @@ -92,16 +92,16 @@ public void AppendRotationDegrees_WithoutSpecificRotationCenter_RotationIsCenter float x, float y) { - var size = new Size(width, height); + Size size = new(width, height); TBuilder builder = this.CreateBuilder(); this.AppendRotationDegrees(builder, degrees); // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness - Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size, TransformSpace.Pixel); + Matrix3x2 matrix = TransformUtilities.CreateRotationTransformMatrixDegrees(degrees, size); - var position = new Vector2(x, y); - var expected = Vector2.Transform(position, matrix); + Vector2 position = new(x, y); + Vector2 expected = Vector2.Transform(position, matrix); Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); Assert.Equal(actual, expected, Comparer); @@ -120,16 +120,16 @@ public void AppendRotationDegrees_WithRotationCenter( float x, float y) { - var size = new Size(width, height); + Size size = new(width, height); TBuilder builder = this.CreateBuilder(); - var centerPoint = new Vector2(cx, cy); + Vector2 centerPoint = new(cx, cy); this.AppendRotationDegrees(builder, degrees, centerPoint); - var matrix = Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); + Matrix3x2 matrix = Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); - var position = new Vector2(x, y); - var expected = Vector2.Transform(position, matrix); + Vector2 position = new(x, y); + Vector2 expected = Vector2.Transform(position, matrix); Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); Assert.Equal(actual, expected, Comparer); @@ -147,15 +147,15 @@ public void AppendSkewDegrees_WithoutSpecificSkewCenter_SkewIsCenteredAroundImag float x, float y) { - var size = new Size(width, height); + Size size = new(width, height); TBuilder builder = this.CreateBuilder(); this.AppendSkewDegrees(builder, degreesX, degreesY); - Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size, TransformSpace.Pixel); + Matrix3x2 matrix = TransformUtilities.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size); - var position = new Vector2(x, y); - var expected = Vector2.Transform(position, matrix); + Vector2 position = new(x, y); + Vector2 expected = Vector2.Transform(position, matrix); Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); Assert.Equal(actual, expected, Comparer); } @@ -174,16 +174,16 @@ public void AppendSkewDegrees_WithSkewCenter( float x, float y) { - var size = new Size(width, height); + Size size = new(width, height); TBuilder builder = this.CreateBuilder(); - var centerPoint = new Vector2(cx, cy); + Vector2 centerPoint = new(cx, cy); this.AppendSkewDegrees(builder, degreesX, degreesY, centerPoint); - var matrix = Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); + Matrix3x2 matrix = Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); - var position = new Vector2(x, y); - var expected = Vector2.Transform(position, matrix); + Vector2 position = new(x, y); + Vector2 expected = Vector2.Transform(position, matrix); Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); Assert.Equal(actual, expected, Comparer); @@ -192,7 +192,7 @@ public void AppendSkewDegrees_WithSkewCenter( [Fact] public void AppendPrependOpposite() { - var rectangle = new Rectangle(-1, -1, 3, 3); + Rectangle rectangle = new(-1, -1, 3, 3); TBuilder b1 = this.CreateBuilder(); TBuilder b2 = this.CreateBuilder(); @@ -226,7 +226,7 @@ public void AppendPrependOpposite() [InlineData(-1, 0)] public void ThrowsForInvalidSizes(int width, int height) { - var size = new Size(width, height); + Size size = new(width, height); Assert.ThrowsAny( () => @@ -248,6 +248,48 @@ public void ThrowsForInvalidMatrix() }); } + [Fact] + public void Clear_ResetsBuilderToIdentity() + { + Size size = new(100, 100); + Rectangle rectangle = new(Point.Empty, size); + Vector2 source = new(10, 20); + + TBuilder builder = this.CreateBuilder(); + + // Apply a transform that changes the point. + this.AppendScale(builder, new SizeF(2, 3)); + this.AppendTranslation(builder, new PointF(50, 50)); + Vector2 transformed = this.Execute(builder, rectangle, source); + Assert.NotEqual(source, transformed, Comparer); + + // Clear and verify the builder produces identity behavior. + this.ClearBuilder(builder); + Vector2 afterClear = this.Execute(builder, rectangle, source); + Assert.Equal(source, afterClear, Comparer); + } + + [Fact] + public void Clear_AllowsReuse() + { + Size size = new(100, 100); + Rectangle rectangle = new(Point.Empty, size); + Vector2 source = new(10, 20); + + TBuilder builder = this.CreateBuilder(); + + // First transform: scale by 2. + this.AppendScale(builder, new SizeF(2, 2)); + Vector2 scaled = this.Execute(builder, rectangle, source); + Assert.Equal(new Vector2(20, 40), scaled, Comparer); + + // Clear and apply a different transform: translate. + this.ClearBuilder(builder); + this.AppendTranslation(builder, new PointF(5, 10)); + Vector2 translated = this.Execute(builder, rectangle, source); + Assert.Equal(new Vector2(15, 30), translated, Comparer); + } + protected abstract TBuilder CreateBuilder(); protected abstract void AppendRotationDegrees(TBuilder builder, float degrees); @@ -282,5 +324,7 @@ public void ThrowsForInvalidMatrix() protected abstract void PrependTranslation(TBuilder builder, PointF translate); + protected abstract void ClearBuilder(TBuilder builder); + protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs deleted file mode 100644 index 21b92a01e8..0000000000 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.ImageSharp.Tests.TestUtilities; - -namespace SixLabors.ImageSharp.Tests.Processing.Transforms; - -[Trait("Category", "Processors")] -public class TransformsHelpersTest -{ - [Fact] - public void HelperCanChangeExifDataType() - { - int xy = 1; - - using (var img = new Image(xy, xy)) - { - var profile = new ExifProfile(); - img.Metadata.ExifProfile = profile; - profile.SetValue(ExifTag.PixelXDimension, xy + ushort.MaxValue); - profile.SetValue(ExifTag.PixelYDimension, xy + ushort.MaxValue); - - Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); - Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); - - TransformProcessorHelpers.UpdateDimensionalMetadata(img); - - Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelXDimension).DataType); - Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelYDimension).DataType); - } - } -} diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs index a1d7e358b6..209dd361ec 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs @@ -18,7 +18,7 @@ public LoadResizeSaveProfilingBenchmarks(ITestOutputHelper output) [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] public void LoadResizeSave(string imagePath) { - var configuration = Configuration.CreateDefaultInstance(); + Configuration configuration = Configuration.CreateDefaultInstance(); configuration.MaxDegreeOfParallelism = 1; DecoderOptions options = new() @@ -28,12 +28,12 @@ public void LoadResizeSave(string imagePath) byte[] imageBytes = TestFile.Create(imagePath).Bytes; - using var ms = new MemoryStream(); + using MemoryStream ms = new(); this.Measure( 30, () => { - using (var image = Image.Load(options, imageBytes)) + using (Image image = Image.Load(options, imageBytes)) { image.Mutate(x => x.Resize(image.Size / 4)); image.SaveAsJpeg(ms); diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs index f20ca8ce18..bab11b0f9d 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs @@ -28,7 +28,7 @@ public void ResizeBicubic(int width, int height) this.ExecutionCount, () => { - using (var image = new Image(this.configuration, width, height)) + using (Image image = new(this.configuration, width, height)) { image.Mutate(x => x.Resize(width / 5, height / 5)); } diff --git a/tests/ImageSharp.Tests/Quantization/AlphaAssociationQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/AlphaAssociationQuantizerTests.cs new file mode 100644 index 0000000000..ddad67b949 --- /dev/null +++ b/tests/ImageSharp.Tests/Quantization/AlphaAssociationQuantizerTests.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Tests.Quantization; + +public class AlphaAssociationQuantizerTests +{ + public static TheoryData Dithers { get; } = new() + { + null, + KnownDitherings.Bayer4x4, + KnownDitherings.FloydSteinberg + }; + + [Theory] + [MemberData(nameof(Dithers))] + public void WuQuantizerProducesEquivalentResultsForAssociatedPixels(IDither? dither) + { + byte[] alphaValues = [0, 1, 63, 64, 65, 127, 128, 254, 255]; + Rgba32[] source = new Rgba32[alphaValues.Length * 4]; + + for (int i = 0; i < source.Length; i++) + { + byte alpha = alphaValues[i % alphaValues.Length]; + source[i] = alpha == 0 ? default : new Rgba32(255, 255, 255, alpha); + } + + QuantizerOptions options = new() + { + Dither = dither, + MaxColors = 8, + TransparencyThreshold = .5F + }; + + Rgba32[] expected = Quantize(source, options); + Rgba32[] actual = Quantize(source, options); + + Assert.Equal(expected, actual); + } + + [Fact] + public void PaletteQuantizerAppliesTransparencyThresholdToAssociatedPixels() + { + Configuration configuration = Configuration.Default; + Rgba32P source = new(127, 0, 0, 127); + Color[] palette = [Color.Transparent, Color.FromPixel(source)]; + QuantizerOptions options = new() { Dither = null, TransparencyThreshold = .5F }; + PaletteQuantizer quantizer = new(palette, options); + + using Image image = new(configuration, 1, 1, source); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(configuration); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds); + + Assert.Equal(0, result.DangerousGetRowSpan(0)[0]); + Assert.Equal(default, result.Palette.Span[0]); + } + + [Theory] + [InlineData(64 / 255F, (byte)63, (byte)64)] + [InlineData(.5F, (byte)127, (byte)128)] + [InlineData(1F, (byte)254, (byte)255)] + public void WuQuantizerAppliesNormalizedTransparencyThresholdToAssociatedPixels(float threshold, byte below, byte retained) + { + Rgba32[] source = + [ + new Rgba32(255, 255, 255, below), + new Rgba32(255, 255, 255, retained) + ]; + + QuantizerOptions options = new() + { + Dither = null, + MaxColors = 2, + TransparencyThreshold = threshold + }; + + Rgba32[] expected = Quantize(source, options); + Rgba32[] actual = Quantize(source, options); + + Assert.Equal(expected, actual); + Assert.Equal(default, actual[0]); + Assert.NotEqual(0, actual[1].A); + } + + private static Rgba32[] Quantize(Rgba32[] source, QuantizerOptions options) + where TPixel : unmanaged, IPixel + { + Configuration configuration = Configuration.Default; + using Image image = new(configuration, source.Length, 1); + + for (int x = 0; x < source.Length; x++) + { + image[x, 0] = TPixel.FromRgba32(source[x]); + } + + WuQuantizer quantizer = new(options); + ImageFrame frame = image.Frames.RootFrame; + + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(configuration); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); + + ReadOnlySpan palette = result.Palette.Span; + ReadOnlySpan indices = result.DangerousGetRowSpan(0); + Rgba32[] colors = new Rgba32[source.Length]; + + for (int x = 0; x < colors.Length; x++) + { + colors[x] = palette[indices[x]].ToRgba32(); + } + + return colors; + } +} diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index 59a0ad4274..e940886520 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -13,14 +13,14 @@ public class QuantizedImageTests [Fact] public void QuantizersDitherByDefault() { - var werner = new WernerPaletteQuantizer(); - var webSafe = new WebSafePaletteQuantizer(); - var octree = new OctreeQuantizer(); - var wu = new WuQuantizer(); + WernerPaletteQuantizer werner = new(); + WebSafePaletteQuantizer webSafe = new(); + HexadecatreeQuantizer hexadecatree = new(); + WuQuantizer wu = new(); Assert.NotNull(werner.Options.Dither); Assert.NotNull(webSafe.Options.Dither); - Assert.NotNull(octree.Options.Dither); + Assert.NotNull(hexadecatree.Options.Dither); Assert.NotNull(wu.Options.Dither); using (IQuantizer quantizer = werner.CreatePixelSpecificQuantizer(this.Configuration)) @@ -33,7 +33,7 @@ public void QuantizersDitherByDefault() Assert.NotNull(quantizer.Options.Dither); } - using (IQuantizer quantizer = octree.CreatePixelSpecificQuantizer(this.Configuration)) + using (IQuantizer quantizer = hexadecatree.CreatePixelSpecificQuantizer(this.Configuration)) { Assert.NotNull(quantizer.Options.Dither); } @@ -47,32 +47,27 @@ public void QuantizersDitherByDefault() [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] - public void OctreeQuantizerYieldsCorrectTransparentPixel( + public void HexadecatreeQuantizerYieldsCorrectTransparentPixel( TestImageProvider provider, bool dither) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - Assert.True(image[0, 0].Equals(default)); + using Image image = provider.GetImage(); - var options = new QuantizerOptions(); - if (!dither) - { - options.Dither = null; - } + QuantizerOptions options = new(); + if (!dither) + { + options.Dither = null; + } - var quantizer = new OctreeQuantizer(options); + HexadecatreeQuantizer quantizer = new(options); - foreach (ImageFrame frame in image.Frames) - { - using (IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration)) - using (IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds())) - { - int index = this.GetTransparentIndex(quantized); - Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); - } - } + foreach (ImageFrame frame in image.Frames) + { + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); + int index = this.GetTransparentIndex(quantized); + Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); } } @@ -82,27 +77,22 @@ public void OctreeQuantizerYieldsCorrectTransparentPixel( public void WuQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - Assert.True(image[0, 0].Equals(default)); + using Image image = provider.GetImage(); - var options = new QuantizerOptions(); - if (!dither) - { - options.Dither = null; - } + QuantizerOptions options = new(); + if (!dither) + { + options.Dither = null; + } - var quantizer = new WuQuantizer(options); + WuQuantizer quantizer = new(options); - foreach (ImageFrame frame in image.Frames) - { - using (IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration)) - using (IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds())) - { - int index = this.GetTransparentIndex(quantized); - Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); - } - } + foreach (ImageFrame frame in image.Frames) + { + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); + int index = this.GetTransparentIndex(quantized); + Assert.Equal(index, quantized.DangerousGetRowSpan(0)[0]); } } @@ -112,13 +102,11 @@ public void WuQuantizerYieldsCorrectTransparentPixel(TestImageProvider(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using (Image image = provider.GetImage()) - { - var octreeQuantizer = new OctreeQuantizer(); - IQuantizer quantizer = octreeQuantizer.CreatePixelSpecificQuantizer(Configuration.Default, new QuantizerOptions() { MaxColors = 128 }); - ImageFrame frame = image.Frames[0]; - quantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); - } + using Image image = provider.GetImage(); + HexadecatreeQuantizer hexadecatreeQuantizer = new(); + IQuantizer quantizer = hexadecatreeQuantizer.CreatePixelSpecificQuantizer(Configuration.Default, new QuantizerOptions { MaxColors = 128 }); + ImageFrame frame = image.Frames[0]; + quantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); } private int GetTransparentIndex(IndexedImageFrame quantized) diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index d6b1f1f980..28a7c49e51 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -12,19 +12,19 @@ public class WuQuantizerTests public void SinglePixelOpaque() { Configuration config = Configuration.Default; - var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); + WuQuantizer quantizer = new(new QuantizerOptions { Dither = null }); - using var image = new Image(config, 1, 1, Color.Black); + using Image image = new(config, 1, 1, Color.Black.ToPixel()); ImageFrame frame = image.Frames.RootFrame; using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); - using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); Assert.Equal(1, result.Palette.Length); 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]); } @@ -32,13 +32,13 @@ public void SinglePixelOpaque() public void SinglePixelTransparent() { Configuration config = Configuration.Default; - var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); + WuQuantizer quantizer = new(new QuantizerOptions { Dither = null }); - using var image = new Image(config, 1, 1, default(Rgba32)); + using Image image = new(config, 1, 1, default(Rgba32)); ImageFrame frame = image.Frames.RootFrame; using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); - using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); Assert.Equal(1, result.Palette.Length); Assert.Equal(1, result.Width); @@ -66,7 +66,7 @@ public void SinglePixelTransparent() [Fact] public void Palette256() { - using var image = new Image(1, 256); + using Image image = new(1, 256); for (int i = 0; i < 256; i++) { @@ -79,18 +79,18 @@ public void Palette256() } Configuration config = Configuration.Default; - var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); + WuQuantizer quantizer = new(new QuantizerOptions { Dither = null, TransparencyThreshold = 0 }); ImageFrame frame = image.Frames.RootFrame; using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); - using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); Assert.Equal(256, result.Palette.Length); Assert.Equal(1, result.Width); Assert.Equal(256, result.Height); - using var actualImage = new Image(1, 256); + using Image actualImage = new(1, 256); actualImage.ProcessPixelRows(accessor => { @@ -123,72 +123,68 @@ public void LowVariance(TestImageProvider provider) where TPixel : unmanaged, IPixel { // See https://github.com/SixLabors/ImageSharp/issues/866 - using (Image image = provider.GetImage()) - { - Configuration config = Configuration.Default; - var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); - ImageFrame frame = image.Frames.RootFrame; + using Image image = provider.GetImage(); + Configuration config = Configuration.Default; + WuQuantizer quantizer = new(new QuantizerOptions { Dither = null }); + ImageFrame frame = image.Frames.RootFrame; - using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); - using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config); + using IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds); - Assert.Equal(48, result.Palette.Length); - } + Assert.Equal(48, result.Palette.Length); } private static void TestScale(Func pixelBuilder) { - using (var image = new Image(1, 256)) - using (var expectedImage = new Image(1, 256)) - using (var actualImage = new Image(1, 256)) + using Image image = new(1, 256); + using Image expectedImage = new(1, 256); + using Image actualImage = new(1, 256); + for (int i = 0; i < 256; i++) { - for (int i = 0; i < 256; i++) - { - byte c = (byte)i; - image[0, i] = pixelBuilder.Invoke(c); - } + byte c = (byte)i; + image[0, i] = pixelBuilder.Invoke(c); + } - for (int i = 0; i < 256; i++) - { - byte c = (byte)((i & ~7) + 4); - expectedImage[0, i] = pixelBuilder.Invoke(c); - } + for (int i = 0; i < 256; i++) + { + byte c = (byte)((i & ~7) + 4); + expectedImage[0, i] = pixelBuilder.Invoke(c); + } - Configuration config = Configuration.Default; - var quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }); + Configuration config = Configuration.Default; + WuQuantizer quantizer = new(new QuantizerOptions { Dither = null, TransparencyThreshold = 0 }); - ImageFrame frame = image.Frames.RootFrame; - using (IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config)) - using (IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds())) - { - Assert.Equal(4 * 8, result.Palette.Length); - Assert.Equal(1, result.Width); - Assert.Equal(256, result.Height); + ImageFrame frame = image.Frames.RootFrame; + using (IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(config)) + using (IndexedImageFrame result = frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds)) + { + Assert.Equal(4 * 8, result.Palette.Length); + Assert.Equal(1, result.Width); + Assert.Equal(256, result.Height); - actualImage.ProcessPixelRows(accessor => + actualImage.ProcessPixelRows(accessor => + { + ReadOnlySpan paletteSpan = result.Palette.Span; + int paletteCount = paletteSpan.Length - 1; + for (int y = 0; y < accessor.Height; y++) { - ReadOnlySpan paletteSpan = result.Palette.Span; - int paletteCount = paletteSpan.Length - 1; - for (int y = 0; y < accessor.Height; y++) - { - Span row = accessor.GetRowSpan(y); - ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); + Span row = accessor.GetRowSpan(y); + ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); - for (int x = 0; x < accessor.Width; x++) - { - row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; - } + for (int x = 0; x < accessor.Width; x++) + { + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; } - }); - } - - expectedImage.ProcessPixelRows(actualImage, static (expectedAccessor, actualAccessor) => - { - for (int y = 0; y < expectedAccessor.Height; y++) - { - Assert.True(expectedAccessor.GetRowSpan(y).SequenceEqual(actualAccessor.GetRowSpan(y))); } }); } + + expectedImage.ProcessPixelRows(actualImage, static (expectedAccessor, actualAccessor) => + { + for (int y = 0; y < expectedAccessor.Height; y++) + { + Assert.True(expectedAccessor.GetRowSpan(y).SequenceEqual(actualAccessor.GetRowSpan(y))); + } + }); } } diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs new file mode 100644 index 0000000000..397986a189 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataClut.cs @@ -0,0 +1,162 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataClut +{ + internal static IccClut Clut3x2 = new( + [ + 0.1f, 0.1f, + 0.2f, 0.2f, + 0.3f, 0.3f, + + 0.11f, 0.11f, + 0.21f, 0.21f, + 0.31f, 0.31f, + + 0.12f, 0.12f, + 0.22f, 0.22f, + 0.32f, 0.32f, + + 0.13f, 0.13f, + 0.23f, 0.23f, + 0.33f, 0.33f, + + 0.14f, 0.14f, + 0.24f, 0.24f, + 0.34f, 0.34f, + + 0.15f, 0.15f, + 0.25f, 0.25f, + 0.35f, 0.35f, + + 0.16f, 0.16f, + 0.26f, 0.26f, + 0.36f, 0.36f, + + 0.17f, 0.17f, + 0.27f, 0.27f, + 0.37f, 0.37f, + + 0.18f, 0.18f, + 0.28f, 0.28f, + 0.38f, 0.38f, + ], + [3, 3, 3], + IccClutDataType.Float, + outputChannelCount: 2); + + internal static IccClut Clut3x1 = new( + [ + 0.10f, + 0.20f, + 0.30f, + + 0.11f, + 0.21f, + 0.31f, + + 0.12f, + 0.22f, + 0.32f, + + 0.13f, + 0.23f, + 0.33f, + + 0.14f, + 0.24f, + 0.34f, + + 0.15f, + 0.25f, + 0.35f, + + 0.16f, + 0.26f, + 0.36f, + + 0.17f, + 0.27f, + 0.37f, + + 0.18f, + 0.28f, + 0.38f, + ], + [3, 3, 3], + IccClutDataType.Float, + outputChannelCount: 1); + + internal static IccClut Clut2x2 = new( + [ + 0.1f, 0.9f, + 0.2f, 0.8f, + 0.3f, 0.7f, + + 0.4f, 0.6f, + 0.5f, 0.5f, + 0.6f, 0.4f, + + 0.7f, 0.3f, + 0.8f, 0.2f, + 0.9f, 0.1f, + ], + [3, 3], + IccClutDataType.Float, + outputChannelCount: 2); + + internal static IccClut Clut2x1 = new( + [ + 0.1f, + 0.2f, + 0.3f, + + 0.4f, + 0.5f, + 0.6f, + + 0.7f, + 0.8f, + 0.9f, + ], + [3, 3], + IccClutDataType.Float, + outputChannelCount: 1); + + internal static IccClut Clut1x2 = new( + [ + 0f, 0.5f, + 0.25f, 0.75f, + 0.5f, 1f, + ], + [3], + IccClutDataType.Float, + outputChannelCount: 2); + + internal static IccClut Clut1x1 = new( + [ + 0f, + 0.5f, + 1f, + ], + [3], + IccClutDataType.Float, + outputChannelCount: 1); + + public static object[][] ClutConversionTestData = + [ + [Clut3x2, new Vector4(0.75f, 0.75f, 0.75f, 0), new Vector4(0.31f, 0.31f, 0, 0)], + [Clut3x1, new Vector4(0.2f, 0.6f, 0.8f, 0), new Vector4(0.284f, 0, 0, 0)], + [Clut3x1, new Vector4(0.75f, 0.75f, 0.75f, 0), new Vector4(0.31f, 0, 0, 0)], + [Clut2x2, new Vector4(0.2f, 0.6f, 0, 0), new Vector4(0.34f, 0.66f, 0, 0)], + [Clut2x2, new Vector4(0.25f, 0.75f, 0, 0), new Vector4(0.4f, 0.6f, 0, 0)], + [Clut2x1, new Vector4(0.25f, 0.75f, 0, 0), new Vector4(0.4f, 0, 0, 0)], + [Clut1x2, new Vector4(0.25f, 0, 0, 0), new Vector4(0.125f, 0.625f, 0, 0)], + [Clut1x1, new Vector4(0.25f, 0, 0, 0), new Vector4(0.25f, 0, 0, 0)], + ]; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLut.cs new file mode 100644 index 0000000000..e3bc3bba6e --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLut.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataLut +{ + private static readonly float[] LutEven = [0, 0.5f, 1]; + + private static readonly float[] LutUneven = [0, 0.7f, 1]; + + public static object[][] LutConversionTestData = + [ + [LutEven, false, 0.5f, 0.5f], + [LutEven, false, 0.25f, 0.25f], + [LutEven, false, 0.75f, 0.75f], + + [LutEven, true, 0.5f, 0.5f], + [LutEven, true, 0.25f, 0.25f], + [LutEven, true, 0.75f, 0.75f], + + [LutUneven, false, 0.1, 0.14], + [LutUneven, false, 0.5, 0.7], + [LutUneven, false, 0.75, 0.85], + + [LutUneven, true, 0.14, 0.1], + [LutUneven, true, 0.7, 0.5], + [LutUneven, true, 0.85, 0.75] + ]; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutAB.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutAB.cs new file mode 100644 index 0000000000..f38bc68d10 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutAB.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataLutAB +{ + private static readonly IccLutAToBTagDataEntry LutAtoBSingleCurve = new( + [ + IccConversionDataTrc.IdentityCurve, + IccConversionDataTrc.IdentityCurve, + IccConversionDataTrc.IdentityCurve + ], + null, + null, + null, + null, + null); + + // also need: + // # CurveM + matrix + // # CurveA + CLUT + CurveB + // # CurveA + CLUT + CurveM + Matrix + CurveB + private static readonly IccLutBToATagDataEntry LutBtoASingleCurve = new( + [ + IccConversionDataTrc.IdentityCurve, + IccConversionDataTrc.IdentityCurve, + IccConversionDataTrc.IdentityCurve + ], + null, + null, + null, + null, + null); + + public static object[][] LutAToBConversionTestData = + [ + [LutAtoBSingleCurve, new Vector4(0.2f, 0.3f, 0.4f, 0), new Vector4(0.2f, 0.3f, 0.4f, 0)] + ]; + + public static object[][] LutBToAConversionTestData = + [ + [LutBtoASingleCurve, new Vector4(0.2f, 0.3f, 0.4f, 0), new Vector4(0.2f, 0.3f, 0.4f, 0)] + ]; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutEntry.cs new file mode 100644 index 0000000000..40f54ea74c --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataLutEntry.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataLutEntry +{ + private static readonly IccLut Lut256 = CreateLut(256); + private static readonly IccLut Lut32 = CreateLut(32); + private static readonly IccLut LutIdentity = CreateIdentityLut(0, 1); + + // test cases were originally calculated unaware that + // IccConversionDataMatrix.Matrix3x3Random is actually the row-major transposed matrix + // of the typical column-major matrix + public static float[,] TestMatrix = + { + { 0.1f, 0.4f, 0.7f }, + { 0.2f, 0.5f, 0.8f }, + { 0.3f, 0.6f, 0.9f } + }; + + private static readonly IccLut8TagDataEntry Lut8 = new( + [Lut256, Lut256], + IccConversionDataClut.Clut2x1, + [Lut256]); + + private static readonly IccLut16TagDataEntry Lut16 = new( + [Lut32, Lut32], + IccConversionDataClut.Clut2x1, + [LutIdentity]); + + private static readonly IccLut8TagDataEntry Lut8Matrix = new( + TestMatrix, + [Lut256, Lut256, Lut256], + IccConversionDataClut.Clut3x1, + [Lut256]); + + private static readonly IccLut16TagDataEntry Lut16Matrix = new( + TestMatrix, + [Lut32, Lut32, Lut32], + IccConversionDataClut.Clut3x1, + [LutIdentity]); + + private static IccLut CreateLut(int length) + { + float[] values = new float[length]; + for (int i = 0; i < values.Length; i++) + { + values[i] = 0.1f + (i / (float)length); + } + + return new IccLut(values); + } + + private static IccLut CreateIdentityLut(float min, float max) => new([min, max]); + + public static object[][] Lut8ConversionTestData = + [ + [Lut8, new Vector4(0.2f, 0.3f, 0, 0), new Vector4(0.4578933760499877f, 0, 0, 0)], + [Lut8Matrix, new Vector4(0.21f, 0.31f, 0.41f, 0), new Vector4(0.40099657491875312f, 0, 0, 0)] + ]; + + public static object[][] Lut16ConversionTestData = + [ + [Lut16, new Vector4(0.2f, 0.3f, 0, 0), new Vector4(0.3543750030529918f, 0, 0, 0)], + [Lut16Matrix, new Vector4(0.21f, 0.31f, 0.41f, 0), new Vector4(0.29739562389689594f, 0, 0, 0)] + ]; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMatrix.cs new file mode 100644 index 0000000000..5f14174616 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMatrix.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataMatrix +{ + private static readonly Vector3 Vector3Zero = new(0.0f, 0.0f, 0.0f); + + public static float[,] Matrix3x3Random = + { + { 0.1f, 0.2f, 0.3f }, + { 0.4f, 0.5f, 0.6f }, + { 0.7f, 0.8f, 0.9f } + }; + + public static float[,] Matrix3x3Identity = + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; + + public static object[][] MatrixConversionTestData = + [ + [CreateMatrix(Matrix3x3Identity), Vector3Zero, new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.5f, 0.5f, 0.5f, 0) + ], + [CreateMatrix(Matrix3x3Identity), new Vector3(0.2f, 0.2f, 0.2f), new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.7f, 0.7f, 0.7f, 0) + ], + [CreateMatrix(Matrix3x3Random), Vector3Zero, new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.6f, 0.75f, 0.9f, 0) + ], + [CreateMatrix(Matrix3x3Random), new Vector3(0.1f, 0.2f, 0.3f), new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.7f, 0.95f, 1.2f, 0) + ], + [CreateMatrix(Matrix3x3Random), Vector3Zero, new Vector4(0.2f, 0.4f, 0.7f, 0), new Vector4(0.67f, 0.8f, 0.93f, 0) + ], + [CreateMatrix(Matrix3x3Random), new Vector3(0.1f, 0.2f, 0.3f), new Vector4(0.2f, 0.4f, 0.7f, 0), new Vector4(0.77f, 1, 1.23f, 0) + ] + ]; + + private static Matrix4x4 CreateMatrix(float[,] matrix) => new( + matrix[0, 0], + matrix[0, 1], + matrix[0, 2], + 0, + matrix[1, 0], + matrix[1, 1], + matrix[1, 2], + 0, + matrix[2, 0], + matrix[2, 1], + matrix[2, 2], + 0, + 0, + 0, + 0, + 1); +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs new file mode 100644 index 0000000000..f79666e3e3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataMultiProcessElement.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public class IccConversionDataMultiProcessElement +{ + private static readonly IccMatrixProcessElement Matrix = new( + new float[,] + { + { 2, 4, 6 }, + { 3, 5, 7 }, + }, + new float[] { 3, 4, 5 }); + + private static readonly IccClut Clut = new( + new[] + { + 0.2f, 0.3f, + 0.4f, 0.2f, + + 0.21f, 0.31f, + 0.41f, 0.51f, + + 0.22f, 0.32f, + 0.42f, 0.52f, + + 0.23f, 0.33f, + 0.43f, 0.53f, + }, + new byte[] { 2, 2, 2 }, + IccClutDataType.Float, + outputChannelCount: 2); + + private static readonly IccFormulaCurveElement FormulaCurveElement1 = new(IccFormulaCurveType.Type1, 2.2f, 0.7f, 0.2f, 0.3f, 0, 0); + private static readonly IccFormulaCurveElement FormulaCurveElement2 = new(IccFormulaCurveType.Type2, 2.2f, 0.9f, 0.9f, 0.02f, 0.1f, 0); + private static readonly IccFormulaCurveElement FormulaCurveElement3 = new(IccFormulaCurveType.Type3, 0, 0.9f, 0.9f, 1.02f, 0.1f, 0.02f); + + private static readonly IccCurveSetProcessElement CurveSet1DFormula1 = Create1DSingleCurveSet(FormulaCurveElement1); + private static readonly IccCurveSetProcessElement CurveSet1DFormula2 = Create1DSingleCurveSet(FormulaCurveElement2); + private static readonly IccCurveSetProcessElement CurveSet1DFormula3 = Create1DSingleCurveSet(FormulaCurveElement3); + + private static readonly IccCurveSetProcessElement CurveSet1DFormula1And2 = Create1DMultiCurveSet(new[] { 0.5f }, FormulaCurveElement1, FormulaCurveElement2); + + private static readonly IccClutProcessElement ClutElement = new(Clut); + + private static IccCurveSetProcessElement Create1DSingleCurveSet(IccCurveSegment segment) + { + IccOneDimensionalCurve curve = new(new float[0], new[] { segment }); + return new IccCurveSetProcessElement(new[] { curve }); + } + + private static IccCurveSetProcessElement Create1DMultiCurveSet(float[] breakPoints, params IccCurveSegment[] segments) + { + IccOneDimensionalCurve curve = new(breakPoints, segments); + return new IccCurveSetProcessElement(new[] { curve }); + } + + public static object[][] MpeCurveConversionTestData = + { + new object[] { CurveSet1DFormula1, new[] { 0.51f }, new[] { 0.575982451f } }, + new object[] { CurveSet1DFormula2, new[] { 0.52f }, new[] { -0.4684991f } }, + new object[] { CurveSet1DFormula3, new[] { 0.53f }, new[] { 0.86126f } }, + + new object[] { CurveSet1DFormula1And2, new[] { 0.31f }, new[] { 0.445982f } }, + new object[] { CurveSet1DFormula1And2, new[] { 0.61f }, new[] { -0.341274023f } }, + }; + + public static object[][] MpeMatrixConversionTestData = + { + new object[] { Matrix, new float[] { 2, 4 }, new float[] { 19, 32, 45 } } + }; + + public static object[][] MpeClutConversionTestData = + { + new object[] { ClutElement, new[] { 0.5f, 0.5f, 0.5f }, new[] { 0.5f, 0.5f } } + }; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataTrc.cs b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataTrc.cs new file mode 100644 index 0000000000..d0ba39e4c7 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionDataTrc.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests.TestDataIcc.Conversion; + +public static class IccConversionDataTrc +{ + internal static IccCurveTagDataEntry IdentityCurve = new(); + internal static IccCurveTagDataEntry Gamma2Curve = new(2); + internal static IccCurveTagDataEntry LutCurve = new([0, 0.7f, 1]); + + internal static IccParametricCurveTagDataEntry ParamCurve1 = new(new IccParametricCurve(2.2f)); + internal static IccParametricCurveTagDataEntry ParamCurve2 = new(new IccParametricCurve(2.2f, 1.5f, -0.5f)); + internal static IccParametricCurveTagDataEntry ParamCurve3 = new(new IccParametricCurve(2.2f, 1.5f, -0.5f, 0.3f)); + internal static IccParametricCurveTagDataEntry ParamCurve4 = new(new IccParametricCurve(2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)); + internal static IccParametricCurveTagDataEntry ParamCurve5 = new(new IccParametricCurve(2.2f, 0.7f, 0.2f, 0.3f, 0.1f, 0.5f, 0.2f)); + + public static object[][] TrcArrayConversionTestData { get; } = + [ + [ + new IccTagDataEntry[] { IdentityCurve, Gamma2Curve, ParamCurve1 }, + false, + new Vector4(2, 2, 0.5f, 0), + new Vector4(2, 4, 0.217637628f, 0) + ], + [ + new IccTagDataEntry[] { IdentityCurve, Gamma2Curve, ParamCurve1 }, + true, + new Vector4(1, 4, 0.217637628f, 0), + new Vector4(1, 2, 0.5f, 0) + ] + ]; + + public static object[][] CurveConversionTestData { get; } = + [ + [IdentityCurve, false, 2, 2], + [Gamma2Curve, false, 2, 4], + [LutCurve, false, 0.1, 0.14], + [LutCurve, false, 0.5, 0.7], + [LutCurve, false, 0.75, 0.85], + + [IdentityCurve, true, 2, 2], + [Gamma2Curve, true, 4, 2], + [LutCurve, true, 0.14, 0.1], + [LutCurve, true, 0.7, 0.5], + [LutCurve, true, 0.85, 0.75] + ]; + + public static object[][] ParametricCurveConversionTestData { get; } = + [ + [ParamCurve1, false, 0.5f, 0.217637628f], + [ParamCurve2, false, 0.6f, 0.133208528f], + [ParamCurve2, false, 0.21f, 0], + [ParamCurve3, false, 0.61f, 0.444446117f], + [ParamCurve3, false, 0.22f, 0.3f], + [ParamCurve4, false, 0.3f, 0.0732389539f], + [ParamCurve4, false, 0.03f, 0.00232198136f], + [ParamCurve5, false, 0.2f, 0.593165159f], + [ParamCurve5, false, 0.05f, 0.215f], + + [ParamCurve1, true, 0.217637628f, 0.5f], + [ParamCurve2, true, 0.133208528f, 0.6f], + [ParamCurve2, true, 0, 1 / 3f], + [ParamCurve3, true, 0.444446117f, 0.61f], + [ParamCurve3, true, 0.3f, 1 / 3f], + [ParamCurve4, true, 0.0732389539f, 0.3f], + [ParamCurve4, true, 0.00232198136f, 0.03f], + [ParamCurve5, true, 0.593165159f, 0.2f], + [ParamCurve5, true, 0.215f, 0.05f] + ]; +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs index 8288a294dd..3799641810 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs @@ -1,109 +1,109 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataArray { - public static readonly byte[] UInt8 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + public static readonly byte[] UInt8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; public static readonly object[][] UInt8TestData = - { - new object[] { UInt8, UInt8 } - }; - - public static readonly ushort[] UInt16_Val = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public static readonly byte[] UInt16_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_0, - IccTestDataPrimitives.UInt16_1, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_4, - IccTestDataPrimitives.UInt16_5, - IccTestDataPrimitives.UInt16_6, - IccTestDataPrimitives.UInt16_7, - IccTestDataPrimitives.UInt16_8, - IccTestDataPrimitives.UInt16_9); + [ + [UInt8, UInt8] + ]; + + public static readonly ushort[] UInt16Val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + public static readonly byte[] UInt16Arr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt160, + IccTestDataPrimitives.UInt161, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt164, + IccTestDataPrimitives.UInt165, + IccTestDataPrimitives.UInt166, + IccTestDataPrimitives.UInt167, + IccTestDataPrimitives.UInt168, + IccTestDataPrimitives.UInt169); public static readonly object[][] UInt16TestData = - { - new object[] { UInt16_Arr, UInt16_Val } - }; - - public static readonly short[] Int16_Val = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public static readonly byte[] Int16_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.Int16_0, - IccTestDataPrimitives.Int16_1, - IccTestDataPrimitives.Int16_2, - IccTestDataPrimitives.Int16_3, - IccTestDataPrimitives.Int16_4, - IccTestDataPrimitives.Int16_5, - IccTestDataPrimitives.Int16_6, - IccTestDataPrimitives.Int16_7, - IccTestDataPrimitives.Int16_8, - IccTestDataPrimitives.Int16_9); + [ + [UInt16Arr, UInt16Val] + ]; + + public static readonly short[] Int16Val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + public static readonly byte[] Int16Arr = ArrayHelper.Concat( + IccTestDataPrimitives.Int160, + IccTestDataPrimitives.Int161, + IccTestDataPrimitives.Int162, + IccTestDataPrimitives.Int163, + IccTestDataPrimitives.Int164, + IccTestDataPrimitives.Int165, + IccTestDataPrimitives.Int166, + IccTestDataPrimitives.Int167, + IccTestDataPrimitives.Int168, + IccTestDataPrimitives.Int169); public static readonly object[][] Int16TestData = - { - new object[] { Int16_Arr, Int16_Val } - }; - - public static readonly uint[] UInt32_Val = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public static readonly byte[] UInt32_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_0, - IccTestDataPrimitives.UInt32_1, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UInt32_3, - IccTestDataPrimitives.UInt32_4, - IccTestDataPrimitives.UInt32_5, - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.UInt32_7, - IccTestDataPrimitives.UInt32_8, - IccTestDataPrimitives.UInt32_9); + [ + [Int16Arr, Int16Val] + ]; + + public static readonly uint[] UInt32Val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + public static readonly byte[] UInt32Arr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt320, + IccTestDataPrimitives.UInt321, + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UInt323, + IccTestDataPrimitives.UInt324, + IccTestDataPrimitives.UInt325, + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.UInt327, + IccTestDataPrimitives.UInt328, + IccTestDataPrimitives.UInt329); public static readonly object[][] UInt32TestData = - { - new object[] { UInt32_Arr, UInt32_Val } - }; - - public static readonly int[] Int32_Val = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public static readonly byte[] Int32_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.Int32_0, - IccTestDataPrimitives.Int32_1, - IccTestDataPrimitives.Int32_2, - IccTestDataPrimitives.Int32_3, - IccTestDataPrimitives.Int32_4, - IccTestDataPrimitives.Int32_5, - IccTestDataPrimitives.Int32_6, - IccTestDataPrimitives.Int32_7, - IccTestDataPrimitives.Int32_8, - IccTestDataPrimitives.Int32_9); + [ + [UInt32Arr, UInt32Val] + ]; + + public static readonly int[] Int32Val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + public static readonly byte[] Int32Arr = ArrayHelper.Concat( + IccTestDataPrimitives.Int320, + IccTestDataPrimitives.Int321, + IccTestDataPrimitives.Int322, + IccTestDataPrimitives.Int323, + IccTestDataPrimitives.Int324, + IccTestDataPrimitives.Int325, + IccTestDataPrimitives.Int326, + IccTestDataPrimitives.Int327, + IccTestDataPrimitives.Int328, + IccTestDataPrimitives.Int329); public static readonly object[][] Int32TestData = - { - new object[] { Int32_Arr, Int32_Val } - }; - - public static readonly ulong[] UInt64_Val = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public static readonly byte[] UInt64_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt64_0, - IccTestDataPrimitives.UInt64_1, - IccTestDataPrimitives.UInt64_2, - IccTestDataPrimitives.UInt64_3, - IccTestDataPrimitives.UInt64_4, - IccTestDataPrimitives.UInt64_5, - IccTestDataPrimitives.UInt64_6, - IccTestDataPrimitives.UInt64_7, - IccTestDataPrimitives.UInt64_8, - IccTestDataPrimitives.UInt64_9); + [ + [Int32Arr, Int32Val] + ]; + + public static readonly ulong[] UInt64Val = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + public static readonly byte[] UInt64Arr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt640, + IccTestDataPrimitives.UInt641, + IccTestDataPrimitives.UInt642, + IccTestDataPrimitives.UInt643, + IccTestDataPrimitives.UInt644, + IccTestDataPrimitives.UInt645, + IccTestDataPrimitives.UInt646, + IccTestDataPrimitives.UInt647, + IccTestDataPrimitives.UInt648, + IccTestDataPrimitives.UInt649); public static readonly object[][] UInt64TestData = - { - new object[] { UInt64_Arr, UInt64_Val } - }; + [ + [UInt64Arr, UInt64Val] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs index f76d2ba036..be0d077b6a 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs @@ -3,309 +3,307 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataCurves { /// /// Channels: 3 /// - public static readonly IccResponseCurve Response_ValGrad = new IccResponseCurve( + public static readonly IccResponseCurve ResponseValGrad = new( IccCurveMeasurementEncodings.StatusA, - new[] - { - IccTestDataNonPrimitives.XyzNumber_ValVar1, - IccTestDataNonPrimitives.XyzNumber_ValVar2, - IccTestDataNonPrimitives.XyzNumber_ValVar3 - }, - new IccResponseNumber[][] - { - new IccResponseNumber[] { IccTestDataNonPrimitives.ResponseNumber_Val1, IccTestDataNonPrimitives.ResponseNumber_Val2 }, - new IccResponseNumber[] { IccTestDataNonPrimitives.ResponseNumber_Val3, IccTestDataNonPrimitives.ResponseNumber_Val4 }, - new IccResponseNumber[] { IccTestDataNonPrimitives.ResponseNumber_Val5, IccTestDataNonPrimitives.ResponseNumber_Val6 }, - }); + [ + IccTestDataNonPrimitives.XyzNumberValVar1, + IccTestDataNonPrimitives.XyzNumberValVar2, + IccTestDataNonPrimitives.XyzNumberValVar3 + ], + [ + [IccTestDataNonPrimitives.ResponseNumberVal1, IccTestDataNonPrimitives.ResponseNumberVal2], + [IccTestDataNonPrimitives.ResponseNumberVal3, IccTestDataNonPrimitives.ResponseNumberVal4], + [IccTestDataNonPrimitives.ResponseNumberVal5, IccTestDataNonPrimitives.ResponseNumberVal6] + ]); /// /// Channels: 3 /// - public static readonly byte[] Response_Grad = ArrayHelper.Concat( + public static readonly byte[] ResponseGrad = ArrayHelper.Concat( new byte[] { 0x53, 0x74, 0x61, 0x41 }, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UInt32_2, - IccTestDataNonPrimitives.XyzNumber_Var1, - IccTestDataNonPrimitives.XyzNumber_Var2, - IccTestDataNonPrimitives.XyzNumber_Var3, - IccTestDataNonPrimitives.ResponseNumber_1, - IccTestDataNonPrimitives.ResponseNumber_2, - IccTestDataNonPrimitives.ResponseNumber_3, - IccTestDataNonPrimitives.ResponseNumber_4, - IccTestDataNonPrimitives.ResponseNumber_5, - IccTestDataNonPrimitives.ResponseNumber_6); + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UInt322, + IccTestDataNonPrimitives.XyzNumberVar1, + IccTestDataNonPrimitives.XyzNumberVar2, + IccTestDataNonPrimitives.XyzNumberVar3, + IccTestDataNonPrimitives.ResponseNumber1, + IccTestDataNonPrimitives.ResponseNumber2, + IccTestDataNonPrimitives.ResponseNumber3, + IccTestDataNonPrimitives.ResponseNumber4, + IccTestDataNonPrimitives.ResponseNumber5, + IccTestDataNonPrimitives.ResponseNumber6); public static readonly object[][] ResponseCurveTestData = - { - new object[] { Response_Grad, Response_ValGrad, 3 }, - }; + [ + [ResponseGrad, ResponseValGrad, 3] + ]; - public static readonly IccParametricCurve Parametric_ValVar1 = new IccParametricCurve(1); - public static readonly IccParametricCurve Parametric_ValVar2 = new IccParametricCurve(1, 2, 3); - public static readonly IccParametricCurve Parametric_ValVar3 = new IccParametricCurve(1, 2, 3, 4); - public static readonly IccParametricCurve Parametric_ValVar4 = new IccParametricCurve(1, 2, 3, 4, 5); - public static readonly IccParametricCurve Parametric_ValVar5 = new IccParametricCurve(1, 2, 3, 4, 5, 6, 7); + public static readonly IccParametricCurve ParametricValVar1 = new(1); + public static readonly IccParametricCurve ParametricValVar2 = new(1, 2, 3); + public static readonly IccParametricCurve ParametricValVar3 = new(1, 2, 3, 4); + public static readonly IccParametricCurve ParametricValVar4 = new(1, 2, 3, 4, 5); + public static readonly IccParametricCurve ParametricValVar5 = new(1, 2, 3, 4, 5, 6, 7); - public static readonly byte[] Parametric_Var1 = ArrayHelper.Concat( + public static readonly byte[] ParametricVar1 = ArrayHelper.Concat( new byte[] { 0x00, 0x00, 0x00, 0x00, }, - IccTestDataPrimitives.Fix16_1); + IccTestDataPrimitives.Fix161); - public static readonly byte[] Parametric_Var2 = ArrayHelper.Concat( + public static readonly byte[] ParametricVar2 = ArrayHelper.Concat( new byte[] { 0x00, 0x01, 0x00, 0x00, }, - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_3); + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix163); - public static readonly byte[] Parametric_Var3 = ArrayHelper.Concat( + public static readonly byte[] ParametricVar3 = ArrayHelper.Concat( new byte[] { 0x00, 0x02, 0x00, 0x00, }, - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_3, - IccTestDataPrimitives.Fix16_4); + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix163, + IccTestDataPrimitives.Fix164); - public static readonly byte[] Parametric_Var4 = ArrayHelper.Concat( + public static readonly byte[] ParametricVar4 = ArrayHelper.Concat( new byte[] { 0x00, 0x03, 0x00, 0x00, }, - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_3, - IccTestDataPrimitives.Fix16_4, - IccTestDataPrimitives.Fix16_5); + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix163, + IccTestDataPrimitives.Fix164, + IccTestDataPrimitives.Fix165); - public static readonly byte[] Parametric_Var5 = ArrayHelper.Concat( + public static readonly byte[] ParametricVar5 = ArrayHelper.Concat( new byte[] { 0x00, 0x04, 0x00, 0x00, }, - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_3, - IccTestDataPrimitives.Fix16_4, - IccTestDataPrimitives.Fix16_5, - IccTestDataPrimitives.Fix16_6, - IccTestDataPrimitives.Fix16_7); + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix163, + IccTestDataPrimitives.Fix164, + IccTestDataPrimitives.Fix165, + IccTestDataPrimitives.Fix166, + IccTestDataPrimitives.Fix167); public static readonly object[][] ParametricCurveTestData = - { - new object[] { Parametric_Var1, Parametric_ValVar1 }, - new object[] { Parametric_Var2, Parametric_ValVar2 }, - new object[] { Parametric_Var3, Parametric_ValVar3 }, - new object[] { Parametric_Var4, Parametric_ValVar4 }, - new object[] { Parametric_Var5, Parametric_ValVar5 }, - }; + [ + [ParametricVar1, ParametricValVar1], + [ParametricVar2, ParametricValVar2], + [ParametricVar3, ParametricValVar3], + [ParametricVar4, ParametricValVar4], + [ParametricVar5, ParametricValVar5] + ]; // Formula Segment - public static readonly IccFormulaCurveElement Formula_ValVar1 = new IccFormulaCurveElement(IccFormulaCurveType.Type1, 1, 2, 3, 4, 0, 0); - public static readonly IccFormulaCurveElement Formula_ValVar2 = new IccFormulaCurveElement(IccFormulaCurveType.Type2, 1, 2, 3, 4, 5, 0); - public static readonly IccFormulaCurveElement Formula_ValVar3 = new IccFormulaCurveElement(IccFormulaCurveType.Type3, 0, 2, 3, 4, 5, 6); + public static readonly IccFormulaCurveElement FormulaValVar1 = new(IccFormulaCurveType.Type1, 1, 2, 3, 4, 0, 0); + public static readonly IccFormulaCurveElement FormulaValVar2 = new(IccFormulaCurveType.Type2, 1, 2, 3, 4, 5, 0); + public static readonly IccFormulaCurveElement FormulaValVar3 = new(IccFormulaCurveType.Type3, 0, 2, 3, 4, 5, 6); - public static readonly byte[] Formula_Var1 = ArrayHelper.Concat( + public static readonly byte[] FormulaVar1 = ArrayHelper.Concat( new byte[] { 0x00, 0x00, 0x00, 0x00, }, - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4); + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4); - public static readonly byte[] Formula_Var2 = ArrayHelper.Concat( + public static readonly byte[] FormulaVar2 = ArrayHelper.Concat( new byte[] { 0x00, 0x01, 0x00, 0x00, }, - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5); + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5); - public static readonly byte[] Formula_Var3 = ArrayHelper.Concat( + public static readonly byte[] FormulaVar3 = ArrayHelper.Concat( new byte[] { 0x00, 0x02, 0x00, 0x00, }, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_6); + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single6); public static readonly object[][] FormulaCurveSegmentTestData = - { - new object[] { Formula_Var1, Formula_ValVar1 }, - new object[] { Formula_Var2, Formula_ValVar2 }, - new object[] { Formula_Var3, Formula_ValVar3 }, - }; + [ + [FormulaVar1, FormulaValVar1], + [FormulaVar2, FormulaValVar2], + [FormulaVar3, FormulaValVar3] + ]; // Sampled Segment - public static readonly IccSampledCurveElement Sampled_ValGrad1 = new IccSampledCurveElement(new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }); - public static readonly IccSampledCurveElement Sampled_ValGrad2 = new IccSampledCurveElement(new float[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 }); - - public static readonly byte[] Sampled_Grad1 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_9, - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_9); - - public static readonly byte[] Sampled_Grad2 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_9, - IccTestDataPrimitives.Single_9, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_1); + public static readonly IccSampledCurveElement SampledValGrad1 = new([1, 2, 3, 4, 5, 6, 7, 8, 9]); + public static readonly IccSampledCurveElement SampledValGrad2 = new([9, 8, 7, 6, 5, 4, 3, 2, 1]); + + public static readonly byte[] SampledGrad1 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt329, + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single9); + + public static readonly byte[] SampledGrad2 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt329, + IccTestDataPrimitives.Single9, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single1); public static readonly object[][] SampledCurveSegmentTestData = - { - new object[] { Sampled_Grad1, Sampled_ValGrad1 }, - new object[] { Sampled_Grad2, Sampled_ValGrad2 }, - }; - - public static readonly IccCurveSegment Segment_ValFormula1 = Formula_ValVar1; - public static readonly IccCurveSegment Segment_ValFormula2 = Formula_ValVar2; - public static readonly IccCurveSegment Segment_ValFormula3 = Formula_ValVar3; - public static readonly IccCurveSegment Segment_ValSampled1 = Sampled_ValGrad1; - public static readonly IccCurveSegment Segment_ValSampled2 = Sampled_ValGrad2; - - public static readonly byte[] Segment_Formula1 = ArrayHelper.Concat( + [ + [SampledGrad1, SampledValGrad1], + [SampledGrad2, SampledValGrad2] + ]; + + public static readonly IccCurveSegment SegmentValFormula1 = FormulaValVar1; + public static readonly IccCurveSegment SegmentValFormula2 = FormulaValVar2; + public static readonly IccCurveSegment SegmentValFormula3 = FormulaValVar3; + public static readonly IccCurveSegment SegmentValSampled1 = SampledValGrad1; + public static readonly IccCurveSegment SegmentValSampled2 = SampledValGrad2; + + public static readonly byte[] SegmentFormula1 = ArrayHelper.Concat( new byte[] { 0x70, 0x61, 0x72, 0x66, 0x00, 0x00, 0x00, 0x00, }, - Formula_Var1); + FormulaVar1); - public static readonly byte[] Segment_Formula2 = ArrayHelper.Concat( + public static readonly byte[] SegmentFormula2 = ArrayHelper.Concat( new byte[] { 0x70, 0x61, 0x72, 0x66, 0x00, 0x00, 0x00, 0x00, }, - Formula_Var2); + FormulaVar2); - public static readonly byte[] Segment_Formula3 = ArrayHelper.Concat( + public static readonly byte[] SegmentFormula3 = ArrayHelper.Concat( new byte[] { 0x70, 0x61, 0x72, 0x66, 0x00, 0x00, 0x00, 0x00, }, - Formula_Var3); + FormulaVar3); - public static readonly byte[] Segment_Sampled1 = ArrayHelper.Concat( + public static readonly byte[] SegmentSampled1 = ArrayHelper.Concat( new byte[] { 0x73, 0x61, 0x6D, 0x66, 0x00, 0x00, 0x00, 0x00, }, - Sampled_Grad1); + SampledGrad1); - public static readonly byte[] Segment_Sampled2 = ArrayHelper.Concat( + public static readonly byte[] SegmentSampled2 = ArrayHelper.Concat( new byte[] { 0x73, 0x61, 0x6D, 0x66, 0x00, 0x00, 0x00, 0x00, }, - Sampled_Grad2); + SampledGrad2); public static readonly object[][] CurveSegmentTestData = - { - new object[] { Segment_Formula1, Segment_ValFormula1 }, - new object[] { Segment_Formula2, Segment_ValFormula2 }, - new object[] { Segment_Formula3, Segment_ValFormula3 }, - new object[] { Segment_Sampled1, Segment_ValSampled1 }, - new object[] { Segment_Sampled2, Segment_ValSampled2 }, - }; - - public static readonly IccOneDimensionalCurve OneDimensional_ValFormula1 = new IccOneDimensionalCurve( - new float[] { 0, 1 }, - new IccCurveSegment[] { Segment_ValFormula1, Segment_ValFormula2, Segment_ValFormula3 }); - - public static readonly IccOneDimensionalCurve OneDimensional_ValFormula2 = new IccOneDimensionalCurve( - new float[] { 0, 1 }, - new IccCurveSegment[] { Segment_ValFormula3, Segment_ValFormula2, Segment_ValFormula1 }); - - public static readonly IccOneDimensionalCurve OneDimensional_ValSampled = new IccOneDimensionalCurve( - new float[] { 0, 1 }, - new IccCurveSegment[] { Segment_ValSampled1, Segment_ValSampled2, Segment_ValSampled1 }); - - public static readonly byte[] OneDimensional_Formula1 = ArrayHelper.Concat( + [ + [SegmentFormula1, SegmentValFormula1], + [SegmentFormula2, SegmentValFormula2], + [SegmentFormula3, SegmentValFormula3], + [SegmentSampled1, SegmentValSampled1], + [SegmentSampled2, SegmentValSampled2] + ]; + + public static readonly IccOneDimensionalCurve OneDimensionalValFormula1 = new( + [0, 1], + [SegmentValFormula1, SegmentValFormula2, SegmentValFormula3]); + + public static readonly IccOneDimensionalCurve OneDimensionalValFormula2 = new( + [0, 1], + [SegmentValFormula3, SegmentValFormula2, SegmentValFormula1]); + + public static readonly IccOneDimensionalCurve OneDimensionalValSampled = new( + [0, 1], + [SegmentValSampled1, SegmentValSampled2, SegmentValSampled1]); + + public static readonly byte[] OneDimensionalFormula1 = ArrayHelper.Concat( new byte[] { 0x00, 0x03, 0x00, 0x00, }, - IccTestDataPrimitives.Single_0, - IccTestDataPrimitives.Single_1, - Segment_Formula1, - Segment_Formula2, - Segment_Formula3); + IccTestDataPrimitives.Single0, + IccTestDataPrimitives.Single1, + SegmentFormula1, + SegmentFormula2, + SegmentFormula3); - public static readonly byte[] OneDimensional_Formula2 = ArrayHelper.Concat( + public static readonly byte[] OneDimensionalFormula2 = ArrayHelper.Concat( new byte[] { 0x00, 0x03, 0x00, 0x00, }, - IccTestDataPrimitives.Single_0, - IccTestDataPrimitives.Single_1, - Segment_Formula3, - Segment_Formula2, - Segment_Formula1); + IccTestDataPrimitives.Single0, + IccTestDataPrimitives.Single1, + SegmentFormula3, + SegmentFormula2, + SegmentFormula1); - public static readonly byte[] OneDimensional_Sampled = ArrayHelper.Concat( + public static readonly byte[] OneDimensionalSampled = ArrayHelper.Concat( new byte[] { 0x00, 0x03, 0x00, 0x00, }, - IccTestDataPrimitives.Single_0, - IccTestDataPrimitives.Single_1, - Segment_Sampled1, - Segment_Sampled2, - Segment_Sampled1); + IccTestDataPrimitives.Single0, + IccTestDataPrimitives.Single1, + SegmentSampled1, + SegmentSampled2, + SegmentSampled1); public static readonly object[][] OneDimensionalCurveTestData = - { - new object[] { OneDimensional_Formula1, OneDimensional_ValFormula1 }, - new object[] { OneDimensional_Formula2, OneDimensional_ValFormula2 }, - new object[] { OneDimensional_Sampled, OneDimensional_ValSampled }, - }; + [ + [OneDimensionalFormula1, OneDimensionalValFormula1], + [OneDimensionalFormula2, OneDimensionalValFormula2], + [OneDimensionalSampled, OneDimensionalValSampled] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs index 7a778f269b..c0404d5f12 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs @@ -3,14 +3,14 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataLut { - public static readonly IccLut LUT8_ValGrad = CreateLUT8Val(); - public static readonly byte[] LUT8_Grad = CreateLUT8(); + public static readonly IccLut Lut8ValGrad = CreateLut8Val(); + public static readonly byte[] Lut8Grad = CreateLut8(); - private static IccLut CreateLUT8Val() + private static IccLut CreateLut8Val() { float[] result = new float[256]; for (int i = 0; i < 256; i++) @@ -21,7 +21,7 @@ private static IccLut CreateLUT8Val() return new IccLut(result); } - private static byte[] CreateLUT8() + private static byte[] CreateLut8() { byte[] result = new byte[256]; for (int i = 0; i < 256; i++) @@ -33,12 +33,11 @@ private static byte[] CreateLUT8() } public static readonly object[][] Lut8TestData = - { - new object[] { LUT8_Grad, LUT8_ValGrad }, - }; + [ + [Lut8Grad, Lut8ValGrad] + ]; - public static readonly IccLut LUT16_ValGrad = new IccLut(new float[] - { + public static readonly IccLut Lut16ValGrad = new([ 1f / ushort.MaxValue, 2f / ushort.MaxValue, 3f / ushort.MaxValue, @@ -50,51 +49,51 @@ private static byte[] CreateLUT8() 9f / ushort.MaxValue, 32768f / ushort.MaxValue, 1f - }); - - public static readonly byte[] LUT16_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_1, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_4, - IccTestDataPrimitives.UInt16_5, - IccTestDataPrimitives.UInt16_6, - IccTestDataPrimitives.UInt16_7, - IccTestDataPrimitives.UInt16_8, - IccTestDataPrimitives.UInt16_9, - IccTestDataPrimitives.UInt16_32768, - IccTestDataPrimitives.UInt16_Max); + ]); + + public static readonly byte[] Lut16Grad = ArrayHelper.Concat( + IccTestDataPrimitives.UInt161, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt164, + IccTestDataPrimitives.UInt165, + IccTestDataPrimitives.UInt166, + IccTestDataPrimitives.UInt167, + IccTestDataPrimitives.UInt168, + IccTestDataPrimitives.UInt169, + IccTestDataPrimitives.UInt1632768, + IccTestDataPrimitives.UInt16Max); public static readonly object[][] Lut16TestData = - { - new object[] { LUT16_Grad, LUT16_ValGrad, 11 }, - }; - - public static readonly IccClut CLUT8_ValGrad = new IccClut( - new float[][] - { - new float[] { 1f / byte.MaxValue, 2f / byte.MaxValue, 3f / byte.MaxValue }, - new float[] { 4f / byte.MaxValue, 5f / byte.MaxValue, 6f / byte.MaxValue }, - new float[] { 7f / byte.MaxValue, 8f / byte.MaxValue, 9f / byte.MaxValue }, - - new float[] { 10f / byte.MaxValue, 11f / byte.MaxValue, 12f / byte.MaxValue }, - new float[] { 13f / byte.MaxValue, 14f / byte.MaxValue, 15f / byte.MaxValue }, - new float[] { 16f / byte.MaxValue, 17f / byte.MaxValue, 18f / byte.MaxValue }, - - new float[] { 19f / byte.MaxValue, 20f / byte.MaxValue, 21f / byte.MaxValue }, - new float[] { 22f / byte.MaxValue, 23f / byte.MaxValue, 24f / byte.MaxValue }, - new float[] { 25f / byte.MaxValue, 26f / byte.MaxValue, 27f / byte.MaxValue }, - }, - new byte[] { 3, 3 }, - IccClutDataType.UInt8); + [ + [Lut16Grad, Lut16ValGrad, 11] + ]; + + public static readonly IccClut Clut8ValGrad = new( + [ + 1f / byte.MaxValue, 2f / byte.MaxValue, 3f / byte.MaxValue, + 4f / byte.MaxValue, 5f / byte.MaxValue, 6f / byte.MaxValue, + 7f / byte.MaxValue, 8f / byte.MaxValue, 9f / byte.MaxValue, + + 10f / byte.MaxValue, 11f / byte.MaxValue, 12f / byte.MaxValue, + 13f / byte.MaxValue, 14f / byte.MaxValue, 15f / byte.MaxValue, + 16f / byte.MaxValue, 17f / byte.MaxValue, 18f / byte.MaxValue, + + 19f / byte.MaxValue, 20f / byte.MaxValue, 21f / byte.MaxValue, + 22f / byte.MaxValue, 23f / byte.MaxValue, 24f / byte.MaxValue, + 25f / byte.MaxValue, 26f / byte.MaxValue, 27f / byte.MaxValue + ], + [3, 3], + IccClutDataType.UInt8, + outputChannelCount: 3); /// /// Input Channel Count: 2 /// Output Channel Count: 3 /// Grid-point Count: { 3, 3 } /// - public static readonly byte[] CLUT8_Grad = - { + public static readonly byte[] Clut8Grad = + [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, @@ -105,39 +104,39 @@ private static byte[] CreateLUT8() 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x1A, 0x1B, - }; + 0x19, 0x1A, 0x1B + ]; public static readonly object[][] Clut8TestData = - { - new object[] { CLUT8_Grad, CLUT8_ValGrad, 2, 3, new byte[] { 3, 3 } }, - }; - - public static readonly IccClut CLUT16_ValGrad = new IccClut( - new float[][] - { - new float[] { 1f / ushort.MaxValue, 2f / ushort.MaxValue, 3f / ushort.MaxValue }, - new float[] { 4f / ushort.MaxValue, 5f / ushort.MaxValue, 6f / ushort.MaxValue }, - new float[] { 7f / ushort.MaxValue, 8f / ushort.MaxValue, 9f / ushort.MaxValue }, - - new float[] { 10f / ushort.MaxValue, 11f / ushort.MaxValue, 12f / ushort.MaxValue }, - new float[] { 13f / ushort.MaxValue, 14f / ushort.MaxValue, 15f / ushort.MaxValue }, - new float[] { 16f / ushort.MaxValue, 17f / ushort.MaxValue, 18f / ushort.MaxValue }, - - new float[] { 19f / ushort.MaxValue, 20f / ushort.MaxValue, 21f / ushort.MaxValue }, - new float[] { 22f / ushort.MaxValue, 23f / ushort.MaxValue, 24f / ushort.MaxValue }, - new float[] { 25f / ushort.MaxValue, 26f / ushort.MaxValue, 27f / ushort.MaxValue }, - }, - new byte[] { 3, 3 }, - IccClutDataType.UInt16); + [ + [Clut8Grad, Clut8ValGrad, 2, 3, new byte[] { 3, 3 }] + ]; + + public static readonly IccClut Clut16ValGrad = new( + [ + 1f / ushort.MaxValue, 2f / ushort.MaxValue, 3f / ushort.MaxValue, + 4f / ushort.MaxValue, 5f / ushort.MaxValue, 6f / ushort.MaxValue, + 7f / ushort.MaxValue, 8f / ushort.MaxValue, 9f / ushort.MaxValue, + + 10f / ushort.MaxValue, 11f / ushort.MaxValue, 12f / ushort.MaxValue, + 13f / ushort.MaxValue, 14f / ushort.MaxValue, 15f / ushort.MaxValue, + 16f / ushort.MaxValue, 17f / ushort.MaxValue, 18f / ushort.MaxValue, + + 19f / ushort.MaxValue, 20f / ushort.MaxValue, 21f / ushort.MaxValue, + 22f / ushort.MaxValue, 23f / ushort.MaxValue, 24f / ushort.MaxValue, + 25f / ushort.MaxValue, 26f / ushort.MaxValue, 27f / ushort.MaxValue + ], + [3, 3], + IccClutDataType.UInt16, + outputChannelCount: 3); /// /// Input Channel Count: 2 /// Output Channel Count: 3 /// Grid-point Count: { 3, 3 } /// - public static readonly byte[] CLUT16_Grad = - { + public static readonly byte[] Clut16Grad = + [ 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, @@ -148,93 +147,93 @@ private static byte[] CreateLUT8() 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, - 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, - }; + 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B + ]; public static readonly object[][] Clut16TestData = - { - new object[] { CLUT16_Grad, CLUT16_ValGrad, 2, 3, new byte[] { 3, 3 } }, - }; - - public static readonly IccClut CLUTf32_ValGrad = new IccClut( - new float[][] - { - new float[] { 1f, 2f, 3f }, - new float[] { 4f, 5f, 6f }, - new float[] { 7f, 8f, 9f }, - - new float[] { 1f, 2f, 3f }, - new float[] { 4f, 5f, 6f }, - new float[] { 7f, 8f, 9f }, - - new float[] { 1f, 2f, 3f }, - new float[] { 4f, 5f, 6f }, - new float[] { 7f, 8f, 9f }, - }, - new byte[] { 3, 3 }, - IccClutDataType.Float); + [ + [Clut16Grad, Clut16ValGrad, 2, 3, new byte[] { 3, 3 }] + ]; + + public static readonly IccClut CluTf32ValGrad = new( + [ + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f, + + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f, + + 1f, 2f, 3f, + 4f, 5f, 6f, + 7f, 8f, 9f + ], + [3, 3], + IccClutDataType.Float, + outputChannelCount: 3); /// /// Input Channel Count: 2 /// Output Channel Count: 3 /// Grid-point Count: { 3, 3 } /// - public static readonly byte[] CLUTf32_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_9, - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_9, - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_9); + public static readonly byte[] CluTf32Grad = ArrayHelper.Concat( + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single9, + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single9, + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single9); public static readonly object[][] ClutF32TestData = - { - new object[] { CLUTf32_Grad, CLUTf32_ValGrad, 2, 3, new byte[] { 3, 3 } }, - }; + [ + [CluTf32Grad, CluTf32ValGrad, 2, 3, new byte[] { 3, 3 }] + ]; - public static readonly IccClut CLUT_Val8 = CLUT8_ValGrad; - public static readonly IccClut CLUT_Val16 = CLUT16_ValGrad; - public static readonly IccClut CLUT_Valf32 = CLUTf32_ValGrad; + public static readonly IccClut ClutVal8 = Clut8ValGrad; + public static readonly IccClut ClutVal16 = Clut16ValGrad; + public static readonly IccClut ClutValf32 = CluTf32ValGrad; - public static readonly byte[] CLUT_8 = ArrayHelper.Concat( + public static readonly byte[] Clut8 = ArrayHelper.Concat( new byte[16] { 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new byte[4] { 0x01, 0x00, 0x00, 0x00 }, - CLUT8_Grad); + Clut8Grad); - public static readonly byte[] CLUT_16 = ArrayHelper.Concat( + public static readonly byte[] Clut16 = ArrayHelper.Concat( new byte[16] { 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, new byte[4] { 0x02, 0x00, 0x00, 0x00 }, - CLUT16_Grad); + Clut16Grad); - public static readonly byte[] CLUT_f32 = ArrayHelper.Concat( + public static readonly byte[] ClutF32 = ArrayHelper.Concat( new byte[16] { 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - CLUTf32_Grad); + CluTf32Grad); public static readonly object[][] ClutTestData = - { - new object[] { CLUT_8, CLUT_Val8, 2, 3, false }, - new object[] { CLUT_16, CLUT_Val16, 2, 3, false }, - new object[] { CLUT_f32, CLUT_Valf32, 2, 3, true }, - }; + [ + [Clut8, ClutVal8, 2, 3, false], + [Clut16, ClutVal16, 2, 3, false], + [ClutF32, ClutValf32, 2, 3, true] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 94df8d69a6..6a900a0154 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -3,16 +3,14 @@ using System.Numerics; -namespace SixLabors.ImageSharp.Tests; - -using SixLabors.ImageSharp; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataMatrix { /// /// 3x3 Matrix /// - public static readonly float[,] Single_2DArray_ValGrad = + public static readonly float[,] Single2DArrayValGrad = { { 1, 2, 3 }, { 4, 5, 6 }, @@ -22,7 +20,7 @@ internal static class IccTestDataMatrix /// /// 3x3 Matrix /// - public static readonly float[,] Single_2DArray_ValIdentity = + public static readonly float[,] Single2DArrayValIdentity = { { 1, 0, 0 }, { 0, 1, 0 }, @@ -32,121 +30,121 @@ internal static class IccTestDataMatrix /// /// 3x3 Matrix /// - public static readonly Matrix4x4 Single_Matrix4x4_ValGrad = new Matrix4x4(1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 0, 0, 0, 1); + public static readonly Matrix4x4 SingleMatrix4X4ValGrad = new(1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 0, 0, 0, 1); /// /// 3x3 Matrix /// - public static readonly Matrix4x4 Single_Matrix4x4_ValIdentity = Matrix4x4.Identity; + public static readonly Matrix4x4 SingleMatrix4X4ValIdentity = Matrix4x4.Identity; /// /// 3x3 Matrix /// - public static readonly DenseMatrix Single_DenseMatrix_ValGrad = new DenseMatrix(Single_2DArray_ValGrad); + public static readonly DenseMatrix SingleDenseMatrixValGrad = new(Single2DArrayValGrad); /// /// 3x3 Matrix /// - public static readonly DenseMatrix Single_DenseMatrix_ValIdentity = new DenseMatrix(Single_2DArray_ValIdentity); + public static readonly DenseMatrix SingleDenseMatrixValIdentity = new(Single2DArrayValIdentity); /// /// 3x3 Matrix /// - public static readonly byte[] Fix16_2D_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_4, - IccTestDataPrimitives.Fix16_7, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_5, - IccTestDataPrimitives.Fix16_8, - IccTestDataPrimitives.Fix16_3, - IccTestDataPrimitives.Fix16_6, - IccTestDataPrimitives.Fix16_9); + public static readonly byte[] Fix162DGrad = ArrayHelper.Concat( + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix164, + IccTestDataPrimitives.Fix167, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix165, + IccTestDataPrimitives.Fix168, + IccTestDataPrimitives.Fix163, + IccTestDataPrimitives.Fix166, + IccTestDataPrimitives.Fix169); /// /// 3x3 Matrix /// - public static readonly byte[] Fix16_2D_Identity = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_0, - IccTestDataPrimitives.Fix16_1); + public static readonly byte[] Fix162DIdentity = ArrayHelper.Concat( + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix160, + IccTestDataPrimitives.Fix161); /// /// 3x3 Matrix /// - public static readonly byte[] Single_2D_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_7, - IccTestDataPrimitives.Single_2, - IccTestDataPrimitives.Single_5, - IccTestDataPrimitives.Single_8, - IccTestDataPrimitives.Single_3, - IccTestDataPrimitives.Single_6, - IccTestDataPrimitives.Single_9); - - public static readonly object[][] Matrix2D_FloatArrayTestData = - { - new object[] { Fix16_2D_Grad, 3, 3, false, Single_2DArray_ValGrad }, - new object[] { Fix16_2D_Identity, 3, 3, false, Single_2DArray_ValIdentity }, - new object[] { Single_2D_Grad, 3, 3, true, Single_2DArray_ValGrad }, - }; - - public static readonly object[][] Matrix2D_DenseMatrixTestData = - { - new object[] { Fix16_2D_Grad, 3, 3, false, Single_DenseMatrix_ValGrad }, - new object[] { Fix16_2D_Identity, 3, 3, false, Single_DenseMatrix_ValIdentity }, - new object[] { Single_2D_Grad, 3, 3, true, Single_DenseMatrix_ValGrad }, - }; - - public static readonly object[][] Matrix2D_Matrix4x4TestData = - { - new object[] { Fix16_2D_Grad, 3, 3, false, Single_Matrix4x4_ValGrad }, - new object[] { Fix16_2D_Identity, 3, 3, false, Single_Matrix4x4_ValIdentity }, - new object[] { Single_2D_Grad, 3, 3, true, Single_Matrix4x4_ValGrad }, - }; + public static readonly byte[] Single2DGrad = ArrayHelper.Concat( + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single7, + IccTestDataPrimitives.Single2, + IccTestDataPrimitives.Single5, + IccTestDataPrimitives.Single8, + IccTestDataPrimitives.Single3, + IccTestDataPrimitives.Single6, + IccTestDataPrimitives.Single9); + + public static readonly object[][] Matrix2DFloatArrayTestData = + [ + [Fix162DGrad, 3, 3, false, Single2DArrayValGrad], + [Fix162DIdentity, 3, 3, false, Single2DArrayValIdentity], + [Single2DGrad, 3, 3, true, Single2DArrayValGrad] + ]; + + public static readonly object[][] Matrix2DDenseMatrixTestData = + [ + [Fix162DGrad, 3, 3, false, SingleDenseMatrixValGrad], + [Fix162DIdentity, 3, 3, false, SingleDenseMatrixValIdentity], + [Single2DGrad, 3, 3, true, SingleDenseMatrixValGrad] + ]; + + public static readonly object[][] Matrix2DMatrix4X4TestData = + [ + [Fix162DGrad, 3, 3, false, SingleMatrix4X4ValGrad], + [Fix162DIdentity, 3, 3, false, SingleMatrix4X4ValIdentity], + [Single2DGrad, 3, 3, true, SingleMatrix4X4ValGrad] + ]; /// /// 3x1 Matrix /// - public static readonly float[] Single_1DArray_ValGrad = { 1, 4, 7 }; + public static readonly float[] Single1DArrayValGrad = [1, 4, 7]; /// /// 3x1 Matrix /// - public static readonly Vector3 Single_Vector3_ValGrad = new Vector3(1, 4, 7); + public static readonly Vector3 SingleVector3ValGrad = new(1, 4, 7); /// /// 3x1 Matrix /// - public static readonly byte[] Fix16_1D_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_4, - IccTestDataPrimitives.Fix16_7); + public static readonly byte[] Fix161DGrad = ArrayHelper.Concat( + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix164, + IccTestDataPrimitives.Fix167); /// /// 3x1 Matrix /// - public static readonly byte[] Single_1D_Grad = ArrayHelper.Concat( - IccTestDataPrimitives.Single_1, - IccTestDataPrimitives.Single_4, - IccTestDataPrimitives.Single_7); - - public static readonly object[][] Matrix1D_ArrayTestData = - { - new object[] { Fix16_1D_Grad, 3, false, Single_1DArray_ValGrad }, - new object[] { Single_1D_Grad, 3, true, Single_1DArray_ValGrad }, - }; - - public static readonly object[][] Matrix1D_Vector3TestData = - { - new object[] { Fix16_1D_Grad, 3, false, Single_Vector3_ValGrad }, - new object[] { Single_1D_Grad, 3, true, Single_Vector3_ValGrad }, - }; + public static readonly byte[] Single1DGrad = ArrayHelper.Concat( + IccTestDataPrimitives.Single1, + IccTestDataPrimitives.Single4, + IccTestDataPrimitives.Single7); + + public static readonly object[][] Matrix1DArrayTestData = + [ + [Fix161DGrad, 3, false, Single1DArrayValGrad], + [Single1DGrad, 3, true, Single1DArrayValGrad] + ]; + + public static readonly object[][] Matrix1DVector3TestData = + [ + [Fix161DGrad, 3, false, SingleVector3ValGrad], + [Single1DGrad, 3, true, SingleVector3ValGrad] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs index 2c5c432710..5219dfdacd 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataMultiProcessElements { @@ -11,120 +11,119 @@ internal static class IccTestDataMultiProcessElements /// Input Channel Count: 3 /// Output Channel Count: 3 /// - public static readonly IccCurveSetProcessElement CurvePE_ValGrad = new IccCurveSetProcessElement(new IccOneDimensionalCurve[] - { - IccTestDataCurves.OneDimensional_ValFormula1, - IccTestDataCurves.OneDimensional_ValFormula2, - IccTestDataCurves.OneDimensional_ValFormula1 - }); + public static readonly IccCurveSetProcessElement CurvePeValGrad = new([ + IccTestDataCurves.OneDimensionalValFormula1, + IccTestDataCurves.OneDimensionalValFormula2, + IccTestDataCurves.OneDimensionalValFormula1 + ]); /// /// Input Channel Count: 3 /// Output Channel Count: 3 /// - public static readonly byte[] CurvePE_Grad = ArrayHelper.Concat( - IccTestDataCurves.OneDimensional_Formula1, - IccTestDataCurves.OneDimensional_Formula2, - IccTestDataCurves.OneDimensional_Formula1); + public static readonly byte[] CurvePeGrad = ArrayHelper.Concat( + IccTestDataCurves.OneDimensionalFormula1, + IccTestDataCurves.OneDimensionalFormula2, + IccTestDataCurves.OneDimensionalFormula1); public static readonly object[][] CurveSetTestData = - { - new object[] { CurvePE_Grad, CurvePE_ValGrad, 3, 3 }, - }; + [ + [CurvePeGrad, CurvePeValGrad, 3, 3] + ]; /// /// Input Channel Count: 3 /// Output Channel Count: 3 /// - public static readonly IccMatrixProcessElement MatrixPE_ValGrad = new IccMatrixProcessElement( - IccTestDataMatrix.Single_2DArray_ValGrad, - IccTestDataMatrix.Single_1DArray_ValGrad); + public static readonly IccMatrixProcessElement MatrixPeValGrad = new( + IccTestDataMatrix.Single2DArrayValGrad, + IccTestDataMatrix.Single1DArrayValGrad); /// /// Input Channel Count: 3 /// Output Channel Count: 3 /// - public static readonly byte[] MatrixPE_Grad = ArrayHelper.Concat( - IccTestDataMatrix.Single_2D_Grad, - IccTestDataMatrix.Single_1D_Grad); + public static readonly byte[] MatrixPeGrad = ArrayHelper.Concat( + IccTestDataMatrix.Single2DGrad, + IccTestDataMatrix.Single1DGrad); public static readonly object[][] MatrixTestData = - { - new object[] { MatrixPE_Grad, MatrixPE_ValGrad, 3, 3 }, - }; + [ + [MatrixPeGrad, MatrixPeValGrad, 3, 3] + ]; /// /// Input Channel Count: 2 /// Output Channel Count: 3 /// - public static readonly IccClutProcessElement CLUTPE_ValGrad = new IccClutProcessElement(IccTestDataLut.CLUT_Valf32); + public static readonly IccClutProcessElement ClutpeValGrad = new(IccTestDataLut.ClutValf32); /// /// Input Channel Count: 2 /// Output Channel Count: 3 /// - public static readonly byte[] CLUTPE_Grad = IccTestDataLut.CLUT_f32; + public static readonly byte[] ClutpeGrad = IccTestDataLut.ClutF32; public static readonly object[][] ClutTestData = - { - new object[] { CLUTPE_Grad, CLUTPE_ValGrad, 2, 3 }, - }; + [ + [ClutpeGrad, ClutpeValGrad, 2, 3] + ]; - public static readonly IccMultiProcessElement MPE_ValMatrix = MatrixPE_ValGrad; - public static readonly IccMultiProcessElement MPE_ValCLUT = CLUTPE_ValGrad; - public static readonly IccMultiProcessElement MPE_ValCurve = CurvePE_ValGrad; - public static readonly IccMultiProcessElement MPE_ValbACS = new IccBAcsProcessElement(3, 3); - public static readonly IccMultiProcessElement MPE_ValeACS = new IccEAcsProcessElement(3, 3); + public static readonly IccMultiProcessElement MpeValMatrix = MatrixPeValGrad; + public static readonly IccMultiProcessElement MpeValClut = ClutpeValGrad; + public static readonly IccMultiProcessElement MpeValCurve = CurvePeValGrad; + public static readonly IccMultiProcessElement MpeValbAcs = new IccBAcsProcessElement(3, 3); + public static readonly IccMultiProcessElement MpeValeAcs = new IccEAcsProcessElement(3, 3); - public static readonly byte[] MPE_Matrix = ArrayHelper.Concat( + public static readonly byte[] MpeMatrix = ArrayHelper.Concat( new byte[] { 0x6D, 0x61, 0x74, 0x66, 0x00, 0x03, 0x00, 0x03, }, - MatrixPE_Grad); + MatrixPeGrad); - public static readonly byte[] MPE_CLUT = ArrayHelper.Concat( + public static readonly byte[] MpeClut = ArrayHelper.Concat( new byte[] { 0x63, 0x6C, 0x75, 0x74, 0x00, 0x02, 0x00, 0x03, }, - CLUTPE_Grad); + ClutpeGrad); - public static readonly byte[] MPE_Curve = ArrayHelper.Concat( + public static readonly byte[] MpeCurve = ArrayHelper.Concat( new byte[] { 0x6D, 0x66, 0x6C, 0x74, 0x00, 0x03, 0x00, 0x03, }, - CurvePE_Grad); + CurvePeGrad); - public static readonly byte[] MPE_bACS = - { + public static readonly byte[] MpeBAcs = + [ 0x62, 0x41, 0x43, 0x53, 0x00, 0x03, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly byte[] MPE_eACS = - { + public static readonly byte[] MpeEAcs = + [ 0x65, 0x41, 0x43, 0x53, 0x00, 0x03, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; public static readonly object[][] MultiProcessElementTestData = - { - new object[] { MPE_Matrix, MPE_ValMatrix }, - new object[] { MPE_CLUT, MPE_ValCLUT }, - new object[] { MPE_Curve, MPE_ValCurve }, - new object[] { MPE_bACS, MPE_ValbACS }, - new object[] { MPE_eACS, MPE_ValeACS }, - }; + [ + [MpeMatrix, MpeValMatrix], + [MpeClut, MpeValClut], + [MpeCurve, MpeValCurve], + [MpeBAcs, MpeValbAcs], + [MpeEAcs, MpeValeAcs] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs index c476f2e6c7..34625aa1c5 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs @@ -5,185 +5,185 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataNonPrimitives { - public static readonly DateTime DateTime_ValMin = new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public static readonly DateTime DateTime_ValMax = new DateTime(9999, 12, 31, 23, 59, 59, DateTimeKind.Utc); - public static readonly DateTime DateTime_ValRand1 = new DateTime(1990, 11, 26, 3, 19, 47, DateTimeKind.Utc); + public static readonly DateTime DateTimeValMin = new(1, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public static readonly DateTime DateTimeValMax = new(9999, 12, 31, 23, 59, 59, DateTimeKind.Utc); + public static readonly DateTime DateTimeValRand1 = new(1990, 11, 26, 3, 19, 47, DateTimeKind.Utc); - public static readonly byte[] DateTime_Min = - { + public static readonly byte[] DateTimeMin = + [ 0x00, 0x01, // Year 1 0x00, 0x01, // Month 1 0x00, 0x01, // Day 1 0x00, 0x00, // Hour 0 0x00, 0x00, // Minute 0 - 0x00, 0x00, // Second 0 - }; + 0x00, 0x00 // Second 0 + ]; - public static readonly byte[] DateTime_Max = - { - 0x27, 0x0F, // Year 9999 + public static readonly byte[] DateTimeMax = + [ + 0x27, 0x0F, // Year 9999 0x00, 0x0C, // Month 12 0x00, 0x1F, // Day 31 0x00, 0x17, // Hour 23 0x00, 0x3B, // Minute 59 - 0x00, 0x3B, // Second 59 - }; + 0x00, 0x3B // Second 59 + ]; - public static readonly byte[] DateTime_Invalid = - { - 0xFF, 0xFF, // Year 65535 + public static readonly byte[] DateTimeInvalid = + [ + 0xFF, 0xFF, // Year 65535 0x00, 0x0E, // Month 14 0x00, 0x21, // Day 33 0x00, 0x19, // Hour 25 0x00, 0x3D, // Minute 61 - 0x00, 0x3D, // Second 61 - }; + 0x00, 0x3D // Second 61 + ]; - public static readonly byte[] DateTime_Rand1 = - { - 0x07, 0xC6, // Year 1990 + public static readonly byte[] DateTimeRand1 = + [ + 0x07, 0xC6, // Year 1990 0x00, 0x0B, // Month 11 0x00, 0x1A, // Day 26 0x00, 0x03, // Hour 3 0x00, 0x13, // Minute 19 - 0x00, 0x2F, // Second 47 - }; + 0x00, 0x2F // Second 47 + ]; public static readonly object[][] DateTimeTestData = - { - new object[] { DateTime_Min, DateTime_ValMin }, - new object[] { DateTime_Max, DateTime_ValMax }, - new object[] { DateTime_Rand1, DateTime_ValRand1 }, - }; - - public static readonly IccVersion VersionNumber_ValMin = new IccVersion(0, 0, 0); - public static readonly IccVersion VersionNumber_Val211 = new IccVersion(2, 1, 1); - public static readonly IccVersion VersionNumber_Val430 = new IccVersion(4, 3, 0); - public static readonly IccVersion VersionNumber_ValMax = new IccVersion(255, 15, 15); - - public static readonly byte[] VersionNumber_Min = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] VersionNumber_211 = { 0x02, 0x11, 0x00, 0x00 }; - public static readonly byte[] VersionNumber_430 = { 0x04, 0x30, 0x00, 0x00 }; - public static readonly byte[] VersionNumber_Max = { 0xFF, 0xFF, 0x00, 0x00 }; + [ + [DateTimeMin, DateTimeValMin], + [DateTimeMax, DateTimeValMax], + [DateTimeRand1, DateTimeValRand1] + ]; + + public static readonly IccVersion VersionNumberValMin = new(0, 0, 0); + public static readonly IccVersion VersionNumberVal211 = new(2, 1, 1); + public static readonly IccVersion VersionNumberVal430 = new(4, 3, 0); + public static readonly IccVersion VersionNumberValMax = new(255, 15, 15); + + public static readonly byte[] VersionNumberMin = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] VersionNumber211 = [0x02, 0x11, 0x00, 0x00]; + public static readonly byte[] VersionNumber430 = [0x04, 0x30, 0x00, 0x00]; + public static readonly byte[] VersionNumberMax = [0xFF, 0xFF, 0x00, 0x00]; public static readonly object[][] VersionNumberTestData = - { - new object[] { VersionNumber_Min, VersionNumber_ValMin }, - new object[] { VersionNumber_211, VersionNumber_Val211 }, - new object[] { VersionNumber_430, VersionNumber_Val430 }, - new object[] { VersionNumber_Max, VersionNumber_ValMax }, - }; - - public static readonly Vector3 XyzNumber_ValMin = new Vector3(IccTestDataPrimitives.Fix16_ValMin, IccTestDataPrimitives.Fix16_ValMin, IccTestDataPrimitives.Fix16_ValMin); - public static readonly Vector3 XyzNumber_Val0 = new Vector3(0, 0, 0); - public static readonly Vector3 XyzNumber_Val1 = new Vector3(1, 1, 1); - public static readonly Vector3 XyzNumber_ValVar1 = new Vector3(1, 2, 3); - public static readonly Vector3 XyzNumber_ValVar2 = new Vector3(4, 5, 6); - public static readonly Vector3 XyzNumber_ValVar3 = new Vector3(7, 8, 9); - public static readonly Vector3 XyzNumber_ValMax = new Vector3(IccTestDataPrimitives.Fix16_ValMax, IccTestDataPrimitives.Fix16_ValMax, IccTestDataPrimitives.Fix16_ValMax); - - public static readonly byte[] XyzNumber_Min = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_Min, IccTestDataPrimitives.Fix16_Min, IccTestDataPrimitives.Fix16_Min); - public static readonly byte[] XyzNumber_0 = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_0, IccTestDataPrimitives.Fix16_0, IccTestDataPrimitives.Fix16_0); - public static readonly byte[] XyzNumber_1 = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_1, IccTestDataPrimitives.Fix16_1, IccTestDataPrimitives.Fix16_1); - public static readonly byte[] XyzNumber_Var1 = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_1, IccTestDataPrimitives.Fix16_2, IccTestDataPrimitives.Fix16_3); - public static readonly byte[] XyzNumber_Var2 = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_4, IccTestDataPrimitives.Fix16_5, IccTestDataPrimitives.Fix16_6); - public static readonly byte[] XyzNumber_Var3 = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_7, IccTestDataPrimitives.Fix16_8, IccTestDataPrimitives.Fix16_9); - public static readonly byte[] XyzNumber_Max = ArrayHelper.Concat(IccTestDataPrimitives.Fix16_Max, IccTestDataPrimitives.Fix16_Max, IccTestDataPrimitives.Fix16_Max); + [ + [VersionNumberMin, VersionNumberValMin], + [VersionNumber211, VersionNumberVal211], + [VersionNumber430, VersionNumberVal430], + [VersionNumberMax, VersionNumberValMax] + ]; + + public static readonly Vector3 XyzNumberValMin = new(IccTestDataPrimitives.Fix16ValMin, IccTestDataPrimitives.Fix16ValMin, IccTestDataPrimitives.Fix16ValMin); + public static readonly Vector3 XyzNumberVal0 = new(0, 0, 0); + public static readonly Vector3 XyzNumberVal1 = new(1, 1, 1); + public static readonly Vector3 XyzNumberValVar1 = new(1, 2, 3); + public static readonly Vector3 XyzNumberValVar2 = new(4, 5, 6); + public static readonly Vector3 XyzNumberValVar3 = new(7, 8, 9); + public static readonly Vector3 XyzNumberValMax = new(IccTestDataPrimitives.Fix16ValMax, IccTestDataPrimitives.Fix16ValMax, IccTestDataPrimitives.Fix16ValMax); + + public static readonly byte[] XyzNumberMin = ArrayHelper.Concat(IccTestDataPrimitives.Fix16Min, IccTestDataPrimitives.Fix16Min, IccTestDataPrimitives.Fix16Min); + public static readonly byte[] XyzNumber0 = ArrayHelper.Concat(IccTestDataPrimitives.Fix160, IccTestDataPrimitives.Fix160, IccTestDataPrimitives.Fix160); + public static readonly byte[] XyzNumber1 = ArrayHelper.Concat(IccTestDataPrimitives.Fix161, IccTestDataPrimitives.Fix161, IccTestDataPrimitives.Fix161); + public static readonly byte[] XyzNumberVar1 = ArrayHelper.Concat(IccTestDataPrimitives.Fix161, IccTestDataPrimitives.Fix162, IccTestDataPrimitives.Fix163); + public static readonly byte[] XyzNumberVar2 = ArrayHelper.Concat(IccTestDataPrimitives.Fix164, IccTestDataPrimitives.Fix165, IccTestDataPrimitives.Fix166); + public static readonly byte[] XyzNumberVar3 = ArrayHelper.Concat(IccTestDataPrimitives.Fix167, IccTestDataPrimitives.Fix168, IccTestDataPrimitives.Fix169); + public static readonly byte[] XyzNumberMax = ArrayHelper.Concat(IccTestDataPrimitives.Fix16Max, IccTestDataPrimitives.Fix16Max, IccTestDataPrimitives.Fix16Max); public static readonly object[][] XyzNumberTestData = - { - new object[] { XyzNumber_Min, XyzNumber_ValMin }, - new object[] { XyzNumber_0, XyzNumber_Val0 }, - new object[] { XyzNumber_Var1, XyzNumber_ValVar1 }, - new object[] { XyzNumber_Max, XyzNumber_ValMax }, - }; + [ + [XyzNumberMin, XyzNumberValMin], + [XyzNumber0, XyzNumberVal0], + [XyzNumberVar1, XyzNumberValVar1], + [XyzNumberMax, XyzNumberValMax] + ]; - public static readonly IccProfileId ProfileId_ValMin = new IccProfileId(0, 0, 0, 0); - public static readonly IccProfileId ProfileId_ValRand = new IccProfileId(IccTestDataPrimitives.UInt32_ValRand1, IccTestDataPrimitives.UInt32_ValRand2, IccTestDataPrimitives.UInt32_ValRand3, IccTestDataPrimitives.UInt32_ValRand4); - public static readonly IccProfileId ProfileId_ValMax = new IccProfileId(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue); + public static readonly IccProfileId ProfileIdValMin = new(0, 0, 0, 0); + public static readonly IccProfileId ProfileIdValRand = new(IccTestDataPrimitives.UInt32ValRand1, IccTestDataPrimitives.UInt32ValRand2, IccTestDataPrimitives.UInt32ValRand3, IccTestDataPrimitives.UInt32ValRand4); + public static readonly IccProfileId ProfileIdValMax = new(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue); - public static readonly byte[] ProfileId_Min = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_0, IccTestDataPrimitives.UInt32_0, IccTestDataPrimitives.UInt32_0, IccTestDataPrimitives.UInt32_0); - public static readonly byte[] ProfileId_Rand = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_Rand1, IccTestDataPrimitives.UInt32_Rand2, IccTestDataPrimitives.UInt32_Rand3, IccTestDataPrimitives.UInt32_Rand4); - public static readonly byte[] ProfileId_Max = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_Max, IccTestDataPrimitives.UInt32_Max, IccTestDataPrimitives.UInt32_Max, IccTestDataPrimitives.UInt32_Max); + public static readonly byte[] ProfileIdMin = ArrayHelper.Concat(IccTestDataPrimitives.UInt320, IccTestDataPrimitives.UInt320, IccTestDataPrimitives.UInt320, IccTestDataPrimitives.UInt320); + public static readonly byte[] ProfileIdRand = ArrayHelper.Concat(IccTestDataPrimitives.UInt32Rand1, IccTestDataPrimitives.UInt32Rand2, IccTestDataPrimitives.UInt32Rand3, IccTestDataPrimitives.UInt32Rand4); + public static readonly byte[] ProfileIdMax = ArrayHelper.Concat(IccTestDataPrimitives.UInt32Max, IccTestDataPrimitives.UInt32Max, IccTestDataPrimitives.UInt32Max, IccTestDataPrimitives.UInt32Max); public static readonly object[][] ProfileIdTestData = - { - new object[] { ProfileId_Min, ProfileId_ValMin }, - new object[] { ProfileId_Rand, ProfileId_ValRand }, - new object[] { ProfileId_Max, ProfileId_ValMax }, - }; + [ + [ProfileIdMin, ProfileIdValMin], + [ProfileIdRand, ProfileIdValRand], + [ProfileIdMax, ProfileIdValMax] + ]; - public static readonly IccPositionNumber PositionNumber_ValMin = new IccPositionNumber(0, 0); - public static readonly IccPositionNumber PositionNumber_ValRand = new IccPositionNumber(IccTestDataPrimitives.UInt32_ValRand1, IccTestDataPrimitives.UInt32_ValRand2); - public static readonly IccPositionNumber PositionNumber_ValMax = new IccPositionNumber(uint.MaxValue, uint.MaxValue); + public static readonly IccPositionNumber PositionNumberValMin = new(0, 0); + public static readonly IccPositionNumber PositionNumberValRand = new(IccTestDataPrimitives.UInt32ValRand1, IccTestDataPrimitives.UInt32ValRand2); + public static readonly IccPositionNumber PositionNumberValMax = new(uint.MaxValue, uint.MaxValue); - public static readonly byte[] PositionNumber_Min = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_0, IccTestDataPrimitives.UInt32_0); - public static readonly byte[] PositionNumber_Rand = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_Rand1, IccTestDataPrimitives.UInt32_Rand2); - public static readonly byte[] PositionNumber_Max = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_Max, IccTestDataPrimitives.UInt32_Max); + public static readonly byte[] PositionNumberMin = ArrayHelper.Concat(IccTestDataPrimitives.UInt320, IccTestDataPrimitives.UInt320); + public static readonly byte[] PositionNumberRand = ArrayHelper.Concat(IccTestDataPrimitives.UInt32Rand1, IccTestDataPrimitives.UInt32Rand2); + public static readonly byte[] PositionNumberMax = ArrayHelper.Concat(IccTestDataPrimitives.UInt32Max, IccTestDataPrimitives.UInt32Max); public static readonly object[][] PositionNumberTestData = - { - new object[] { PositionNumber_Min, PositionNumber_ValMin }, - new object[] { PositionNumber_Rand, PositionNumber_ValRand }, - new object[] { PositionNumber_Max, PositionNumber_ValMax }, - }; - - public static readonly IccResponseNumber ResponseNumber_ValMin = new IccResponseNumber(0, IccTestDataPrimitives.Fix16_ValMin); - public static readonly IccResponseNumber ResponseNumber_Val1 = new IccResponseNumber(1, 1); - public static readonly IccResponseNumber ResponseNumber_Val2 = new IccResponseNumber(2, 2); - public static readonly IccResponseNumber ResponseNumber_Val3 = new IccResponseNumber(3, 3); - public static readonly IccResponseNumber ResponseNumber_Val4 = new IccResponseNumber(4, 4); - public static readonly IccResponseNumber ResponseNumber_Val5 = new IccResponseNumber(5, 5); - public static readonly IccResponseNumber ResponseNumber_Val6 = new IccResponseNumber(6, 6); - public static readonly IccResponseNumber ResponseNumber_Val7 = new IccResponseNumber(7, 7); - public static readonly IccResponseNumber ResponseNumber_Val8 = new IccResponseNumber(8, 8); - public static readonly IccResponseNumber ResponseNumber_Val9 = new IccResponseNumber(9, 9); - public static readonly IccResponseNumber ResponseNumber_ValMax = new IccResponseNumber(ushort.MaxValue, IccTestDataPrimitives.Fix16_ValMax); - - public static readonly byte[] ResponseNumber_Min = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_0, IccTestDataPrimitives.Fix16_Min); - public static readonly byte[] ResponseNumber_1 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_1, IccTestDataPrimitives.Fix16_1); - public static readonly byte[] ResponseNumber_2 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_2, IccTestDataPrimitives.Fix16_2); - public static readonly byte[] ResponseNumber_3 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_3, IccTestDataPrimitives.Fix16_3); - public static readonly byte[] ResponseNumber_4 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_4, IccTestDataPrimitives.Fix16_4); - public static readonly byte[] ResponseNumber_5 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_5, IccTestDataPrimitives.Fix16_5); - public static readonly byte[] ResponseNumber_6 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_6, IccTestDataPrimitives.Fix16_6); - public static readonly byte[] ResponseNumber_7 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_7, IccTestDataPrimitives.Fix16_7); - public static readonly byte[] ResponseNumber_8 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_8, IccTestDataPrimitives.Fix16_8); - public static readonly byte[] ResponseNumber_9 = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_9, IccTestDataPrimitives.Fix16_9); - public static readonly byte[] ResponseNumber_Max = ArrayHelper.Concat(IccTestDataPrimitives.UInt16_Max, IccTestDataPrimitives.Fix16_Max); + [ + [PositionNumberMin, PositionNumberValMin], + [PositionNumberRand, PositionNumberValRand], + [PositionNumberMax, PositionNumberValMax] + ]; + + public static readonly IccResponseNumber ResponseNumberValMin = new(0, IccTestDataPrimitives.Fix16ValMin); + public static readonly IccResponseNumber ResponseNumberVal1 = new(1, 1); + public static readonly IccResponseNumber ResponseNumberVal2 = new(2, 2); + public static readonly IccResponseNumber ResponseNumberVal3 = new(3, 3); + public static readonly IccResponseNumber ResponseNumberVal4 = new(4, 4); + public static readonly IccResponseNumber ResponseNumberVal5 = new(5, 5); + public static readonly IccResponseNumber ResponseNumberVal6 = new(6, 6); + public static readonly IccResponseNumber ResponseNumberVal7 = new(7, 7); + public static readonly IccResponseNumber ResponseNumberVal8 = new(8, 8); + public static readonly IccResponseNumber ResponseNumberVal9 = new(9, 9); + public static readonly IccResponseNumber ResponseNumberValMax = new(ushort.MaxValue, IccTestDataPrimitives.Fix16ValMax); + + public static readonly byte[] ResponseNumberMin = ArrayHelper.Concat(IccTestDataPrimitives.UInt160, IccTestDataPrimitives.Fix16Min); + public static readonly byte[] ResponseNumber1 = ArrayHelper.Concat(IccTestDataPrimitives.UInt161, IccTestDataPrimitives.Fix161); + public static readonly byte[] ResponseNumber2 = ArrayHelper.Concat(IccTestDataPrimitives.UInt162, IccTestDataPrimitives.Fix162); + public static readonly byte[] ResponseNumber3 = ArrayHelper.Concat(IccTestDataPrimitives.UInt163, IccTestDataPrimitives.Fix163); + public static readonly byte[] ResponseNumber4 = ArrayHelper.Concat(IccTestDataPrimitives.UInt164, IccTestDataPrimitives.Fix164); + public static readonly byte[] ResponseNumber5 = ArrayHelper.Concat(IccTestDataPrimitives.UInt165, IccTestDataPrimitives.Fix165); + public static readonly byte[] ResponseNumber6 = ArrayHelper.Concat(IccTestDataPrimitives.UInt166, IccTestDataPrimitives.Fix166); + public static readonly byte[] ResponseNumber7 = ArrayHelper.Concat(IccTestDataPrimitives.UInt167, IccTestDataPrimitives.Fix167); + public static readonly byte[] ResponseNumber8 = ArrayHelper.Concat(IccTestDataPrimitives.UInt168, IccTestDataPrimitives.Fix168); + public static readonly byte[] ResponseNumber9 = ArrayHelper.Concat(IccTestDataPrimitives.UInt169, IccTestDataPrimitives.Fix169); + public static readonly byte[] ResponseNumberMax = ArrayHelper.Concat(IccTestDataPrimitives.UInt16Max, IccTestDataPrimitives.Fix16Max); public static readonly object[][] ResponseNumberTestData = - { - new object[] { ResponseNumber_Min, ResponseNumber_ValMin }, - new object[] { ResponseNumber_1, ResponseNumber_Val1 }, - new object[] { ResponseNumber_4, ResponseNumber_Val4 }, - new object[] { ResponseNumber_Max, ResponseNumber_ValMax }, - }; - - public static readonly IccNamedColor NamedColor_ValMin = new IccNamedColor( + [ + [ResponseNumberMin, ResponseNumberValMin], + [ResponseNumber1, ResponseNumberVal1], + [ResponseNumber4, ResponseNumberVal4], + [ResponseNumberMax, ResponseNumberValMax] + ]; + + public static readonly IccNamedColor NamedColorValMin = new( ArrayHelper.Fill('A', 31), - new ushort[] { 0, 0, 0 }, - new ushort[] { 0, 0, 0 }); + [0, 0, 0], + [0, 0, 0]); - public static readonly IccNamedColor NamedColor_ValRand = new IccNamedColor( + public static readonly IccNamedColor NamedColorValRand = new( ArrayHelper.Fill('5', 31), - new ushort[] { 10794, 10794, 10794 }, - new ushort[] { 17219, 17219, 17219, 17219, 17219 }); + [10794, 10794, 10794], + [17219, 17219, 17219, 17219, 17219]); - public static readonly IccNamedColor NamedColor_ValMax = new IccNamedColor( + public static readonly IccNamedColor NamedColorValMax = new( ArrayHelper.Fill('4', 31), - new ushort[] { ushort.MaxValue, ushort.MaxValue, ushort.MaxValue }, - new ushort[] { ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue }); + [ushort.MaxValue, ushort.MaxValue, ushort.MaxValue], + [ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue]); - public static readonly byte[] NamedColor_Min = CreateNamedColor(3, 0x41, 0x00, 0x00); - public static readonly byte[] NamedColor_Rand = CreateNamedColor(5, 0x35, 42, 67); - public static readonly byte[] NamedColor_Max = CreateNamedColor(4, 0x34, 0xFF, 0xFF); + public static readonly byte[] NamedColorMin = CreateNamedColor(3, 0x41, 0x00, 0x00); + public static readonly byte[] NamedColorRand = CreateNamedColor(5, 0x35, 42, 67); + public static readonly byte[] NamedColorMax = CreateNamedColor(4, 0x34, 0xFF, 0xFF); - private static byte[] CreateNamedColor(int devCoordCount, byte name, byte pCS, byte device) + private static byte[] CreateNamedColor(int devCoordCount, byte name, byte pCs, byte device) { byte[] data = new byte[32 + 6 + (devCoordCount * 2)]; for (int i = 0; i < data.Length; i++) @@ -198,7 +198,7 @@ private static byte[] CreateNamedColor(int devCoordCount, byte name, byte pCS, b } else if (i < 32 + 6) { - data[i] = pCS; // PCS Coordinates + data[i] = pCs; // PCS Coordinates } else { @@ -210,146 +210,146 @@ private static byte[] CreateNamedColor(int devCoordCount, byte name, byte pCS, b } public static readonly object[][] NamedColorTestData = - { - new object[] { NamedColor_Min, NamedColor_ValMin, 3u }, - new object[] { NamedColor_Rand, NamedColor_ValRand, 5u }, - new object[] { NamedColor_Max, NamedColor_ValMax, 4u }, - }; - - private static readonly CultureInfo CultureEnUs = new CultureInfo("en-US"); - private static readonly CultureInfo CultureDeAT = new CultureInfo("de-AT"); - - private static readonly IccLocalizedString LocalizedString_Rand1 = new IccLocalizedString(CultureEnUs, IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand2 = new IccLocalizedString(CultureDeAT, IccTestDataPrimitives.Unicode_ValRand3); - - private static readonly IccLocalizedString[] LocalizedString_RandArr1 = new IccLocalizedString[] - { - LocalizedString_Rand1, - LocalizedString_Rand2, - }; - - private static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr1); - private static readonly byte[] MultiLocalizedUnicode_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, + [ + [NamedColorMin, NamedColorValMin, 3u], + [NamedColorRand, NamedColorValRand, 5u], + [NamedColorMax, NamedColorValMax, 4u] + ]; + + private static readonly CultureInfo CultureEnUs = new("en-US"); + private static readonly CultureInfo CultureDeAt = new("de-AT"); + + private static readonly IccLocalizedString LocalizedStringRand1 = new(CultureEnUs, IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRand2 = new(CultureDeAt, IccTestDataPrimitives.UnicodeValRand3); + + private static readonly IccLocalizedString[] LocalizedStringRandArr1 = + [ + LocalizedStringRand1, + LocalizedStringRand2 + ]; + + private static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicodeVal = new(LocalizedStringRandArr1); + private static readonly byte[] MultiLocalizedUnicodeArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 - new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, + [(byte)'e', (byte)'n', (byte)'U', (byte)'S'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 - new byte[] { (byte)'d', (byte)'e', (byte)'A', (byte)'T' }, + [(byte)'d', (byte)'e', (byte)'A', (byte)'T'], new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 - IccTestDataPrimitives.Unicode_Rand2, - IccTestDataPrimitives.Unicode_Rand3); + IccTestDataPrimitives.UnicodeRand2, + IccTestDataPrimitives.UnicodeRand3); - public static readonly IccTextDescriptionTagDataEntry TextDescription_Val1 = new IccTextDescriptionTagDataEntry( - IccTestDataPrimitives.Ascii_ValRand, - IccTestDataPrimitives.Unicode_ValRand1, + public static readonly IccTextDescriptionTagDataEntry TextDescriptionVal1 = new( + IccTestDataPrimitives.AsciiValRand, + IccTestDataPrimitives.UnicodeValRand1, ArrayHelper.Fill('A', 66), 1701729619, 2); - public static readonly byte[] TextDescription_Arr1 = ArrayHelper.Concat( + public static readonly byte[] TextDescriptionArr1 = ArrayHelper.Concat( new byte[] { 0x00, 0x00, 0x00, 0x0B }, // 11 - IccTestDataPrimitives.Ascii_Rand, + IccTestDataPrimitives.AsciiRand, new byte[] { 0x00 }, // Null terminator - new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, + [(byte)'e', (byte)'n', (byte)'U', (byte)'S'], new byte[] { 0x00, 0x00, 0x00, 0x07 }, // 7 - IccTestDataPrimitives.Unicode_Rand2, + IccTestDataPrimitives.UnicodeRand2, new byte[] { 0x00, 0x00 }, // Null terminator new byte[] { 0x00, 0x02, 0x43 }, // 2, 67 ArrayHelper.Fill((byte)0x41, 66), new byte[] { 0x00 }); // Null terminator - public static readonly IccProfileDescription ProfileDescription_ValRand1 = new IccProfileDescription( + public static readonly IccProfileDescription ProfileDescriptionValRand1 = new( 1, 2, IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.ReflectivityMatte, IccProfileTag.ProfileDescription, - MultiLocalizedUnicode_Val.Texts, - MultiLocalizedUnicode_Val.Texts); + MultiLocalizedUnicodeVal.Texts, + MultiLocalizedUnicodeVal.Texts); - public static readonly IccProfileDescription ProfileDescription_ValRand2 = new IccProfileDescription( + public static readonly IccProfileDescription ProfileDescriptionValRand2 = new( 1, 2, IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.ReflectivityMatte, IccProfileTag.ProfileDescription, - new IccLocalizedString[] { LocalizedString_Rand1 }, - new IccLocalizedString[] { LocalizedString_Rand1 }); + [LocalizedStringRand1], + [LocalizedStringRand1]); - public static readonly byte[] ProfileDescription_Rand1 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_1, - IccTestDataPrimitives.UInt32_2, + public static readonly byte[] ProfileDescriptionRand1 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt321, + IccTestDataPrimitives.UInt322, new byte[] { 0, 0, 0, 0, 0, 0, 0, 10 }, new byte[] { 0x64, 0x65, 0x73, 0x63 }, new byte[] { 0x6D, 0x6C, 0x75, 0x63 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, - MultiLocalizedUnicode_Arr, + MultiLocalizedUnicodeArr, new byte[] { 0x6D, 0x6C, 0x75, 0x63 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, - MultiLocalizedUnicode_Arr); + MultiLocalizedUnicodeArr); - public static readonly byte[] ProfileDescription_Rand2 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_1, - IccTestDataPrimitives.UInt32_2, + public static readonly byte[] ProfileDescriptionRand2 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt321, + IccTestDataPrimitives.UInt322, new byte[] { 0, 0, 0, 0, 0, 0, 0, 10 }, new byte[] { 0x64, 0x65, 0x73, 0x63 }, new byte[] { 0x64, 0x65, 0x73, 0x63 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, - TextDescription_Arr1, + TextDescriptionArr1, new byte[] { 0x64, 0x65, 0x73, 0x63 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, - TextDescription_Arr1); + TextDescriptionArr1); public static readonly object[][] ProfileDescriptionReadTestData = - { - new object[] { ProfileDescription_Rand1, ProfileDescription_ValRand1 }, - new object[] { ProfileDescription_Rand2, ProfileDescription_ValRand2 }, - }; + [ + [ProfileDescriptionRand1, ProfileDescriptionValRand1], + [ProfileDescriptionRand2, ProfileDescriptionValRand2] + ]; public static readonly object[][] ProfileDescriptionWriteTestData = - { - new object[] { ProfileDescription_Rand1, ProfileDescription_ValRand1 }, - }; + [ + [ProfileDescriptionRand1, ProfileDescriptionValRand1] + ]; - public static readonly IccColorantTableEntry ColorantTableEntry_ValRand1 = new IccColorantTableEntry(ArrayHelper.Fill('A', 31), 1, 2, 3); - public static readonly IccColorantTableEntry ColorantTableEntry_ValRand2 = new IccColorantTableEntry(ArrayHelper.Fill('4', 31), 4, 5, 6); + public static readonly IccColorantTableEntry ColorantTableEntryValRand1 = new(ArrayHelper.Fill('A', 31), 1, 2, 3); + public static readonly IccColorantTableEntry ColorantTableEntryValRand2 = new(ArrayHelper.Fill('4', 31), 4, 5, 6); - public static readonly byte[] ColorantTableEntry_Rand1 = ArrayHelper.Concat( + public static readonly byte[] ColorantTableEntryRand1 = ArrayHelper.Concat( ArrayHelper.Fill((byte)0x41, 31), new byte[1], // null terminator - IccTestDataPrimitives.UInt16_1, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3); + IccTestDataPrimitives.UInt161, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163); - public static readonly byte[] ColorantTableEntry_Rand2 = ArrayHelper.Concat( + public static readonly byte[] ColorantTableEntryRand2 = ArrayHelper.Concat( ArrayHelper.Fill((byte)0x34, 31), new byte[1], // null terminator - IccTestDataPrimitives.UInt16_4, - IccTestDataPrimitives.UInt16_5, - IccTestDataPrimitives.UInt16_6); + IccTestDataPrimitives.UInt164, + IccTestDataPrimitives.UInt165, + IccTestDataPrimitives.UInt166); public static readonly object[][] ColorantTableEntryTestData = - { - new object[] { ColorantTableEntry_Rand1, ColorantTableEntry_ValRand1 }, - new object[] { ColorantTableEntry_Rand2, ColorantTableEntry_ValRand2 }, - }; + [ + [ColorantTableEntryRand1, ColorantTableEntryValRand1], + [ColorantTableEntryRand2, ColorantTableEntryValRand2] + ]; - public static readonly IccScreeningChannel ScreeningChannel_ValRand1 = new IccScreeningChannel(4, 6, IccScreeningSpotType.Cross); - public static readonly IccScreeningChannel ScreeningChannel_ValRand2 = new IccScreeningChannel(8, 5, IccScreeningSpotType.Diamond); + public static readonly IccScreeningChannel ScreeningChannelValRand1 = new(4, 6, IccScreeningSpotType.Cross); + public static readonly IccScreeningChannel ScreeningChannelValRand2 = new(8, 5, IccScreeningSpotType.Diamond); - public static readonly byte[] ScreeningChannel_Rand1 = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_4, - IccTestDataPrimitives.Fix16_6, - IccTestDataPrimitives.Int32_7); + public static readonly byte[] ScreeningChannelRand1 = ArrayHelper.Concat( + IccTestDataPrimitives.Fix164, + IccTestDataPrimitives.Fix166, + IccTestDataPrimitives.Int327); - public static readonly byte[] ScreeningChannel_Rand2 = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_8, - IccTestDataPrimitives.Fix16_5, - IccTestDataPrimitives.Int32_3); + public static readonly byte[] ScreeningChannelRand2 = ArrayHelper.Concat( + IccTestDataPrimitives.Fix168, + IccTestDataPrimitives.Fix165, + IccTestDataPrimitives.Int323); public static readonly object[][] ScreeningChannelTestData = - { - new object[] { ScreeningChannel_Rand1, ScreeningChannel_ValRand1 }, - new object[] { ScreeningChannel_Rand2, ScreeningChannel_ValRand2 }, - }; + [ + [ScreeningChannelRand1, ScreeningChannelValRand1], + [ScreeningChannelRand2, ScreeningChannelValRand2] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs index f8e8717273..5f9d70fb23 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs @@ -1,243 +1,243 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataPrimitives { - public static readonly byte[] UInt16_0 = { 0x00, 0x00 }; - public static readonly byte[] UInt16_1 = { 0x00, 0x01 }; - public static readonly byte[] UInt16_2 = { 0x00, 0x02 }; - public static readonly byte[] UInt16_3 = { 0x00, 0x03 }; - public static readonly byte[] UInt16_4 = { 0x00, 0x04 }; - public static readonly byte[] UInt16_5 = { 0x00, 0x05 }; - public static readonly byte[] UInt16_6 = { 0x00, 0x06 }; - public static readonly byte[] UInt16_7 = { 0x00, 0x07 }; - public static readonly byte[] UInt16_8 = { 0x00, 0x08 }; - public static readonly byte[] UInt16_9 = { 0x00, 0x09 }; - public static readonly byte[] UInt16_32768 = { 0x80, 0x00 }; - public static readonly byte[] UInt16_Max = { 0xFF, 0xFF }; - - public static readonly byte[] Int16_Min = { 0x80, 0x00 }; - public static readonly byte[] Int16_0 = { 0x00, 0x00 }; - public static readonly byte[] Int16_1 = { 0x00, 0x01 }; - public static readonly byte[] Int16_2 = { 0x00, 0x02 }; - public static readonly byte[] Int16_3 = { 0x00, 0x03 }; - public static readonly byte[] Int16_4 = { 0x00, 0x04 }; - public static readonly byte[] Int16_5 = { 0x00, 0x05 }; - public static readonly byte[] Int16_6 = { 0x00, 0x06 }; - public static readonly byte[] Int16_7 = { 0x00, 0x07 }; - public static readonly byte[] Int16_8 = { 0x00, 0x08 }; - public static readonly byte[] Int16_9 = { 0x00, 0x09 }; - public static readonly byte[] Int16_Max = { 0x7F, 0xFF }; - - public static readonly byte[] UInt32_0 = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] UInt32_1 = { 0x00, 0x00, 0x00, 0x01 }; - public static readonly byte[] UInt32_2 = { 0x00, 0x00, 0x00, 0x02 }; - public static readonly byte[] UInt32_3 = { 0x00, 0x00, 0x00, 0x03 }; - public static readonly byte[] UInt32_4 = { 0x00, 0x00, 0x00, 0x04 }; - public static readonly byte[] UInt32_5 = { 0x00, 0x00, 0x00, 0x05 }; - public static readonly byte[] UInt32_6 = { 0x00, 0x00, 0x00, 0x06 }; - public static readonly byte[] UInt32_7 = { 0x00, 0x00, 0x00, 0x07 }; - public static readonly byte[] UInt32_8 = { 0x00, 0x00, 0x00, 0x08 }; - public static readonly byte[] UInt32_9 = { 0x00, 0x00, 0x00, 0x09 }; - public static readonly byte[] UInt32_Max = { 0xFF, 0xFF, 0xFF, 0xFF }; - - public static readonly uint UInt32_ValRand1 = 1749014123; - public static readonly uint UInt32_ValRand2 = 3870560989; - public static readonly uint UInt32_ValRand3 = 1050090334; - public static readonly uint UInt32_ValRand4 = 3550252874; - - public static readonly byte[] UInt32_Rand1 = { 0x68, 0x3F, 0xD6, 0x6B }; - public static readonly byte[] UInt32_Rand2 = { 0xE6, 0xB4, 0x12, 0xDD }; - public static readonly byte[] UInt32_Rand3 = { 0x3E, 0x97, 0x1B, 0x5E }; - public static readonly byte[] UInt32_Rand4 = { 0xD3, 0x9C, 0x8F, 0x4A }; - - public static readonly byte[] Int32_Min = { 0x80, 0x00, 0x00, 0x00 }; - public static readonly byte[] Int32_0 = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Int32_1 = { 0x00, 0x00, 0x00, 0x01 }; - public static readonly byte[] Int32_2 = { 0x00, 0x00, 0x00, 0x02 }; - public static readonly byte[] Int32_3 = { 0x00, 0x00, 0x00, 0x03 }; - public static readonly byte[] Int32_4 = { 0x00, 0x00, 0x00, 0x04 }; - public static readonly byte[] Int32_5 = { 0x00, 0x00, 0x00, 0x05 }; - public static readonly byte[] Int32_6 = { 0x00, 0x00, 0x00, 0x06 }; - public static readonly byte[] Int32_7 = { 0x00, 0x00, 0x00, 0x07 }; - public static readonly byte[] Int32_8 = { 0x00, 0x00, 0x00, 0x08 }; - public static readonly byte[] Int32_9 = { 0x00, 0x00, 0x00, 0x09 }; - public static readonly byte[] Int32_Max = { 0x7F, 0xFF, 0xFF, 0xFF }; - - public static readonly byte[] UInt64_0 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] UInt64_1 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; - public static readonly byte[] UInt64_2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }; - public static readonly byte[] UInt64_3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }; - public static readonly byte[] UInt64_4 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }; - public static readonly byte[] UInt64_5 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 }; - public static readonly byte[] UInt64_6 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 }; - public static readonly byte[] UInt64_7 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 }; - public static readonly byte[] UInt64_8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; - public static readonly byte[] UInt64_9 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 }; - public static readonly byte[] UInt64_Max = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - public static readonly byte[] Int64_Min = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Int64_0 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Int64_1 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; - public static readonly byte[] Int64_2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }; - public static readonly byte[] Int64_3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }; - public static readonly byte[] Int64_4 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }; - public static readonly byte[] Int64_5 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 }; - public static readonly byte[] Int64_6 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 }; - public static readonly byte[] Int64_7 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 }; - public static readonly byte[] Int64_8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; - public static readonly byte[] Int64_9 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 }; - public static readonly byte[] Int64_Max = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - public static readonly byte[] Single_Min = { 0xFF, 0x7F, 0xFF, 0xFF }; - public static readonly byte[] Single_0 = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Single_1 = { 0x3F, 0x80, 0x00, 0x00 }; - public static readonly byte[] Single_2 = { 0x40, 0x00, 0x00, 0x00 }; - public static readonly byte[] Single_3 = { 0x40, 0x40, 0x00, 0x00 }; - public static readonly byte[] Single_4 = { 0x40, 0x80, 0x00, 0x00 }; - public static readonly byte[] Single_5 = { 0x40, 0xA0, 0x00, 0x00 }; - public static readonly byte[] Single_6 = { 0x40, 0xC0, 0x00, 0x00 }; - public static readonly byte[] Single_7 = { 0x40, 0xE0, 0x00, 0x00 }; - public static readonly byte[] Single_8 = { 0x41, 0x00, 0x00, 0x00 }; - public static readonly byte[] Single_9 = { 0x41, 0x10, 0x00, 0x00 }; - public static readonly byte[] Single_Max = { 0x7F, 0x7F, 0xFF, 0xFF }; - - public static readonly byte[] Double_Min = { 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - public static readonly byte[] Double_0 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Double_1 = { 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Double_Max = { 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - public const float Fix16_ValMin = short.MinValue; - public const float Fix16_ValMax = short.MaxValue + (65535f / 65536f); - - public static readonly byte[] Fix16_Min = { 0x80, 0x00, 0x00, 0x00 }; - public static readonly byte[] Fix16_0 = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] Fix16_1 = { 0x00, 0x01, 0x00, 0x00 }; - public static readonly byte[] Fix16_2 = { 0x00, 0x02, 0x00, 0x00 }; - public static readonly byte[] Fix16_3 = { 0x00, 0x03, 0x00, 0x00 }; - public static readonly byte[] Fix16_4 = { 0x00, 0x04, 0x00, 0x00 }; - public static readonly byte[] Fix16_5 = { 0x00, 0x05, 0x00, 0x00 }; - public static readonly byte[] Fix16_6 = { 0x00, 0x06, 0x00, 0x00 }; - public static readonly byte[] Fix16_7 = { 0x00, 0x07, 0x00, 0x00 }; - public static readonly byte[] Fix16_8 = { 0x00, 0x08, 0x00, 0x00 }; - public static readonly byte[] Fix16_9 = { 0x00, 0x09, 0x00, 0x00 }; - public static readonly byte[] Fix16_Max = { 0x7F, 0xFF, 0xFF, 0xFF }; + public static readonly byte[] UInt160 = [0x00, 0x00]; + public static readonly byte[] UInt161 = [0x00, 0x01]; + public static readonly byte[] UInt162 = [0x00, 0x02]; + public static readonly byte[] UInt163 = [0x00, 0x03]; + public static readonly byte[] UInt164 = [0x00, 0x04]; + public static readonly byte[] UInt165 = [0x00, 0x05]; + public static readonly byte[] UInt166 = [0x00, 0x06]; + public static readonly byte[] UInt167 = [0x00, 0x07]; + public static readonly byte[] UInt168 = [0x00, 0x08]; + public static readonly byte[] UInt169 = [0x00, 0x09]; + public static readonly byte[] UInt1632768 = [0x80, 0x00]; + public static readonly byte[] UInt16Max = [0xFF, 0xFF]; + + public static readonly byte[] Int16Min = [0x80, 0x00]; + public static readonly byte[] Int160 = [0x00, 0x00]; + public static readonly byte[] Int161 = [0x00, 0x01]; + public static readonly byte[] Int162 = [0x00, 0x02]; + public static readonly byte[] Int163 = [0x00, 0x03]; + public static readonly byte[] Int164 = [0x00, 0x04]; + public static readonly byte[] Int165 = [0x00, 0x05]; + public static readonly byte[] Int166 = [0x00, 0x06]; + public static readonly byte[] Int167 = [0x00, 0x07]; + public static readonly byte[] Int168 = [0x00, 0x08]; + public static readonly byte[] Int169 = [0x00, 0x09]; + public static readonly byte[] Int16Max = [0x7F, 0xFF]; + + public static readonly byte[] UInt320 = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] UInt321 = [0x00, 0x00, 0x00, 0x01]; + public static readonly byte[] UInt322 = [0x00, 0x00, 0x00, 0x02]; + public static readonly byte[] UInt323 = [0x00, 0x00, 0x00, 0x03]; + public static readonly byte[] UInt324 = [0x00, 0x00, 0x00, 0x04]; + public static readonly byte[] UInt325 = [0x00, 0x00, 0x00, 0x05]; + public static readonly byte[] UInt326 = [0x00, 0x00, 0x00, 0x06]; + public static readonly byte[] UInt327 = [0x00, 0x00, 0x00, 0x07]; + public static readonly byte[] UInt328 = [0x00, 0x00, 0x00, 0x08]; + public static readonly byte[] UInt329 = [0x00, 0x00, 0x00, 0x09]; + public static readonly byte[] UInt32Max = [0xFF, 0xFF, 0xFF, 0xFF]; + + public static readonly uint UInt32ValRand1 = 1749014123; + public static readonly uint UInt32ValRand2 = 3870560989; + public static readonly uint UInt32ValRand3 = 1050090334; + public static readonly uint UInt32ValRand4 = 3550252874; + + public static readonly byte[] UInt32Rand1 = [0x68, 0x3F, 0xD6, 0x6B]; + public static readonly byte[] UInt32Rand2 = [0xE6, 0xB4, 0x12, 0xDD]; + public static readonly byte[] UInt32Rand3 = [0x3E, 0x97, 0x1B, 0x5E]; + public static readonly byte[] UInt32Rand4 = [0xD3, 0x9C, 0x8F, 0x4A]; + + public static readonly byte[] Int32Min = [0x80, 0x00, 0x00, 0x00]; + public static readonly byte[] Int320 = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Int321 = [0x00, 0x00, 0x00, 0x01]; + public static readonly byte[] Int322 = [0x00, 0x00, 0x00, 0x02]; + public static readonly byte[] Int323 = [0x00, 0x00, 0x00, 0x03]; + public static readonly byte[] Int324 = [0x00, 0x00, 0x00, 0x04]; + public static readonly byte[] Int325 = [0x00, 0x00, 0x00, 0x05]; + public static readonly byte[] Int326 = [0x00, 0x00, 0x00, 0x06]; + public static readonly byte[] Int327 = [0x00, 0x00, 0x00, 0x07]; + public static readonly byte[] Int328 = [0x00, 0x00, 0x00, 0x08]; + public static readonly byte[] Int329 = [0x00, 0x00, 0x00, 0x09]; + public static readonly byte[] Int32Max = [0x7F, 0xFF, 0xFF, 0xFF]; + + public static readonly byte[] UInt640 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] UInt641 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + public static readonly byte[] UInt642 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]; + public static readonly byte[] UInt643 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03]; + public static readonly byte[] UInt644 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04]; + public static readonly byte[] UInt645 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05]; + public static readonly byte[] UInt646 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06]; + public static readonly byte[] UInt647 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07]; + public static readonly byte[] UInt648 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08]; + public static readonly byte[] UInt649 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09]; + public static readonly byte[] UInt64Max = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + + public static readonly byte[] Int64Min = [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Int640 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Int641 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + public static readonly byte[] Int642 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]; + public static readonly byte[] Int643 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03]; + public static readonly byte[] Int644 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04]; + public static readonly byte[] Int645 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05]; + public static readonly byte[] Int646 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06]; + public static readonly byte[] Int647 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07]; + public static readonly byte[] Int648 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08]; + public static readonly byte[] Int649 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09]; + public static readonly byte[] Int64Max = [0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + + public static readonly byte[] SingleMin = [0xFF, 0x7F, 0xFF, 0xFF]; + public static readonly byte[] Single0 = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Single1 = [0x3F, 0x80, 0x00, 0x00]; + public static readonly byte[] Single2 = [0x40, 0x00, 0x00, 0x00]; + public static readonly byte[] Single3 = [0x40, 0x40, 0x00, 0x00]; + public static readonly byte[] Single4 = [0x40, 0x80, 0x00, 0x00]; + public static readonly byte[] Single5 = [0x40, 0xA0, 0x00, 0x00]; + public static readonly byte[] Single6 = [0x40, 0xC0, 0x00, 0x00]; + public static readonly byte[] Single7 = [0x40, 0xE0, 0x00, 0x00]; + public static readonly byte[] Single8 = [0x41, 0x00, 0x00, 0x00]; + public static readonly byte[] Single9 = [0x41, 0x10, 0x00, 0x00]; + public static readonly byte[] SingleMax = [0x7F, 0x7F, 0xFF, 0xFF]; + + public static readonly byte[] DoubleMin = [0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + public static readonly byte[] Double0 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Double1 = [0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] DoubleMax = [0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + + public const float Fix16ValMin = short.MinValue; + public const float Fix16ValMax = short.MaxValue + (65535f / 65536f); + + public static readonly byte[] Fix16Min = [0x80, 0x00, 0x00, 0x00]; + public static readonly byte[] Fix160 = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] Fix161 = [0x00, 0x01, 0x00, 0x00]; + public static readonly byte[] Fix162 = [0x00, 0x02, 0x00, 0x00]; + public static readonly byte[] Fix163 = [0x00, 0x03, 0x00, 0x00]; + public static readonly byte[] Fix164 = [0x00, 0x04, 0x00, 0x00]; + public static readonly byte[] Fix165 = [0x00, 0x05, 0x00, 0x00]; + public static readonly byte[] Fix166 = [0x00, 0x06, 0x00, 0x00]; + public static readonly byte[] Fix167 = [0x00, 0x07, 0x00, 0x00]; + public static readonly byte[] Fix168 = [0x00, 0x08, 0x00, 0x00]; + public static readonly byte[] Fix169 = [0x00, 0x09, 0x00, 0x00]; + public static readonly byte[] Fix16Max = [0x7F, 0xFF, 0xFF, 0xFF]; public static readonly object[][] Fix16TestData = - { - new object[] { Fix16_Min, Fix16_ValMin }, - new object[] { Fix16_0, 0 }, - new object[] { Fix16_4, 4 }, - new object[] { Fix16_Max, Fix16_ValMax }, - }; - - public const float UFix16_ValMin = 0; - public const float UFix16_ValMax = ushort.MaxValue + (65535f / 65536f); - - public static readonly byte[] UFix16_0 = { 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte[] UFix16_1 = { 0x00, 0x01, 0x00, 0x00 }; - public static readonly byte[] UFix16_2 = { 0x00, 0x02, 0x00, 0x00 }; - public static readonly byte[] UFix16_3 = { 0x00, 0x03, 0x00, 0x00 }; - public static readonly byte[] UFix16_4 = { 0x00, 0x04, 0x00, 0x00 }; - public static readonly byte[] UFix16_5 = { 0x00, 0x05, 0x00, 0x00 }; - public static readonly byte[] UFix16_6 = { 0x00, 0x06, 0x00, 0x00 }; - public static readonly byte[] UFix16_7 = { 0x00, 0x07, 0x00, 0x00 }; - public static readonly byte[] UFix16_8 = { 0x00, 0x08, 0x00, 0x00 }; - public static readonly byte[] UFix16_9 = { 0x00, 0x09, 0x00, 0x00 }; - public static readonly byte[] UFix16_Max = { 0xFF, 0xFF, 0xFF, 0xFF }; + [ + [Fix16Min, Fix16ValMin], + [Fix160, 0], + [Fix164, 4], + [Fix16Max, Fix16ValMax] + ]; + + public const float UFix16ValMin = 0; + public const float UFix16ValMax = ushort.MaxValue + (65535f / 65536f); + + public static readonly byte[] UFix160 = [0x00, 0x00, 0x00, 0x00]; + public static readonly byte[] UFix161 = [0x00, 0x01, 0x00, 0x00]; + public static readonly byte[] UFix162 = [0x00, 0x02, 0x00, 0x00]; + public static readonly byte[] UFix163 = [0x00, 0x03, 0x00, 0x00]; + public static readonly byte[] UFix164 = [0x00, 0x04, 0x00, 0x00]; + public static readonly byte[] UFix165 = [0x00, 0x05, 0x00, 0x00]; + public static readonly byte[] UFix166 = [0x00, 0x06, 0x00, 0x00]; + public static readonly byte[] UFix167 = [0x00, 0x07, 0x00, 0x00]; + public static readonly byte[] UFix168 = [0x00, 0x08, 0x00, 0x00]; + public static readonly byte[] UFix169 = [0x00, 0x09, 0x00, 0x00]; + public static readonly byte[] UFix16Max = [0xFF, 0xFF, 0xFF, 0xFF]; public static readonly object[][] UFix16TestData = - { - new object[] { UFix16_0, 0 }, - new object[] { UFix16_4, 4 }, - new object[] { UFix16_Max, UFix16_ValMax }, - }; + [ + [UFix160, 0], + [UFix164, 4], + [UFix16Max, UFix16ValMax] + ]; - public const float U1Fix15_ValMin = 0; - public const float U1Fix15_ValMax = 1f + (32767f / 32768f); + public const float U1Fix15ValMin = 0; + public const float U1Fix15ValMax = 1f + (32767f / 32768f); - public static readonly byte[] U1Fix15_0 = { 0x00, 0x00 }; - public static readonly byte[] U1Fix15_1 = { 0x80, 0x00 }; - public static readonly byte[] U1Fix15_Max = { 0xFF, 0xFF }; + public static readonly byte[] U1Fix150 = [0x00, 0x00]; + public static readonly byte[] U1Fix151 = [0x80, 0x00]; + public static readonly byte[] U1Fix15Max = [0xFF, 0xFF]; public static readonly object[][] U1Fix15TestData = - { - new object[] { U1Fix15_0, 0 }, - new object[] { U1Fix15_1, 1 }, - new object[] { U1Fix15_Max, U1Fix15_ValMax }, - }; - - public const float UFix8_ValMin = 0; - public const float UFix8_ValMax = byte.MaxValue + (255f / 256f); - - public static readonly byte[] UFix8_0 = { 0x00, 0x00 }; - public static readonly byte[] UFix8_1 = { 0x01, 0x00 }; - public static readonly byte[] UFix8_2 = { 0x02, 0x00 }; - public static readonly byte[] UFix8_3 = { 0x03, 0x00 }; - public static readonly byte[] UFix8_4 = { 0x04, 0x00 }; - public static readonly byte[] UFix8_5 = { 0x05, 0x00 }; - public static readonly byte[] UFix8_6 = { 0x06, 0x00 }; - public static readonly byte[] UFix8_7 = { 0x07, 0x00 }; - public static readonly byte[] UFix8_8 = { 0x08, 0x00 }; - public static readonly byte[] UFix8_9 = { 0x09, 0x00 }; - public static readonly byte[] UFix8_Max = { 0xFF, 0xFF }; + [ + [U1Fix150, 0], + [U1Fix151, 1], + [U1Fix15Max, U1Fix15ValMax] + ]; + + public const float UFix8ValMin = 0; + public const float UFix8ValMax = byte.MaxValue + (255f / 256f); + + public static readonly byte[] UFix80 = [0x00, 0x00]; + public static readonly byte[] UFix81 = [0x01, 0x00]; + public static readonly byte[] UFix82 = [0x02, 0x00]; + public static readonly byte[] UFix83 = [0x03, 0x00]; + public static readonly byte[] UFix84 = [0x04, 0x00]; + public static readonly byte[] UFix85 = [0x05, 0x00]; + public static readonly byte[] UFix86 = [0x06, 0x00]; + public static readonly byte[] UFix87 = [0x07, 0x00]; + public static readonly byte[] UFix88 = [0x08, 0x00]; + public static readonly byte[] UFix89 = [0x09, 0x00]; + public static readonly byte[] UFix8Max = [0xFF, 0xFF]; public static readonly object[][] UFix8TestData = - { - new object[] { UFix8_0, 0 }, - new object[] { UFix8_4, 4 }, - new object[] { UFix8_Max, UFix8_ValMax }, - }; - - public const string Ascii_ValRand = "aBcdEf1234"; - public const string Ascii_ValRand1 = "Ecf3a"; - public const string Ascii_ValRand2 = "2Bd4c"; - public const string Ascii_ValRand3 = "cad14"; - public const string Ascii_ValRand4 = "fd4E1"; - public const string Ascii_ValRandLength4 = "aBcd"; - public const string Ascii_ValNullRand = "aBcd\0Ef\0123"; - - public static readonly byte[] Ascii_Rand = { 97, 66, 99, 100, 69, 102, 49, 50, 51, 52 }; - public static readonly byte[] Ascii_Rand1 = { 69, 99, 102, 51, 97 }; - public static readonly byte[] Ascii_Rand2 = { 50, 66, 100, 52, 99 }; - public static readonly byte[] Ascii_Rand3 = { 99, 97, 100, 49, 52 }; - public static readonly byte[] Ascii_Rand4 = { 102, 100, 52, 69, 49 }; - public static readonly byte[] Ascii_RandLength4 = { 97, 66, 99, 100 }; - public static readonly byte[] Ascii_PaddedRand = { 97, 66, 99, 100, 69, 102, 49, 50, 51, 52, 0, 0, 0, 0 }; - public static readonly byte[] Ascii_NullRand = { 97, 66, 99, 100, 0, 69, 102, 0, 49, 50, 51 }; - - public const int Ascii_Rand_Length = 10; - public const int Ascii_PaddedRand_Length = 14; - public const int Ascii_NullRand_Length = 11; - public const int Ascii_NullRand_LengthNoNull = 4; + [ + [UFix80, 0], + [UFix84, 4], + [UFix8Max, UFix8ValMax] + ]; + + public const string AsciiValRand = "aBcdEf1234"; + public const string AsciiValRand1 = "Ecf3a"; + public const string AsciiValRand2 = "2Bd4c"; + public const string AsciiValRand3 = "cad14"; + public const string AsciiValRand4 = "fd4E1"; + public const string AsciiValRandLength4 = "aBcd"; + public const string AsciiValNullRand = "aBcd\0Ef\0123"; + + public static readonly byte[] AsciiRand = [97, 66, 99, 100, 69, 102, 49, 50, 51, 52]; + public static readonly byte[] AsciiRand1 = [69, 99, 102, 51, 97]; + public static readonly byte[] AsciiRand2 = [50, 66, 100, 52, 99]; + public static readonly byte[] AsciiRand3 = [99, 97, 100, 49, 52]; + public static readonly byte[] AsciiRand4 = [102, 100, 52, 69, 49]; + public static readonly byte[] AsciiRandLength4 = [97, 66, 99, 100]; + public static readonly byte[] AsciiPaddedRand = [97, 66, 99, 100, 69, 102, 49, 50, 51, 52, 0, 0, 0, 0]; + public static readonly byte[] AsciiNullRand = [97, 66, 99, 100, 0, 69, 102, 0, 49, 50, 51]; + + public const int AsciiRandLength = 10; + public const int AsciiPaddedRandLength = 14; + public const int AsciiNullRandLength = 11; + public const int AsciiNullRandLengthNoNull = 4; public static readonly object[][] AsciiTestData = - { - new object[] { Ascii_Rand, Ascii_Rand_Length, Ascii_ValRand }, - new object[] { Ascii_Rand, 4, Ascii_ValRandLength4 }, - new object[] { Ascii_NullRand, Ascii_NullRand_LengthNoNull, Ascii_ValRandLength4 }, - }; + [ + [AsciiRand, AsciiRandLength, AsciiValRand], + [AsciiRand, 4, AsciiValRandLength4], + [AsciiNullRand, AsciiNullRandLengthNoNull, AsciiValRandLength4] + ]; public static readonly object[][] AsciiWriteTestData = - { - new object[] { Ascii_Rand, Ascii_ValRand }, - new object[] { Ascii_NullRand, Ascii_ValNullRand }, - }; + [ + [AsciiRand, AsciiValRand], + [AsciiNullRand, AsciiValNullRand] + ]; public static readonly object[][] AsciiPaddingTestData = - { - new object[] { Ascii_PaddedRand, Ascii_PaddedRand_Length, Ascii_ValRand, true }, - new object[] { Ascii_RandLength4, 4, Ascii_ValRand, false }, - }; + [ + [AsciiPaddedRand, AsciiPaddedRandLength, AsciiValRand, true], + [AsciiRandLength4, 4, AsciiValRand, false] + ]; - public const string Unicode_ValRand1 = ".6Abäñ$€β𐐷𤭢"; - public const string Unicode_ValRand2 = ".6Abäñ"; - public const string Unicode_ValRand3 = "$€β𐐷𤭢"; + public const string UnicodeValRand1 = ".6Abäñ$€β𐐷𤭢"; + public const string UnicodeValRand2 = ".6Abäñ"; + public const string UnicodeValRand3 = "$€β𐐷𤭢"; - public static readonly byte[] Unicode_Rand1 = - { + public static readonly byte[] UnicodeRand1 = + [ 0x00, 0x2e, // . 0x00, 0x36, // 6 0x00, 0x41, // A @@ -248,25 +248,25 @@ internal static class IccTestDataPrimitives 0x20, 0xAC, // € 0x03, 0xb2, // β 0xD8, 0x01, 0xDC, 0x37, // 𐐷 - 0xD8, 0x52, 0xDF, 0x62, // 𤭢 - }; + 0xD8, 0x52, 0xDF, 0x62 // 𤭢 + ]; - public static readonly byte[] Unicode_Rand2 = - { + public static readonly byte[] UnicodeRand2 = + [ 0x00, 0x2e, // . 0x00, 0x36, // 6 0x00, 0x41, // A 0x00, 0x62, // b 0x00, 0xe4, // ä - 0x00, 0xf1, // ñ - }; + 0x00, 0xf1 // ñ + ]; - public static readonly byte[] Unicode_Rand3 = - { + public static readonly byte[] UnicodeRand3 = + [ 0x00, 0x24, // $ 0x20, 0xAC, // € 0x03, 0xb2, // β 0xD8, 0x01, 0xDC, 0x37, // 𐐷 - 0xD8, 0x52, 0xDF, 0x62, // 𤭢 - }; + 0xD8, 0x52, 0xDF, 0x62 // 𤭢 + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 8fd6f7e4ae..6e097a0bea 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -4,59 +4,54 @@ using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataProfiles { - public static readonly IccProfileId Header_Random_Id_Value = new IccProfileId(0x84A8D460, 0xC716B6F3, 0x9B0E4C3D, 0xAB95F838); - public static readonly IccProfileId Profile_Random_Id_Value = new IccProfileId(0x917D6DE6, 0x84C958D1, 0x3BB0F5BB, 0xADD1134F); + public static readonly IccProfileId HeaderRandomIdValue = new(0x84A8D460, 0xC716B6F3, 0x9B0E4C3D, 0xAB95F838); + public static readonly IccProfileId ProfileRandomIdValue = new(0x917D6DE6, 0x84C958D1, 0x3BB0F5BB, 0xADD1134F); - public static readonly byte[] Header_Random_Id_Array = - { - 0x84, 0xA8, 0xD4, 0x60, 0xC7, 0x16, 0xB6, 0xF3, 0x9B, 0x0E, 0x4C, 0x3D, 0xAB, 0x95, 0xF8, 0x38, - }; + public static readonly byte[] HeaderRandomIdArray = + [ + 0x84, 0xA8, 0xD4, 0x60, 0xC7, 0x16, 0xB6, 0xF3, 0x9B, 0x0E, 0x4C, 0x3D, 0xAB, 0x95, 0xF8, 0x38 + ]; - public static readonly byte[] Profile_Random_Id_Array = - { - 0x91, 0x7D, 0x6D, 0xE6, 0x84, 0xC9, 0x58, 0xD1, 0x3B, 0xB0, 0xF5, 0xBB, 0xAD, 0xD1, 0x13, 0x4F, - }; + public static readonly byte[] ProfileRandomIdArray = + [ + 0x91, 0x7D, 0x6D, 0xE6, 0x84, 0xC9, 0x58, 0xD1, 0x3B, 0xB0, 0xF5, 0xBB, 0xAD, 0xD1, 0x13, 0x4F + ]; - public static readonly IccProfileHeader Header_Random_Write = CreateHeaderRandomValue( + public static readonly IccProfileHeader HeaderRandomWrite = CreateHeaderRandomValue( 562, // should be overwritten new IccProfileId(1, 2, 3, 4), // should be overwritten "ijkl"); // should be overwritten to "acsp" - public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132, Header_Random_Id_Value, "acsp"); + public static readonly IccProfileHeader HeaderRandomRead = CreateHeaderRandomValue(132, HeaderRandomIdValue, "acsp"); - public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, Header_Random_Id_Array); + public static readonly byte[] HeaderRandomArray = CreateHeaderRandomArray(132, 0, HeaderRandomIdArray); - public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature) + public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature) => new() { - return new IccProfileHeader - { - Class = IccProfileClass.DisplayDevice, - CmmType = "abcd", - CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), - CreatorSignature = "dcba", - DataColorSpace = IccColorSpaceType.Rgb, - DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, - DeviceManufacturer = 123456789u, - DeviceModel = 987654321u, - FileSignature = "acsp", - Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, - Id = id, - PcsIlluminant = new Vector3(4, 5, 6), - PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, - ProfileConnectionSpace = IccColorSpaceType.CieXyz, - RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, - Size = size, - Version = new IccVersion(4, 3, 0), - }; - } - - public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] profileId) - { - return ArrayHelper.Concat( + Class = IccProfileClass.DisplayDevice, + CmmType = "abcd", + CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), + CreatorSignature = "dcba", + DataColorSpace = IccColorSpaceType.Rgb, + DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, + DeviceManufacturer = 123456789u, + DeviceModel = 987654321u, + FileSignature = "acsp", + Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, + Id = id, + PcsIlluminant = new Vector3(4, 5, 6), + PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, + ProfileConnectionSpace = IccColorSpaceType.CieXyz, + RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, + Size = size, + Version = new IccVersion(4, 3, 0), + }; + + public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] profileId) => ArrayHelper.Concat( new byte[] { (byte)(size >> 24), (byte)(size >> 16), (byte)(size >> 8), (byte)size, // Size @@ -91,10 +86,9 @@ public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] (byte)(nrOfEntries >> 8), (byte)nrOfEntries }); - } - public static readonly byte[] Profile_Random_Array = ArrayHelper.Concat( - CreateHeaderRandomArray(168, 2, Profile_Random_Id_Array), + public static readonly byte[] ProfileRandomArray = ArrayHelper.Concat( + CreateHeaderRandomArray(168, 2, ProfileRandomIdArray), new byte[] { 0x00, 0x00, 0x00, 0x00, // tag signature (Unknown) @@ -104,18 +98,18 @@ public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] 0x00, 0x00, 0x00, 0x9C, // tag offset (156) 0x00, 0x00, 0x00, 0x0C, // tag size (12) }, - IccTestDataTagDataEntry.TagDataEntryHeader_UnknownArr, - IccTestDataTagDataEntry.Unknown_Arr); + IccTestDataTagDataEntry.TagDataEntryHeaderUnknownArr, + IccTestDataTagDataEntry.UnknownArr); - public static readonly IccProfile Profile_Random_Val = new IccProfile( + public static readonly IccProfile ProfileRandomVal = new( CreateHeaderRandomValue( 168, - Profile_Random_Id_Value, + ProfileRandomIdValue, "acsp"), - new IccTagDataEntry[] { IccTestDataTagDataEntry.Unknown_Val, IccTestDataTagDataEntry.Unknown_Val }); + [IccTestDataTagDataEntry.UnknownVal, IccTestDataTagDataEntry.UnknownVal]); - public static readonly byte[] Header_CorruptDataColorSpace_Array = - { + public static readonly byte[] HeaderCorruptDataColorSpaceArray = + [ 0x00, 0x00, 0x00, 0x80, // Size 0x61, 0x62, 0x63, 0x64, // CmmType 0x04, 0x30, 0x00, 0x00, // Version @@ -140,11 +134,11 @@ public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly byte[] Header_CorruptProfileConnectionSpace_Array = - { + public static readonly byte[] HeaderCorruptProfileConnectionSpaceArray = + [ 0x00, 0x00, 0x00, 0x80, // Size 0x62, 0x63, 0x64, 0x65, // CmmType 0x04, 0x30, 0x00, 0x00, // Version @@ -169,11 +163,11 @@ public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly byte[] Header_CorruptRenderingIntent_Array = - { + public static readonly byte[] HeaderCorruptRenderingIntentArray = + [ 0x00, 0x00, 0x00, 0x80, // Size 0x63, 0x64, 0x65, 0x66, // CmmType 0x04, 0x30, 0x00, 0x00, // Version @@ -198,32 +192,32 @@ public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly byte[] Header_DataTooSmall_Array = new byte[127]; + public static readonly byte[] HeaderDataTooSmallArray = new byte[127]; - public static readonly byte[] Header_InvalidSizeSmall_Array = CreateHeaderRandomArray(127, 0, Header_Random_Id_Array); + public static readonly byte[] HeaderInvalidSizeSmallArray = CreateHeaderRandomArray(127, 0, HeaderRandomIdArray); - public static readonly byte[] Header_InvalidSizeBig_Array = CreateHeaderRandomArray(50_000_000, 0, Header_Random_Id_Array); + public static readonly byte[] HeaderInvalidSizeBigArray = CreateHeaderRandomArray(50_000_000, 0, HeaderRandomIdArray); - public static readonly byte[] Header_SizeBiggerThanData_Array = CreateHeaderRandomArray(160, 0, Header_Random_Id_Array); + public static readonly byte[] HeaderSizeBiggerThanDataArray = CreateHeaderRandomArray(160, 0, HeaderRandomIdArray); public static readonly object[][] ProfileIdTestData = - { - new object[] { Header_Random_Array, Header_Random_Id_Value }, - new object[] { Profile_Random_Array, Profile_Random_Id_Value }, - }; + [ + [HeaderRandomArray, HeaderRandomIdValue], + [ProfileRandomArray, ProfileRandomIdValue] + ]; public static readonly object[][] ProfileValidityTestData = - { - new object[] { Header_CorruptDataColorSpace_Array, false }, - new object[] { Header_CorruptProfileConnectionSpace_Array, false }, - new object[] { Header_CorruptRenderingIntent_Array, false }, - new object[] { Header_DataTooSmall_Array, false }, - new object[] { Header_InvalidSizeSmall_Array, false }, - new object[] { Header_InvalidSizeBig_Array, false }, - new object[] { Header_SizeBiggerThanData_Array, false }, - new object[] { Header_Random_Array, true }, - }; + [ + [HeaderCorruptDataColorSpaceArray, false], + [HeaderCorruptProfileConnectionSpaceArray, false], + [HeaderCorruptRenderingIntentArray, false], + [HeaderDataTooSmallArray, false], + [HeaderInvalidSizeSmallArray, false], + [HeaderInvalidSizeBigArray, false], + [HeaderSizeBiggerThanDataArray, false], + [HeaderRandomArray, true] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs index 69d375cedf..979b0044dc 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs @@ -2,54 +2,53 @@ // Licensed under the Six Labors Split License. using System.Globalization; -using System.Numerics; using SixLabors.ImageSharp.Metadata.Profiles.Icc; -namespace SixLabors.ImageSharp.Tests; +namespace SixLabors.ImageSharp.Tests.TestDataIcc; internal static class IccTestDataTagDataEntry { - public static readonly IccTypeSignature TagDataEntryHeader_UnknownVal = IccTypeSignature.Unknown; - public static readonly byte[] TagDataEntryHeader_UnknownArr = - { - 0x00, 0x00, 0x00, 0x00, + public static readonly IccTypeSignature TagDataEntryHeaderUnknownVal = IccTypeSignature.Unknown; + public static readonly byte[] TagDataEntryHeaderUnknownArr = + [ 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly IccTypeSignature TagDataEntryHeader_MultiLocalizedUnicodeVal = IccTypeSignature.MultiLocalizedUnicode; - public static readonly byte[] TagDataEntryHeader_MultiLocalizedUnicodeArr = - { + public static readonly IccTypeSignature TagDataEntryHeaderMultiLocalizedUnicodeVal = IccTypeSignature.MultiLocalizedUnicode; + public static readonly byte[] TagDataEntryHeaderMultiLocalizedUnicodeArr = + [ 0x6D, 0x6C, 0x75, 0x63, - 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; - public static readonly IccTypeSignature TagDataEntryHeader_CurveVal = IccTypeSignature.Curve; - public static readonly byte[] TagDataEntryHeader_CurveArr = - { + public static readonly IccTypeSignature TagDataEntryHeaderCurveVal = IccTypeSignature.Curve; + public static readonly byte[] TagDataEntryHeaderCurveArr = + [ 0x63, 0x75, 0x72, 0x76, - 0x00, 0x00, 0x00, 0x00, - }; + 0x00, 0x00, 0x00, 0x00 + ]; public static readonly object[][] TagDataEntryHeaderTestData = - { - new object[] { TagDataEntryHeader_UnknownArr, TagDataEntryHeader_UnknownVal }, - new object[] { TagDataEntryHeader_MultiLocalizedUnicodeArr, TagDataEntryHeader_MultiLocalizedUnicodeVal }, - new object[] { TagDataEntryHeader_CurveArr, TagDataEntryHeader_CurveVal }, - }; + [ + [TagDataEntryHeaderUnknownArr, TagDataEntryHeaderUnknownVal], + [TagDataEntryHeaderMultiLocalizedUnicodeArr, TagDataEntryHeaderMultiLocalizedUnicodeVal], + [TagDataEntryHeaderCurveArr, TagDataEntryHeaderCurveVal] + ]; - public static readonly IccUnknownTagDataEntry Unknown_Val = new(new byte[] { 0x00, 0x01, 0x02, 0x03 }); + public static readonly IccUnknownTagDataEntry UnknownVal = new([0x00, 0x01, 0x02, 0x03]); - public static readonly byte[] Unknown_Arr = { 0x00, 0x01, 0x02, 0x03 }; + public static readonly byte[] UnknownArr = [0x00, 0x01, 0x02, 0x03]; public static readonly object[][] UnknownTagDataEntryTestData = - { - new object[] { Unknown_Arr, Unknown_Val, 12u }, - }; - - public static readonly IccChromaticityTagDataEntry Chromaticity_Val1 = new(IccColorantEncoding.ItuRBt709_2); - public static readonly byte[] Chromaticity_Arr1 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_1, + [ + [UnknownArr, UnknownVal, 12u] + ]; + + public static readonly IccChromaticityTagDataEntry ChromaticityVal1 = new(IccColorantEncoding.ItuRBt709_2); + public static readonly byte[] ChromaticityArr1 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt161, new byte[] { 0x00, 0x00, 0xA3, 0xD7 }, // 0.640 new byte[] { 0x00, 0x00, 0x54, 0x7B }, // 0.330 new byte[] { 0x00, 0x00, 0x4C, 0xCD }, // 0.300 @@ -57,190 +56,175 @@ internal static class IccTestDataTagDataEntry new byte[] { 0x00, 0x00, 0x26, 0x66 }, // 0.150 new byte[] { 0x00, 0x00, 0x0F, 0x5C }); // 0.060 - public static readonly IccChromaticityTagDataEntry Chromaticity_Val2 = new( - new double[][] - { - new double[] { 1, 2 }, - new double[] { 3, 4 }, - }); - - public static readonly byte[] Chromaticity_Arr2 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_0, - IccTestDataPrimitives.UFix16_1, - IccTestDataPrimitives.UFix16_2, - IccTestDataPrimitives.UFix16_3, - IccTestDataPrimitives.UFix16_4); + public static readonly IccChromaticityTagDataEntry ChromaticityVal2 = new( + [ + [1, 2], + [3, 4] + ]); + + public static readonly byte[] ChromaticityArr2 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt160, + IccTestDataPrimitives.UFix161, + IccTestDataPrimitives.UFix162, + IccTestDataPrimitives.UFix163, + IccTestDataPrimitives.UFix164); /// /// : channel count must be 3 for any enum other than /// - public static readonly byte[] Chromaticity_ArrInvalid1 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_5, - IccTestDataPrimitives.UInt16_1); + public static readonly byte[] ChromaticityArrInvalid1 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt165, + IccTestDataPrimitives.UInt161); /// /// : invalid enum value /// - public static readonly byte[] Chromaticity_ArrInvalid2 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_9); + public static readonly byte[] ChromaticityArrInvalid2 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt169); public static readonly object[][] ChromaticityTagDataEntryTestData = - { - new object[] { Chromaticity_Arr1, Chromaticity_Val1 }, - new object[] { Chromaticity_Arr2, Chromaticity_Val2 }, - }; + [ + [ChromaticityArr1, ChromaticityVal1], + [ChromaticityArr2, ChromaticityVal2] + ]; - public static readonly IccColorantOrderTagDataEntry ColorantOrder_Val = new(new byte[] { 0x00, 0x01, 0x02 }); - public static readonly byte[] ColorantOrder_Arr = ArrayHelper.Concat(IccTestDataPrimitives.UInt32_3, new byte[] { 0x00, 0x01, 0x02 }); + public static readonly IccColorantOrderTagDataEntry ColorantOrderVal = new([0x00, 0x01, 0x02]); + public static readonly byte[] ColorantOrderArr = ArrayHelper.Concat(IccTestDataPrimitives.UInt323, new byte[] { 0x00, 0x01, 0x02 }); public static readonly object[][] ColorantOrderTagDataEntryTestData = - { - new object[] { ColorantOrder_Arr, ColorantOrder_Val }, - }; + [ + [ColorantOrderArr, ColorantOrderVal] + ]; - public static readonly IccColorantTableTagDataEntry ColorantTable_Val = new( - new IccColorantTableEntry[] - { - IccTestDataNonPrimitives.ColorantTableEntry_ValRand1, - IccTestDataNonPrimitives.ColorantTableEntry_ValRand2 - }); + public static readonly IccColorantTableTagDataEntry ColorantTableVal = new( + [ + IccTestDataNonPrimitives.ColorantTableEntryValRand1, + IccTestDataNonPrimitives.ColorantTableEntryValRand2 + ]); - public static readonly byte[] ColorantTable_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, - IccTestDataNonPrimitives.ColorantTableEntry_Rand1, - IccTestDataNonPrimitives.ColorantTableEntry_Rand2); + public static readonly byte[] ColorantTableArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, + IccTestDataNonPrimitives.ColorantTableEntryRand1, + IccTestDataNonPrimitives.ColorantTableEntryRand2); public static readonly object[][] ColorantTableTagDataEntryTestData = - { - new object[] { ColorantTable_Arr, ColorantTable_Val }, - }; + [ + [ColorantTableArr, ColorantTableVal] + ]; - public static readonly IccCurveTagDataEntry Curve_Val_0 = new(); - public static readonly byte[] Curve_Arr_0 = IccTestDataPrimitives.UInt32_0; + public static readonly IccCurveTagDataEntry CurveVal0 = new(); + public static readonly byte[] CurveArr0 = IccTestDataPrimitives.UInt320; - public static readonly IccCurveTagDataEntry Curve_Val_1 = new(1f); - public static readonly byte[] Curve_Arr_1 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_1, - IccTestDataPrimitives.UFix8_1); + public static readonly IccCurveTagDataEntry CurveVal1 = new(1f); + public static readonly byte[] CurveArr1 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt321, + IccTestDataPrimitives.UFix81); - public static readonly IccCurveTagDataEntry Curve_Val_2 = new(new float[] { 1 / 65535f, 2 / 65535f, 3 / 65535f }); - public static readonly byte[] Curve_Arr_2 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_3, - IccTestDataPrimitives.UInt16_1, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3); + public static readonly IccCurveTagDataEntry CurveVal2 = new([1 / 65535f, 2 / 65535f, 3 / 65535f]); + public static readonly byte[] CurveArr2 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt323, + IccTestDataPrimitives.UInt161, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163); public static readonly object[][] CurveTagDataEntryTestData = - { - new object[] { Curve_Arr_0, Curve_Val_0 }, - new object[] { Curve_Arr_1, Curve_Val_1 }, - new object[] { Curve_Arr_2, Curve_Val_2 }, - }; + [ + [CurveArr0, CurveVal0], + [CurveArr1, CurveVal1], + [CurveArr2, CurveVal2] + ]; - public static readonly IccDataTagDataEntry Data_ValNoASCII = new( - new byte[] { 0x01, 0x02, 0x03, 0x04 }, - false); + public static readonly IccDataTagDataEntry DataValNoAscii = new([0x01, 0x02, 0x03, 0x04], false); - public static readonly byte[] Data_ArrNoASCII = - { + public static readonly byte[] DataArrNoAscii = + [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04 - }; + ]; - public static readonly IccDataTagDataEntry Data_ValASCII = new( - new byte[] { (byte)'A', (byte)'S', (byte)'C', (byte)'I', (byte)'I' }, - true); + public static readonly IccDataTagDataEntry DataValAscii = new([(byte)'A', (byte)'S', (byte)'C', (byte)'I', (byte)'I' + ], true); - public static readonly byte[] Data_ArrASCII = - { + public static readonly byte[] DataArrAscii = + [ 0x00, 0x00, 0x00, 0x01, (byte)'A', (byte)'S', (byte)'C', (byte)'I', (byte)'I' - }; + ]; public static readonly object[][] DataTagDataEntryTestData = - { - new object[] { Data_ArrNoASCII, Data_ValNoASCII, 16u }, - new object[] { Data_ArrASCII, Data_ValASCII, 17u }, - }; + [ + [DataArrNoAscii, DataValNoAscii, 16u], + [DataArrAscii, DataValAscii, 17u] + ]; - public static readonly IccDateTimeTagDataEntry DateTime_Val = new(IccTestDataNonPrimitives.DateTime_ValRand1); - public static readonly byte[] DateTime_Arr = IccTestDataNonPrimitives.DateTime_Rand1; + public static readonly IccDateTimeTagDataEntry DateTimeVal = new(IccTestDataNonPrimitives.DateTimeValRand1); + public static readonly byte[] DateTimeArr = IccTestDataNonPrimitives.DateTimeRand1; public static readonly object[][] DateTimeTagDataEntryTestData = - { - new object[] { DateTime_Arr, DateTime_Val }, - }; + [ + [DateTimeArr, DateTimeVal] + ]; - public static readonly IccLut16TagDataEntry Lut16_Val = new( - new IccLut[] { IccTestDataLut.LUT16_ValGrad, IccTestDataLut.LUT16_ValGrad }, - IccTestDataLut.CLUT16_ValGrad, - new IccLut[] { IccTestDataLut.LUT16_ValGrad, IccTestDataLut.LUT16_ValGrad, IccTestDataLut.LUT16_ValGrad }); + public static readonly IccLut16TagDataEntry Lut16Val = new( + [IccTestDataLut.Lut16ValGrad, IccTestDataLut.Lut16ValGrad], + IccTestDataLut.Clut16ValGrad, + [IccTestDataLut.Lut16ValGrad, IccTestDataLut.Lut16ValGrad, IccTestDataLut.Lut16ValGrad]); - public static readonly byte[] Lut16_Arr = ArrayHelper.Concat( + public static readonly byte[] Lut16Arr = ArrayHelper.Concat( new byte[] { 0x02, 0x03, 0x03, 0x00 }, - IccTestDataMatrix.Fix16_2D_Identity, - new byte[] { 0x00, (byte)IccTestDataLut.LUT16_ValGrad.Values.Length, 0x00, (byte)IccTestDataLut.LUT16_ValGrad.Values.Length }, - IccTestDataLut.LUT16_Grad, - IccTestDataLut.LUT16_Grad, - IccTestDataLut.CLUT16_Grad, - IccTestDataLut.LUT16_Grad, - IccTestDataLut.LUT16_Grad, - IccTestDataLut.LUT16_Grad); + IccTestDataMatrix.Fix162DIdentity, + new byte[] { 0x00, (byte)IccTestDataLut.Lut16ValGrad.Values.Length, 0x00, (byte)IccTestDataLut.Lut16ValGrad.Values.Length }, + IccTestDataLut.Lut16Grad, + IccTestDataLut.Lut16Grad, + IccTestDataLut.Clut16Grad, + IccTestDataLut.Lut16Grad, + IccTestDataLut.Lut16Grad, + IccTestDataLut.Lut16Grad); public static readonly object[][] Lut16TagDataEntryTestData = - { - new object[] { Lut16_Arr, Lut16_Val }, - }; + [ + [Lut16Arr, Lut16Val] + ]; - public static readonly IccLut8TagDataEntry Lut8_Val = new( - new IccLut[] { IccTestDataLut.LUT8_ValGrad, IccTestDataLut.LUT8_ValGrad }, - IccTestDataLut.CLUT8_ValGrad, - new IccLut[] { IccTestDataLut.LUT8_ValGrad, IccTestDataLut.LUT8_ValGrad, IccTestDataLut.LUT8_ValGrad }); + public static readonly IccLut8TagDataEntry Lut8Val = new( + [IccTestDataLut.Lut8ValGrad, IccTestDataLut.Lut8ValGrad], + IccTestDataLut.Clut8ValGrad, + [IccTestDataLut.Lut8ValGrad, IccTestDataLut.Lut8ValGrad, IccTestDataLut.Lut8ValGrad]); - public static readonly byte[] Lut8_Arr = ArrayHelper.Concat( + public static readonly byte[] Lut8Arr = ArrayHelper.Concat( new byte[] { 0x02, 0x03, 0x03, 0x00 }, - IccTestDataMatrix.Fix16_2D_Identity, - IccTestDataLut.LUT8_Grad, - IccTestDataLut.LUT8_Grad, - IccTestDataLut.CLUT8_Grad, - IccTestDataLut.LUT8_Grad, - IccTestDataLut.LUT8_Grad, - IccTestDataLut.LUT8_Grad); - - public static readonly object[][] Lut8TagDataEntryTestData = - { - new object[] { Lut8_Arr, Lut8_Val }, - }; + IccTestDataMatrix.Fix162DIdentity, + IccTestDataLut.Lut8Grad, + IccTestDataLut.Lut8Grad, + IccTestDataLut.Clut8Grad, + IccTestDataLut.Lut8Grad, + IccTestDataLut.Lut8Grad, + IccTestDataLut.Lut8Grad); - private static readonly byte[] CurveFull_0 = ArrayHelper.Concat( - TagDataEntryHeader_CurveArr, - Curve_Arr_0); + public static readonly object[][] Lut8TagDataEntryTestData = [[Lut8Arr, Lut8Val]]; - private static readonly byte[] CurveFull_1 = ArrayHelper.Concat( - TagDataEntryHeader_CurveArr, - Curve_Arr_1); + private static readonly byte[] CurveFull0 = ArrayHelper.Concat(TagDataEntryHeaderCurveArr, CurveArr0); - private static readonly byte[] CurveFull_2 = ArrayHelper.Concat( - TagDataEntryHeader_CurveArr, - Curve_Arr_2); + private static readonly byte[] CurveFull1 = ArrayHelper.Concat(TagDataEntryHeaderCurveArr, CurveArr1); - public static readonly IccLutAToBTagDataEntry LutAToB_Val + private static readonly byte[] CurveFull2 = ArrayHelper.Concat(TagDataEntryHeaderCurveArr, CurveArr2); + + public static readonly IccLutAToBTagDataEntry LutAToBVal = new( - new IccCurveTagDataEntry[] - { - Curve_Val_0, - Curve_Val_1, - Curve_Val_2, - }, - IccTestDataMatrix.Single_2DArray_ValGrad, - IccTestDataMatrix.Single_1DArray_ValGrad, - new IccCurveTagDataEntry[] { Curve_Val_1, Curve_Val_2, Curve_Val_0 }, - IccTestDataLut.CLUT_Val16, - new IccCurveTagDataEntry[] { Curve_Val_2, Curve_Val_1 }); - - public static readonly byte[] LutAToB_Arr = ArrayHelper.Concat( + [ + CurveVal0, + CurveVal1, + CurveVal2 + ], + IccTestDataMatrix.Single2DArrayValGrad, + IccTestDataMatrix.Single1DArrayValGrad, + [CurveVal1, CurveVal2, CurveVal0], + IccTestDataLut.ClutVal16, + [CurveVal2, CurveVal1]); + + public static readonly byte[] LutAToBArr = ArrayHelper.Concat( new byte[] { 0x02, 0x03, 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x20 }, // b: 32 new byte[] { 0x00, 0x00, 0x00, 0x50 }, // matrix: 80 @@ -249,51 +233,47 @@ public static readonly IccLutAToBTagDataEntry LutAToB_Val new byte[] { 0x00, 0x00, 0x00, 0xFC }, // a: 252 // B - CurveFull_0, // 12 bytes - CurveFull_1, // 14 bytes + CurveFull0, // 12 bytes + CurveFull1, // 14 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_2, // 18 bytes + CurveFull2, // 18 bytes new byte[] { 0x00, 0x00 }, // Padding // Matrix - IccTestDataMatrix.Fix16_2D_Grad, // 36 bytes - IccTestDataMatrix.Fix16_1D_Grad, // 12 bytes + IccTestDataMatrix.Fix162DGrad, // 36 bytes + IccTestDataMatrix.Fix161DGrad, // 12 bytes // M - CurveFull_1, // 14 bytes + CurveFull1, // 14 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_2, // 18 bytes + CurveFull2, // 18 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_0, // 12 bytes + CurveFull0, // 12 bytes // CLUT - IccTestDataLut.CLUT_16, // 74 bytes + IccTestDataLut.Clut16, // 74 bytes new byte[] { 0x00, 0x00 }, // Padding // A - CurveFull_2, // 18 bytes + CurveFull2, // 18 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_1, // 14 bytes + CurveFull1, // 14 bytes new byte[] { 0x00, 0x00 }); // Padding - public static readonly object[][] LutAToBTagDataEntryTestData = - { - new object[] { LutAToB_Arr, LutAToB_Val }, - }; + public static readonly object[][] LutAToBTagDataEntryTestData = [[LutAToBArr, LutAToBVal]]; - public static readonly IccLutBToATagDataEntry LutBToA_Val = new( - new[] - { - Curve_Val_0, - Curve_Val_1, - }, + public static readonly IccLutBToATagDataEntry LutBToAVal = new( + [ + CurveVal0, + CurveVal1 + ], null, null, null, - IccTestDataLut.CLUT_Val16, - new[] { Curve_Val_2, Curve_Val_1, Curve_Val_0 }); + IccTestDataLut.ClutVal16, + [CurveVal2, CurveVal1, CurveVal0]); - public static readonly byte[] LutBToA_Arr = ArrayHelper.Concat( + public static readonly byte[] LutBToAArr = ArrayHelper.Concat( new byte[] { 0x02, 0x03, 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x20 }, // b: 32 new byte[] { 0x00, 0x00, 0x00, 0x00 }, // matrix: 0 @@ -302,52 +282,52 @@ public static readonly IccLutAToBTagDataEntry LutAToB_Val new byte[] { 0x00, 0x00, 0x00, 0x88 }, // a: 136 // B - CurveFull_0, // 12 bytes - CurveFull_1, // 14 bytes + CurveFull0, // 12 bytes + CurveFull1, // 14 bytes new byte[] { 0x00, 0x00 }, // Padding // CLUT - IccTestDataLut.CLUT_16, // 74 bytes + IccTestDataLut.Clut16, // 74 bytes new byte[] { 0x00, 0x00 }, // Padding // A - CurveFull_2, // 18 bytes + CurveFull2, // 18 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_1, // 14 bytes + CurveFull1, // 14 bytes new byte[] { 0x00, 0x00 }, // Padding - CurveFull_0); // 12 bytes + CurveFull0); // 12 bytes public static readonly object[][] LutBToATagDataEntryTestData = - { - new object[] { LutBToA_Arr, LutBToA_Val }, - }; + [ + [LutBToAArr, LutBToAVal] + ]; - public static readonly IccMeasurementTagDataEntry Measurement_Val = new( + public static readonly IccMeasurementTagDataEntry MeasurementVal = new( IccStandardObserver.Cie1931Observer, - IccTestDataNonPrimitives.XyzNumber_ValVar1, + IccTestDataNonPrimitives.XyzNumberValVar1, IccMeasurementGeometry.Degree0ToDOrDTo0, 1f, IccStandardIlluminant.D50); - public static readonly byte[] Measurement_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_1, - IccTestDataNonPrimitives.XyzNumber_Var1, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UFix16_1, - IccTestDataPrimitives.UInt32_1); + public static readonly byte[] MeasurementArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt321, + IccTestDataNonPrimitives.XyzNumberVar1, + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UFix161, + IccTestDataPrimitives.UInt321); public static readonly object[][] MeasurementTagDataEntryTestData = - { - new object[] { Measurement_Arr, Measurement_Val }, - }; - - private static readonly IccLocalizedString LocalizedString_Rand_enUS = CreateLocalizedString("en", "US", IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand_deDE = CreateLocalizedString("de", "DE", IccTestDataPrimitives.Unicode_ValRand3); - private static readonly IccLocalizedString LocalizedString_Rand2_deDE = CreateLocalizedString("de", "DE", IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand2_esXL = CreateLocalizedString("es", "XL", IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand2_xyXL = CreateLocalizedString("xy", "XL", IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand_en = CreateLocalizedString("en", null, IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand_Invariant = new(CultureInfo.InvariantCulture, IccTestDataPrimitives.Unicode_ValRand3); + [ + [MeasurementArr, MeasurementVal] + ]; + + private static readonly IccLocalizedString LocalizedStringRandEnUs = CreateLocalizedString("en", "US", IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRandDeDe = CreateLocalizedString("de", "DE", IccTestDataPrimitives.UnicodeValRand3); + private static readonly IccLocalizedString LocalizedStringRand2DeDe = CreateLocalizedString("de", "DE", IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRand2EsXl = CreateLocalizedString("es", "XL", IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRand2XyXl = CreateLocalizedString("xy", "XL", IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRandEn = CreateLocalizedString("en", null, IccTestDataPrimitives.UnicodeValRand2); + private static readonly IccLocalizedString LocalizedStringRandInvariant = new(CultureInfo.InvariantCulture, IccTestDataPrimitives.UnicodeValRand3); private static IccLocalizedString CreateLocalizedString(string language, string country, string text) { @@ -378,42 +358,42 @@ private static IccLocalizedString CreateLocalizedString(string language, string return new IccLocalizedString(culture, text); } - private static readonly IccLocalizedString[] LocalizedString_RandArr_enUS_deDE = new IccLocalizedString[] - { - LocalizedString_Rand_enUS, - LocalizedString_Rand_deDE, - }; - - private static readonly IccLocalizedString[] LocalizedString_RandArr_en_Invariant = new IccLocalizedString[] - { - LocalizedString_Rand_en, - LocalizedString_Rand_Invariant, - }; - - private static readonly IccLocalizedString[] LocalizedString_SameArr_enUS_deDE_esXL_xyXL = new IccLocalizedString[] - { - LocalizedString_Rand_enUS, - LocalizedString_Rand2_deDE, - LocalizedString_Rand2_esXL, - LocalizedString_Rand2_xyXL, - }; - - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new(LocalizedString_RandArr_enUS_deDE); - public static readonly byte[] MultiLocalizedUnicode_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, + private static readonly IccLocalizedString[] LocalizedStringRandArrEnUsDeDe = + [ + LocalizedStringRandEnUs, + LocalizedStringRandDeDe + ]; + + private static readonly IccLocalizedString[] LocalizedStringRandArrEnInvariant = + [ + LocalizedStringRandEn, + LocalizedStringRandInvariant + ]; + + private static readonly IccLocalizedString[] LocalizedStringSameArrEnUsDeDeEsXlXyXl = + [ + LocalizedStringRandEnUs, + LocalizedStringRand2DeDe, + LocalizedStringRand2EsXl, + LocalizedStringRand2XyXl + ]; + + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicodeVal = new(LocalizedStringRandArrEnUsDeDe); + public static readonly byte[] MultiLocalizedUnicodeArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 - new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, + [(byte)'e', (byte)'n', (byte)'U', (byte)'S'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 - new byte[] { (byte)'d', (byte)'e', (byte)'D', (byte)'E' }, + [(byte)'d', (byte)'e', (byte)'D', (byte)'E'], new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 - IccTestDataPrimitives.Unicode_Rand2, - IccTestDataPrimitives.Unicode_Rand3); + IccTestDataPrimitives.UnicodeRand2, + IccTestDataPrimitives.UnicodeRand3); - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val2 = new(LocalizedString_RandArr_en_Invariant); - public static readonly byte[] MultiLocalizedUnicode_Arr2_Read = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicodeVal2 = new(LocalizedStringRandArrEnInvariant); + public static readonly byte[] MultiLocalizedUnicodeArr2Read = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { (byte)'e', (byte)'n', 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 @@ -421,11 +401,11 @@ private static IccLocalizedString CreateLocalizedString(string language, string new byte[] { 0x00, 0x00, 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 - IccTestDataPrimitives.Unicode_Rand2, - IccTestDataPrimitives.Unicode_Rand3); + IccTestDataPrimitives.UnicodeRand2, + IccTestDataPrimitives.UnicodeRand3); - public static readonly byte[] MultiLocalizedUnicode_Arr2_Write = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, + public static readonly byte[] MultiLocalizedUnicodeArr2Write = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { (byte)'e', (byte)'n', 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 @@ -433,393 +413,388 @@ private static IccLocalizedString CreateLocalizedString(string language, string new byte[] { (byte)'x', (byte)'x', 0x00, 0x00 }, new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 - IccTestDataPrimitives.Unicode_Rand2, - IccTestDataPrimitives.Unicode_Rand3); + IccTestDataPrimitives.UnicodeRand2, + IccTestDataPrimitives.UnicodeRand3); - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val3 = new(LocalizedString_SameArr_enUS_deDE_esXL_xyXL); - public static readonly byte[] MultiLocalizedUnicode_Arr3 = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_4, + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicodeVal3 = new(LocalizedStringSameArrEnUsDeDeEsXlXyXl); + public static readonly byte[] MultiLocalizedUnicodeArr3 = ArrayHelper.Concat( + IccTestDataPrimitives.UInt324, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 - new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, + [(byte)'e', (byte)'n', (byte)'U', (byte)'S'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 - new byte[] { (byte)'d', (byte)'e', (byte)'D', (byte)'E' }, + [(byte)'d', (byte)'e', (byte)'D', (byte)'E'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 - new byte[] { (byte)'e', (byte)'s', (byte)'X', (byte)'L' }, + [(byte)'e', (byte)'s', (byte)'X', (byte)'L'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 - new byte[] { (byte)'x', (byte)'y', (byte)'X', (byte)'L' }, + [(byte)'x', (byte)'y', (byte)'X', (byte)'L'], new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 - IccTestDataPrimitives.Unicode_Rand2); - - public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestData_Read = - { - new object[] { MultiLocalizedUnicode_Arr, MultiLocalizedUnicode_Val }, - new object[] { MultiLocalizedUnicode_Arr2_Read, MultiLocalizedUnicode_Val2 }, - new object[] { MultiLocalizedUnicode_Arr3, MultiLocalizedUnicode_Val3 }, - }; - - public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestData_Write = - { - new object[] { MultiLocalizedUnicode_Arr, MultiLocalizedUnicode_Val }, - new object[] { MultiLocalizedUnicode_Arr2_Write, MultiLocalizedUnicode_Val2 }, - new object[] { MultiLocalizedUnicode_Arr3, MultiLocalizedUnicode_Val3 }, - }; - - public static readonly IccMultiProcessElementsTagDataEntry MultiProcessElements_Val = new( - new IccMultiProcessElement[] - { - IccTestDataMultiProcessElements.MPE_ValCLUT, - IccTestDataMultiProcessElements.MPE_ValCLUT, - }); - - public static readonly byte[] MultiProcessElements_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt32_2, + IccTestDataPrimitives.UnicodeRand2); + + public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestDataRead = + [ + [MultiLocalizedUnicodeArr, MultiLocalizedUnicodeVal], + [MultiLocalizedUnicodeArr2Read, MultiLocalizedUnicodeVal2], + [MultiLocalizedUnicodeArr3, MultiLocalizedUnicodeVal3] + ]; + + public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestDataWrite = + [ + [MultiLocalizedUnicodeArr, MultiLocalizedUnicodeVal], + [MultiLocalizedUnicodeArr2Write, MultiLocalizedUnicodeVal2], + [MultiLocalizedUnicodeArr3, MultiLocalizedUnicodeVal3] + ]; + + public static readonly IccMultiProcessElementsTagDataEntry MultiProcessElementsVal = new( + [ + IccTestDataMultiProcessElements.MpeValClut, + IccTestDataMultiProcessElements.MpeValClut + ]); + + public static readonly byte[] MultiProcessElementsArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x20 }, // 32 new byte[] { 0x00, 0x00, 0x00, 0x84 }, // 132 new byte[] { 0x00, 0x00, 0x00, 0xA4 }, // 164 new byte[] { 0x00, 0x00, 0x00, 0x84 }, // 132 - IccTestDataMultiProcessElements.MPE_CLUT, - IccTestDataMultiProcessElements.MPE_CLUT); + IccTestDataMultiProcessElements.MpeClut, + IccTestDataMultiProcessElements.MpeClut); public static readonly object[][] MultiProcessElementsTagDataEntryTestData = - { - new object[] { MultiProcessElements_Arr, MultiProcessElements_Val }, - }; + [ + [MultiProcessElementsArr, MultiProcessElementsVal] + ]; - public static readonly IccNamedColor2TagDataEntry NamedColor2_Val = new( + public static readonly IccNamedColor2TagDataEntry NamedColor2Val = new( 16909060, ArrayHelper.Fill('A', 31), ArrayHelper.Fill('4', 31), - new IccNamedColor[] { IccTestDataNonPrimitives.NamedColor_ValMin, IccTestDataNonPrimitives.NamedColor_ValMin }); + [IccTestDataNonPrimitives.NamedColorValMin, IccTestDataNonPrimitives.NamedColorValMin]); - public static readonly byte[] NamedColor2_Arr = ArrayHelper.Concat( + public static readonly byte[] NamedColor2Arr = ArrayHelper.Concat( new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UInt32_3, + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UInt323, ArrayHelper.Fill((byte)0x41, 31), new byte[] { 0x00 }, ArrayHelper.Fill((byte)0x34, 31), new byte[] { 0x00 }, - IccTestDataNonPrimitives.NamedColor_Min, - IccTestDataNonPrimitives.NamedColor_Min); + IccTestDataNonPrimitives.NamedColorMin, + IccTestDataNonPrimitives.NamedColorMin); public static readonly object[][] NamedColor2TagDataEntryTestData = - { - new object[] { NamedColor2_Arr, NamedColor2_Val }, - }; + [ + [NamedColor2Arr, NamedColor2Val] + ]; - public static readonly IccParametricCurveTagDataEntry ParametricCurve_Val = new(IccTestDataCurves.Parametric_ValVar1); - public static readonly byte[] ParametricCurve_Arr = IccTestDataCurves.Parametric_Var1; + public static readonly IccParametricCurveTagDataEntry ParametricCurveVal = new(IccTestDataCurves.ParametricValVar1); + public static readonly byte[] ParametricCurveArr = IccTestDataCurves.ParametricVar1; public static readonly object[][] ParametricCurveTagDataEntryTestData = - { - new object[] { ParametricCurve_Arr, ParametricCurve_Val }, - }; + [ + [ParametricCurveArr, ParametricCurveVal] + ]; - public static readonly IccProfileSequenceDescTagDataEntry ProfileSequenceDesc_Val = new( - new IccProfileDescription[] - { - IccTestDataNonPrimitives.ProfileDescription_ValRand1, - IccTestDataNonPrimitives.ProfileDescription_ValRand1 - }); + public static readonly IccProfileSequenceDescTagDataEntry ProfileSequenceDescVal = new( + [ + IccTestDataNonPrimitives.ProfileDescriptionValRand1, + IccTestDataNonPrimitives.ProfileDescriptionValRand1 + ]); - public static readonly byte[] ProfileSequenceDesc_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, - IccTestDataNonPrimitives.ProfileDescription_Rand1, - IccTestDataNonPrimitives.ProfileDescription_Rand1); + public static readonly byte[] ProfileSequenceDescArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, + IccTestDataNonPrimitives.ProfileDescriptionRand1, + IccTestDataNonPrimitives.ProfileDescriptionRand1); public static readonly object[][] ProfileSequenceDescTagDataEntryTestData = - { - new object[] { ProfileSequenceDesc_Arr, ProfileSequenceDesc_Val }, - }; + [ + [ProfileSequenceDescArr, ProfileSequenceDescVal] + ]; public static readonly IccProfileSequenceIdentifierTagDataEntry ProfileSequenceIdentifier_Val = new( - new IccProfileSequenceIdentifier[] - { - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUS_deDE), - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUS_deDE), - }); + [ + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileIdValRand, LocalizedStringRandArrEnUsDeDe), + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileIdValRand, LocalizedStringRandArrEnUsDeDe) + ]); - public static readonly byte[] ProfileSequenceIdentifier_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_2, + public static readonly byte[] ProfileSequenceIdentifierArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt322, new byte[] { 0x00, 0x00, 0x00, 0x1C }, // 28 new byte[] { 0x00, 0x00, 0x00, 0x54 }, // 84 new byte[] { 0x00, 0x00, 0x00, 0x70 }, // 112 new byte[] { 0x00, 0x00, 0x00, 0x54 }, // 84 - IccTestDataNonPrimitives.ProfileId_Rand, // 16 bytes - TagDataEntryHeader_MultiLocalizedUnicodeArr, // 8 bytes - MultiLocalizedUnicode_Arr, // 58 bytes + IccTestDataNonPrimitives.ProfileIdRand, // 16 bytes + TagDataEntryHeaderMultiLocalizedUnicodeArr, // 8 bytes + MultiLocalizedUnicodeArr, // 58 bytes new byte[] { 0x00, 0x00 }, // 2 bytes (padding) - IccTestDataNonPrimitives.ProfileId_Rand, - TagDataEntryHeader_MultiLocalizedUnicodeArr, - MultiLocalizedUnicode_Arr, + IccTestDataNonPrimitives.ProfileIdRand, + TagDataEntryHeaderMultiLocalizedUnicodeArr, + MultiLocalizedUnicodeArr, new byte[] { 0x00, 0x00 }); public static readonly object[][] ProfileSequenceIdentifierTagDataEntryTestData = - { - new object[] { ProfileSequenceIdentifier_Arr, ProfileSequenceIdentifier_Val }, - }; - - public static readonly IccResponseCurveSet16TagDataEntry ResponseCurveSet16_Val = new( - new IccResponseCurve[] - { - IccTestDataCurves.Response_ValGrad, - IccTestDataCurves.Response_ValGrad, - }); - - public static readonly byte[] ResponseCurveSet16_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_2, + [ + [ProfileSequenceIdentifierArr, ProfileSequenceIdentifier_Val] + ]; + + public static readonly IccResponseCurveSet16TagDataEntry ResponseCurveSet16Val = new( + [ + IccTestDataCurves.ResponseValGrad, + IccTestDataCurves.ResponseValGrad + ]); + + public static readonly byte[] ResponseCurveSet16Arr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt162, new byte[] { 0x00, 0x00, 0x00, 0x14 }, // 20 new byte[] { 0x00, 0x00, 0x00, 0x6C }, // 108 - IccTestDataCurves.Response_Grad, // 88 bytes - IccTestDataCurves.Response_Grad); // 88 bytes + IccTestDataCurves.ResponseGrad, // 88 bytes + IccTestDataCurves.ResponseGrad); // 88 bytes public static readonly object[][] ResponseCurveSet16TagDataEntryTestData = - { - new object[] { ResponseCurveSet16_Arr, ResponseCurveSet16_Val }, - }; + [ + [ResponseCurveSet16Arr, ResponseCurveSet16Val] + ]; - public static readonly IccFix16ArrayTagDataEntry Fix16Array_Val = new(new float[] { 1 / 256f, 2 / 256f, 3 / 256f }); - public static readonly byte[] Fix16Array_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.Fix16_1, - IccTestDataPrimitives.Fix16_2, - IccTestDataPrimitives.Fix16_3); + public static readonly IccFix16ArrayTagDataEntry Fix16ArrayVal = new([1 / 256f, 2 / 256f, 3 / 256f]); + public static readonly byte[] Fix16ArrayArr = ArrayHelper.Concat( + IccTestDataPrimitives.Fix161, + IccTestDataPrimitives.Fix162, + IccTestDataPrimitives.Fix163); public static readonly object[][] Fix16ArrayTagDataEntryTestData = - { - new object[] { Fix16Array_Arr, Fix16Array_Val, 20u }, - }; + [ + [Fix16ArrayArr, Fix16ArrayVal, 20u] + ]; - public static readonly IccSignatureTagDataEntry Signature_Val = new("ABCD"); - public static readonly byte[] Signature_Arr = { 0x41, 0x42, 0x43, 0x44, }; + public static readonly IccSignatureTagDataEntry SignatureVal = new("ABCD"); + public static readonly byte[] SignatureArr = [0x41, 0x42, 0x43, 0x44]; public static readonly object[][] SignatureTagDataEntryTestData = - { - new object[] { Signature_Arr, Signature_Val }, - }; + [ + [SignatureArr, SignatureVal] + ]; - public static readonly IccTextTagDataEntry Text_Val = new("ABCD"); - public static readonly byte[] Text_Arr = { 0x41, 0x42, 0x43, 0x44 }; + public static readonly IccTextTagDataEntry TextVal = new("ABCD"); + public static readonly byte[] TextArr = [0x41, 0x42, 0x43, 0x44]; public static readonly object[][] TextTagDataEntryTestData = - { - new object[] { Text_Arr, Text_Val, 12u }, - }; + [ + [TextArr, TextVal, 12u] + ]; - public static readonly IccUFix16ArrayTagDataEntry UFix16Array_Val = new(new float[] { 1, 2, 3 }); - public static readonly byte[] UFix16Array_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UFix16_1, - IccTestDataPrimitives.UFix16_2, - IccTestDataPrimitives.UFix16_3); + public static readonly IccUFix16ArrayTagDataEntry UFix16ArrayVal = new([1, 2, 3]); + public static readonly byte[] UFix16ArrayArr = ArrayHelper.Concat( + IccTestDataPrimitives.UFix161, + IccTestDataPrimitives.UFix162, + IccTestDataPrimitives.UFix163); public static readonly object[][] UFix16ArrayTagDataEntryTestData = - { - new object[] { UFix16Array_Arr, UFix16Array_Val, 20u }, - }; + [ + [UFix16ArrayArr, UFix16ArrayVal, 20u] + ]; - public static readonly IccUInt16ArrayTagDataEntry UInt16Array_Val = new(new ushort[] { 1, 2, 3 }); - public static readonly byte[] UInt16Array_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt16_1, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_3); + public static readonly IccUInt16ArrayTagDataEntry UInt16ArrayVal = new([1, 2, 3]); + public static readonly byte[] UInt16ArrayArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt161, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt163); public static readonly object[][] UInt16ArrayTagDataEntryTestData = - { - new object[] { UInt16Array_Arr, UInt16Array_Val, 14u }, - }; + [ + [UInt16ArrayArr, UInt16ArrayVal, 14u] + ]; - public static readonly IccUInt32ArrayTagDataEntry UInt32Array_Val = new(new uint[] { 1, 2, 3 }); - public static readonly byte[] UInt32Array_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_1, - IccTestDataPrimitives.UInt32_2, - IccTestDataPrimitives.UInt32_3); + public static readonly IccUInt32ArrayTagDataEntry UInt32ArrayVal = new([1, 2, 3]); + public static readonly byte[] UInt32ArrayArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt321, + IccTestDataPrimitives.UInt322, + IccTestDataPrimitives.UInt323); public static readonly object[][] UInt32ArrayTagDataEntryTestData = - { - new object[] { UInt32Array_Arr, UInt32Array_Val, 20u }, - }; + [ + [UInt32ArrayArr, UInt32ArrayVal, 20u] + ]; - public static readonly IccUInt64ArrayTagDataEntry UInt64Array_Val = new(new ulong[] { 1, 2, 3 }); - public static readonly byte[] UInt64Array_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt64_1, - IccTestDataPrimitives.UInt64_2, - IccTestDataPrimitives.UInt64_3); + public static readonly IccUInt64ArrayTagDataEntry UInt64ArrayVal = new([1, 2, 3]); + public static readonly byte[] UInt64ArrayArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt641, + IccTestDataPrimitives.UInt642, + IccTestDataPrimitives.UInt643); public static readonly object[][] UInt64ArrayTagDataEntryTestData = - { - new object[] { UInt64Array_Arr, UInt64Array_Val, 32u }, - }; + [ + [UInt64ArrayArr, UInt64ArrayVal, 32u] + ]; - public static readonly IccUInt8ArrayTagDataEntry UInt8Array_Val = new(new byte[] { 1, 2, 3 }); - public static readonly byte[] UInt8Array_Arr = { 1, 2, 3 }; + public static readonly IccUInt8ArrayTagDataEntry UInt8ArrayVal = new([1, 2, 3]); + public static readonly byte[] UInt8ArrayArr = [1, 2, 3]; public static readonly object[][] UInt8ArrayTagDataEntryTestData = - { - new object[] { UInt8Array_Arr, UInt8Array_Val, 11u }, - }; + [ + [UInt8ArrayArr, UInt8ArrayVal, 11u] + ]; - public static readonly IccViewingConditionsTagDataEntry ViewingConditions_Val = new( - IccTestDataNonPrimitives.XyzNumber_ValVar1, - IccTestDataNonPrimitives.XyzNumber_ValVar2, + public static readonly IccViewingConditionsTagDataEntry ViewingConditionsVal = new( + IccTestDataNonPrimitives.XyzNumberValVar1, + IccTestDataNonPrimitives.XyzNumberValVar2, IccStandardIlluminant.D50); - public static readonly byte[] ViewingConditions_Arr = ArrayHelper.Concat( - IccTestDataNonPrimitives.XyzNumber_Var1, - IccTestDataNonPrimitives.XyzNumber_Var2, - IccTestDataPrimitives.UInt32_1); + public static readonly byte[] ViewingConditionsArr = ArrayHelper.Concat( + IccTestDataNonPrimitives.XyzNumberVar1, + IccTestDataNonPrimitives.XyzNumberVar2, + IccTestDataPrimitives.UInt321); public static readonly object[][] ViewingConditionsTagDataEntryTestData = - { - new object[] { ViewingConditions_Arr, ViewingConditions_Val }, - }; + [ + [ViewingConditionsArr, ViewingConditionsVal] + ]; - public static readonly IccXyzTagDataEntry XYZ_Val = new(new Vector3[] - { - IccTestDataNonPrimitives.XyzNumber_ValVar1, - IccTestDataNonPrimitives.XyzNumber_ValVar2, - IccTestDataNonPrimitives.XyzNumber_ValVar3, - }); + public static readonly IccXyzTagDataEntry XyzVal = new([ + IccTestDataNonPrimitives.XyzNumberValVar1, + IccTestDataNonPrimitives.XyzNumberValVar2, + IccTestDataNonPrimitives.XyzNumberValVar3 + ]); - public static readonly byte[] XYZ_Arr = ArrayHelper.Concat( - IccTestDataNonPrimitives.XyzNumber_Var1, - IccTestDataNonPrimitives.XyzNumber_Var2, - IccTestDataNonPrimitives.XyzNumber_Var3); + public static readonly byte[] XyzArr = ArrayHelper.Concat( + IccTestDataNonPrimitives.XyzNumberVar1, + IccTestDataNonPrimitives.XyzNumberVar2, + IccTestDataNonPrimitives.XyzNumberVar3); public static readonly object[][] XYZTagDataEntryTestData = - { - new object[] { XYZ_Arr, XYZ_Val, 44u }, - }; + [ + [XyzArr, XyzVal, 44u] + ]; - public static readonly IccTextDescriptionTagDataEntry TextDescription_Val1 = new( - IccTestDataPrimitives.Ascii_ValRand, - IccTestDataPrimitives.Unicode_ValRand1, + public static readonly IccTextDescriptionTagDataEntry TextDescriptionVal1 = new( + IccTestDataPrimitives.AsciiValRand, + IccTestDataPrimitives.UnicodeValRand1, ArrayHelper.Fill('A', 66), 1701729619, 2); - public static readonly byte[] TextDescription_Arr1 = ArrayHelper.Concat( + public static readonly byte[] TextDescriptionArr1 = ArrayHelper.Concat( new byte[] { 0x00, 0x00, 0x00, 0x0B }, // 11 - IccTestDataPrimitives.Ascii_Rand, + IccTestDataPrimitives.AsciiRand, new byte[] { 0x00 }, // Null terminator new byte[] { 0x65, 0x6E, 0x55, 0x53 }, // enUS new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 - IccTestDataPrimitives.Unicode_Rand1, + IccTestDataPrimitives.UnicodeRand1, new byte[] { 0x00, 0x00 }, // Null terminator new byte[] { 0x00, 0x02, 0x43 }, // 2, 67 ArrayHelper.Fill((byte)0x41, 66), new byte[] { 0x00 }); // Null terminator - public static readonly IccTextDescriptionTagDataEntry TextDescription_Val2 = new(IccTestDataPrimitives.Ascii_ValRand, null, null, 0, 0); - public static readonly byte[] TextDescription_Arr2 = ArrayHelper.Concat( + public static readonly IccTextDescriptionTagDataEntry TextDescriptionVal2 = new(IccTestDataPrimitives.AsciiValRand, null, null, 0, 0); + public static readonly byte[] TextDescriptionArr2 = ArrayHelper.Concat( new byte[] { 0x00, 0x00, 0x00, 0x0B }, // 11 - IccTestDataPrimitives.Ascii_Rand, + IccTestDataPrimitives.AsciiRand, new byte[] { 0x00 }, // Null terminator - IccTestDataPrimitives.UInt32_0, - IccTestDataPrimitives.UInt32_0, + IccTestDataPrimitives.UInt320, + IccTestDataPrimitives.UInt320, new byte[] { 0x00, 0x00, 0x00 }, // 0, 0 ArrayHelper.Fill((byte)0x00, 67)); public static readonly object[][] TextDescriptionTagDataEntryTestData = - { - new object[] { TextDescription_Arr1, TextDescription_Val1 }, - new object[] { TextDescription_Arr2, TextDescription_Val2 }, - }; - - public static readonly IccCrdInfoTagDataEntry CrdInfo_Val = new( - IccTestDataPrimitives.Ascii_ValRand4, - IccTestDataPrimitives.Ascii_ValRand1, - IccTestDataPrimitives.Ascii_ValRand2, - IccTestDataPrimitives.Ascii_ValRand3, - IccTestDataPrimitives.Ascii_ValRand4); - - public static readonly byte[] CrdInfo_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.Ascii_Rand4, + [ + [TextDescriptionArr1, TextDescriptionVal1], + [TextDescriptionArr2, TextDescriptionVal2] + ]; + + public static readonly IccCrdInfoTagDataEntry CrdInfoVal = new( + IccTestDataPrimitives.AsciiValRand4, + IccTestDataPrimitives.AsciiValRand1, + IccTestDataPrimitives.AsciiValRand2, + IccTestDataPrimitives.AsciiValRand3, + IccTestDataPrimitives.AsciiValRand4); + + public static readonly byte[] CrdInfoArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.AsciiRand4, new byte[] { 0 }, - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.Ascii_Rand1, + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.AsciiRand1, new byte[] { 0 }, - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.Ascii_Rand2, + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.AsciiRand2, new byte[] { 0 }, - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.Ascii_Rand3, + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.AsciiRand3, new byte[] { 0 }, - IccTestDataPrimitives.UInt32_6, - IccTestDataPrimitives.Ascii_Rand4, + IccTestDataPrimitives.UInt326, + IccTestDataPrimitives.AsciiRand4, new byte[] { 0 }); public static readonly object[][] CrdInfoTagDataEntryTestData = - { - new object[] { CrdInfo_Arr, CrdInfo_Val }, - }; + [ + [CrdInfoArr, CrdInfoVal] + ]; - public static readonly IccScreeningTagDataEntry Screening_Val = new( + public static readonly IccScreeningTagDataEntry ScreeningVal = new( IccScreeningFlag.DefaultScreens | IccScreeningFlag.UnitLinesPerCm, - new IccScreeningChannel[] { IccTestDataNonPrimitives.ScreeningChannel_ValRand1, IccTestDataNonPrimitives.ScreeningChannel_ValRand2 }); + [IccTestDataNonPrimitives.ScreeningChannelValRand1, IccTestDataNonPrimitives.ScreeningChannelValRand2]); - public static readonly byte[] Screening_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.Int32_1, - IccTestDataPrimitives.UInt32_2, - IccTestDataNonPrimitives.ScreeningChannel_Rand1, - IccTestDataNonPrimitives.ScreeningChannel_Rand2); + public static readonly byte[] ScreeningArr = ArrayHelper.Concat( + IccTestDataPrimitives.Int321, + IccTestDataPrimitives.UInt322, + IccTestDataNonPrimitives.ScreeningChannelRand1, + IccTestDataNonPrimitives.ScreeningChannelRand2); public static readonly object[][] ScreeningTagDataEntryTestData = - { - new object[] { Screening_Arr, Screening_Val }, - }; - - public static readonly IccUcrBgTagDataEntry UcrBg_Val = new( - new ushort[] { 3, 4, 6 }, - new ushort[] { 9, 7, 2, 5 }, - IccTestDataPrimitives.Ascii_ValRand); - - public static readonly byte[] UcrBg_Arr = ArrayHelper.Concat( - IccTestDataPrimitives.UInt32_3, - IccTestDataPrimitives.UInt16_3, - IccTestDataPrimitives.UInt16_4, - IccTestDataPrimitives.UInt16_6, - IccTestDataPrimitives.UInt32_4, - IccTestDataPrimitives.UInt16_9, - IccTestDataPrimitives.UInt16_7, - IccTestDataPrimitives.UInt16_2, - IccTestDataPrimitives.UInt16_5, - IccTestDataPrimitives.Ascii_Rand, + [ + [ScreeningArr, ScreeningVal] + ]; + + public static readonly IccUcrBgTagDataEntry UcrBgVal = new( + [3, 4, 6], + [9, 7, 2, 5], + IccTestDataPrimitives.AsciiValRand); + + public static readonly byte[] UcrBgArr = ArrayHelper.Concat( + IccTestDataPrimitives.UInt323, + IccTestDataPrimitives.UInt163, + IccTestDataPrimitives.UInt164, + IccTestDataPrimitives.UInt166, + IccTestDataPrimitives.UInt324, + IccTestDataPrimitives.UInt169, + IccTestDataPrimitives.UInt167, + IccTestDataPrimitives.UInt162, + IccTestDataPrimitives.UInt165, + IccTestDataPrimitives.AsciiRand, new byte[] { 0 }); public static readonly object[][] UcrBgTagDataEntryTestData = - { - new object[] { UcrBg_Arr, UcrBg_Val, 41 }, - }; - - public static readonly IccTagDataEntry TagDataEntry_CurveVal = Curve_Val_2; - public static readonly byte[] TagDataEntry_CurveArr = ArrayHelper.Concat( - TagDataEntryHeader_CurveArr, - Curve_Arr_2, + [ + [UcrBgArr, UcrBgVal, 41] + ]; + + public static readonly IccTagDataEntry TagDataEntryCurveVal = CurveVal2; + public static readonly byte[] TagDataEntryCurveArr = ArrayHelper.Concat( + TagDataEntryHeaderCurveArr, + CurveArr2, new byte[] { 0x00, 0x00 }); // padding - public static readonly IccTagDataEntry TagDataEntry_MultiLocalizedUnicodeVal = MultiLocalizedUnicode_Val; - public static readonly byte[] TagDataEntry_MultiLocalizedUnicodeArr = ArrayHelper.Concat( - TagDataEntryHeader_MultiLocalizedUnicodeArr, - MultiLocalizedUnicode_Arr, + public static readonly IccTagDataEntry TagDataEntryMultiLocalizedUnicodeVal = MultiLocalizedUnicodeVal; + public static readonly byte[] TagDataEntryMultiLocalizedUnicodeArr = ArrayHelper.Concat( + TagDataEntryHeaderMultiLocalizedUnicodeArr, + MultiLocalizedUnicodeArr, new byte[] { 0x00, 0x00 }); // padding - public static readonly IccTagTableEntry TagDataEntry_MultiLocalizedUnicodeTable = new( + public static readonly IccTagTableEntry TagDataEntryMultiLocalizedUnicodeTable = new( IccProfileTag.Unknown, 0, - (uint)TagDataEntry_MultiLocalizedUnicodeArr.Length - 2); + (uint)TagDataEntryMultiLocalizedUnicodeArr.Length - 2); - public static readonly IccTagTableEntry TagDataEntry_CurveTable = new(IccProfileTag.Unknown, 0, (uint)TagDataEntry_CurveArr.Length - 2); + public static readonly IccTagTableEntry TagDataEntryCurveTable = new(IccProfileTag.Unknown, 0, (uint)TagDataEntryCurveArr.Length - 2); public static readonly object[][] TagDataEntryTestData = - { - new object[] { TagDataEntry_CurveArr, TagDataEntry_CurveVal }, - new object[] { TagDataEntry_MultiLocalizedUnicodeArr, TagDataEntry_MultiLocalizedUnicodeVal }, - }; + [ + [TagDataEntryCurveArr, TagDataEntryCurveVal], + [TagDataEntryMultiLocalizedUnicodeArr, TagDataEntryMultiLocalizedUnicodeVal] + ]; } diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/CGATS21_CRPC7.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/CGATS21_CRPC7.icc new file mode 100644 index 0000000000..c4b7084721 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/CGATS21_CRPC7.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b186e6dadc38883138ccd30203b2870fb71db6451d3a5d24f9590cfabd26689 +size 3462496 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/Coated_Fogra39L_VIGC_300.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/Coated_Fogra39L_VIGC_300.icc new file mode 100644 index 0000000000..a21b7f657d --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/Coated_Fogra39L_VIGC_300.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6df43849a84d2632e7900a125f657bd09364ed56775bc4740c1e39a5492d47e3 +size 8652444 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/ISO22028-2_ROMM-RGB.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/ISO22028-2_ROMM-RGB.icc new file mode 100644 index 0000000000..b06abf217a --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/ISO22028-2_ROMM-RGB.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96b2f2987f83e2a545e607799fbfdff43ef8158fb9b215b187c574db8f145aaf +size 864 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2003WebCoated.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2003WebCoated.icc new file mode 100644 index 0000000000..028a3cde63 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2003WebCoated.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:662a345063b2853a66ed12dc54215e513e8485cbf4cb7f673971c834979eb25f +size 654432 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2011Coated.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2011Coated.icc new file mode 100644 index 0000000000..ee8dde35aa --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/JapanColor2011Coated.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73202ea802e57a63a0be05ed993fde8e4d51cc436120e6c47815ce785ff42916 +size 1979304 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/SWOP2006_Coated5v2.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/SWOP2006_Coated5v2.icc new file mode 100644 index 0000000000..e24f272f35 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/SWOP2006_Coated5v2.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ac00fe6f03901bfd06ef70e72ec2c55fa3c043c6c34c0b6d70f06cc7a40a822 +size 2747952 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/issue-129.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/issue-129.icc new file mode 100644 index 0000000000..9dee961653 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/issue-129.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35f401731df11a4eba3502af632e51d68bc394bcb7d34632a331c1ba3f4a0bf6 +size 557168 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB2014.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB2014.icc new file mode 100644 index 0000000000..46916dd8ce --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB2014.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:384b832de3412066743b52a75ee906b6fb9fb8d9e09e936fc2c43223815c6e0a +size 3024 diff --git a/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB_v4_ICC_preference.icc b/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB_v4_ICC_preference.icc new file mode 100644 index 0000000000..8ddb31b2b8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataIcc/Profiles/sRGB_v4_ICC_preference.icc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83174717332326ddc198d9df188a4daec27b8979ba152cebbfc470c793d0bb11 +size 60960 diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index a53e508067..1177152c03 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -117,7 +117,7 @@ public string GetFileNameWithoutExtension(object value) /// The . /// public Image CreateRgba32Image(IImageDecoder decoder) - => this.CreateRgba32Image(decoder, new()); + => this.CreateRgba32Image(decoder, new DecoderOptions()); /// /// Creates a new image. diff --git a/tests/ImageSharp.Tests/TestFontUtilities.cs b/tests/ImageSharp.Tests/TestFontUtilities.cs index 01b1faa45a..a2c2032672 100644 --- a/tests/ImageSharp.Tests/TestFontUtilities.cs +++ b/tests/ImageSharp.Tests/TestFontUtilities.cs @@ -37,13 +37,13 @@ public static string GetPath(string file) /// private static string GetFontsDirectory() { - List directories = new List - { - "TestFonts/", // Here for code coverage tests. - "tests/ImageSharp.Tests/TestFonts/", // from travis/build script - "../../../../../ImageSharp.Tests/TestFonts/", // from Sandbox46 - "../../../../TestFonts/" - }; + List directories = + [ + "TestFonts/", // Here for code coverage tests. + "tests/ImageSharp.Tests/TestFonts/", // from travis/build script + "../../../../../ImageSharp.Tests/TestFonts/", // from Sandbox46 + "../../../../TestFonts/" + ]; directories = directories.SelectMany(x => new[] { @@ -59,7 +59,7 @@ private static string GetFontsDirectory() return directory; } - throw new System.Exception($"Unable to find Fonts directory at any of these locations [{string.Join(", ", directories)}]"); + throw new Exception($"Unable to find Fonts directory at any of these locations [{string.Join(", ", directories)}]"); } /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index f597b708d5..a3de7b3feb 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests; @@ -27,7 +28,7 @@ public TestFormat() this.Decoder = new TestDecoder(this); } - public List DecodeCalls { get; } = new(); + public List DecodeCalls { get; } = []; public TestEncoder Encoder { get; } @@ -37,7 +38,7 @@ public TestFormat() public MemoryStream CreateStream(byte[] marker = null) { - var ms = new MemoryStream(); + MemoryStream ms = new(); byte[] data = this.header; ms.Write(data, 0, data.Length); if (marker != null) @@ -53,7 +54,7 @@ public Stream CreateAsyncSemaphoreStream(SemaphoreSlim notifyWaitPositionReached { byte[] buffer = new byte[size]; this.header.CopyTo(buffer, 0); - var semaphoreStream = new SemaphoreReadMemoryStream(buffer, waitAfterPosition, notifyWaitPositionReachedSemaphore, continueSemaphore); + SemaphoreReadMemoryStream semaphoreStream = new(buffer, waitAfterPosition, notifyWaitPositionReachedSemaphore, continueSemaphore); return seeakable ? semaphoreStream : new AsyncStreamWrapper(semaphoreStream, () => false); } @@ -89,7 +90,7 @@ public Image Sample() { 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)]; @@ -102,7 +103,7 @@ public Image Sample() public string Extension => "test_ext"; - public IEnumerable SupportedExtensions => new[] { "test_ext" }; + public IEnumerable SupportedExtensions => ["test_ext"]; public int HeaderSize => this.header.Length; @@ -110,7 +111,7 @@ public Image Sample() public string DefaultMimeType => this.MimeType; - public IEnumerable MimeTypes => new[] { this.MimeType }; + public IEnumerable MimeTypes => [this.MimeType]; public IEnumerable FileExtensions => this.SupportedExtensions; @@ -192,7 +193,7 @@ public class TestDecoder : SpecializedImageDecoder public TestDecoder(TestFormat testFormat) => this.testFormat = testFormat; - public IEnumerable MimeTypes => new[] { this.testFormat.MimeType }; + public IEnumerable MimeTypes => [this.testFormat.MimeType]; public IEnumerable FileExtensions => this.testFormat.SupportedExtensions; @@ -202,11 +203,12 @@ public class TestDecoder : SpecializedImageDecoder 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 ImageInfo(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) @@ -244,7 +246,7 @@ public class TestEncoder : IImageEncoder public TestEncoder(TestFormat testFormat) => this.testFormat = testFormat; - public IEnumerable MimeTypes => new[] { this.testFormat.MimeType }; + public IEnumerable MimeTypes => [this.testFormat.MimeType]; public IEnumerable FileExtensions => this.testFormat.SupportedExtensions; @@ -263,75 +265,73 @@ public Task EncodeAsync(Image image, Stream stream, Cancellation public struct TestPixelForAgnosticDecode : IPixel { - public PixelOperations CreatePixelOperations() => new(); + public readonly Rgba32 ToRgba32() => default; - public void FromScaledVector4(Vector4 vector) - { - } + public readonly Vector4 ToScaledVector4() => default; - public Vector4 ToScaledVector4() => default; + public readonly Vector4 ToVector4() => default; - public void FromVector4(Vector4 vector) - { - } + /// + public readonly Vector4 ToUnassociatedScaledVector4() => default; - public Vector4 ToVector4() => default; + /// + public readonly Vector4 ToAssociatedScaledVector4() => default; - public void FromArgb32(Argb32 source) - { - } + /// + public readonly Vector4 ToUnassociatedVector4() => default; - public void FromBgra5551(Bgra5551 source) - { - } + /// + public readonly Vector4 ToAssociatedVector4() => default; - public void FromBgr24(Bgr24 source) - { - } + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(2, 8, 8), + PixelColorType.Red | PixelColorType.Green, + PixelAlphaRepresentation.None); - public void FromBgra32(Bgra32 source) - { - } + public static PixelOperations CreatePixelOperations() => new(); - public void FromAbgr32(Abgr32 source) - { - } + public static TestPixelForAgnosticDecode FromScaledVector4(Vector4 vector) => default; - public void FromL8(L8 source) - { - } + public static TestPixelForAgnosticDecode FromVector4(Vector4 vector) => default; - public void FromL16(L16 source) - { - } + /// + public static TestPixelForAgnosticDecode FromUnassociatedScaledVector4(Vector4 source) => default; - public void FromLa16(La16 source) - { - } + /// + public static TestPixelForAgnosticDecode FromAssociatedScaledVector4(Vector4 source) => default; - public void FromLa32(La32 source) - { - } + /// + public static TestPixelForAgnosticDecode FromUnassociatedVector4(Vector4 source) => default; - public void FromRgb24(Rgb24 source) - { - } + /// + public static TestPixelForAgnosticDecode FromAssociatedVector4(Vector4 source) => default; - public void FromRgba32(Rgba32 source) - { - } + public static TestPixelForAgnosticDecode FromAbgr32(Abgr32 source) => default; - public void ToRgba32(ref Rgba32 dest) - { - } + public static TestPixelForAgnosticDecode FromArgb32(Argb32 source) => default; - public void FromRgb48(Rgb48 source) - { - } + public static TestPixelForAgnosticDecode FromBgra5551(Bgra5551 source) => default; - public void FromRgba64(Rgba64 source) - { - } + public static TestPixelForAgnosticDecode FromBgr24(Bgr24 source) => default; + + public static TestPixelForAgnosticDecode FromBgra32(Bgra32 source) => default; + + public static TestPixelForAgnosticDecode FromL8(L8 source) => default; + + public static TestPixelForAgnosticDecode FromL16(L16 source) => default; + + public static TestPixelForAgnosticDecode FromLa16(La16 source) => default; + + public static TestPixelForAgnosticDecode FromLa32(La32 source) => default; + + public static TestPixelForAgnosticDecode FromRgb24(Rgb24 source) => default; + + public static TestPixelForAgnosticDecode FromRgba32(Rgba32 source) => default; + + public static TestPixelForAgnosticDecode FromRgb48(Rgb48 source) => default; + + public static TestPixelForAgnosticDecode FromRgba64(Rgba64 source) => default; public bool Equals(TestPixelForAgnosticDecode other) => false; } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a0e951e70d..03937310f5 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -57,11 +57,13 @@ public static class Png public const string LowColorVariance = "Png/low-variance.png"; public const string PngWithMetadata = "Png/PngWithMetaData.png"; public const string InvalidTextData = "Png/InvalidTextData.png"; + public const string DuplicateHeaderChunkResync = "Png/duplicate-header-chunk-resync.png"; public const string David = "Png/david.png"; public const string TestPattern31x31 = "Png/testpattern31x31.png"; public const string TestPattern31x31HalfTransparent = "Png/testpattern31x31-halftransparent.png"; public const string XmpColorPalette = "Png/xmp-colorpalette.png"; public const string AdamHeadsHlg = "Png/adamHeadsHLG.png"; + public const string IptcMetadata = "Png/iptc-profile.png"; // Animated // https://philip.html5.org/tests/apng/tests.html @@ -75,7 +77,12 @@ public static class 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 AnimatedFrameCount = "Png/animated/issue-animated-frame-count.png"; public const string Issue2666 = "Png/issues/Issue_2666.png"; + public const string Issue2882 = "Png/issues/Issue_2882.png"; + public const string Issue396Dragon = "Png/issues/Issue_396_dragon.png"; + public const string Issue396Hand1 = "Png/issues/Issue_396_hand_1.png"; + public const string Issue396Hand2 = "Png/issues/Issue_396_hand_2.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; @@ -159,6 +166,36 @@ public static class Png // Issue 2752: https://github.com/SixLabors/ImageSharp/issues/2752 public const string Issue2752 = "Png/issues/Issue_2752.png"; + // Issue 2924: https://github.com/SixLabors/ImageSharp/issues/2924 + public const string Issue2924 = "Png/issues/Issue_2924.png"; + + // Issue 3000: https://github.com/SixLabors/ImageSharp/issues/3000 + public const string Issue3000 = "Png/issues/issue_3000.png"; + + public static class Icc + { + public const string SRgbGray = "Png/icc-profiles/sRGB_Gray.png"; + public const string SRgbGrayInterlacedRgba32 = "Png/icc-profiles/sRGB_Gray_Interlaced_Rgba32.png"; + public const string SRgbGrayInterlacedRgba64 = "Png/icc-profiles/sRGB_Gray_Interlaced_Rgba64.png"; + public const string Perceptual = "Png/icc-profiles/Perceptual.png"; + public const string PerceptualcLUTOnly = "Png/icc-profiles/Perceptual-cLUT-only.png"; + } + + public static class Cgbi + { + public const string Colors = "Png/cgbi/colors.png"; + public const string Clocks = "Png/cgbi/clocks.png"; + public const string Flecks = "Png/cgbi/flecks.png"; + public const string Screen = "Png/cgbi/screen.png"; + + // Issue 410: https://github.com/SixLabors/ImageSharp/issues/410 + public const string Issue410 = "Png/issues/Issue_410.png"; + + // Synthetic fixtures derived from colors.png to exercise CgBI validation. + public const string BitDepth16 = "Png/cgbi/colors-cgbi-bitdepth16.png"; + public const string Palette = "Png/cgbi/colors-cgbi-palette.png"; + } + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; @@ -181,9 +218,6 @@ public static class Bad // Issue 1047: https://github.com/SixLabors/ImageSharp/issues/1047 public const string Issue1047_BadEndChunk = "Png/issues/Issue_1047.png"; - // Issue 410: https://github.com/SixLabors/ImageSharp/issues/410 - public const string Issue410_MalformedApplePng = "Png/issues/Issue_410.png"; - // Bad bit depth. public const string BitDepthZero = "Png/xd0n2c08.png"; public const string BitDepthThree = "Png/xd3n2c08.png"; @@ -202,6 +236,22 @@ public static class Bad public static class Jpeg { + public static class ICC + { + public const string SRgb = "Jpg/icc-profiles/Momiji-sRGB-yes.jpg"; + public const string AdobeRgb = "Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg"; + public const string ColorMatch = "Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg"; + public const string ProPhoto = "Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg"; + public const string WideRGB = "Jpg/icc-profiles/Momiji-WideRGB-yes.jpg"; + public const string AppleRGB = "Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg"; + public const string CMYK = "Jpg/icc-profiles/issue-129.jpg"; + public const string YCCK = "Jpg/icc-profiles/issue_2723.jpg"; + public const string SRgbGray = "Jpg/icc-profiles/sRGB_Gray.jpg"; + public const string Perceptual = "Jpg/icc-profiles/Perceptual.jpg"; + public const string PerceptualcLUTOnly = "Jpg/icc-profiles/Perceptual-cLUT-only.jpg"; + public const string Issue3064 = "Jpg/icc-profiles/issue-3064.jpg"; + } + public static class Progressive { public const string Fb = "Jpg/progressive/fb.jpg"; @@ -215,7 +265,7 @@ public static class Bad public const string ExifUndefType = "Jpg/progressive/ExifUndefType.jpg"; } - public static readonly string[] All = { Fb, Progress, Festzug }; + public static readonly string[] All = [Fb, Progress, Festzug]; } public static class Baseline @@ -271,12 +321,12 @@ public static class Bad public const string ArithmeticCodingWithRestart = "Jpg/baseline/Calliphora-arithmetic-restart.jpg"; public static readonly string[] All = - { + [ Cmyk, Ycck, Exif, Floorplan, Calliphora, Turtle420, GammaDalaiLamaGray, Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, Ratio1x1, Testorig12bit, YcckSubsample1222 - }; + ]; } public static class Issues @@ -322,8 +372,12 @@ public static class Issues 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 const string Issue2638 = "Jpg/issues/Issue2638.jpg"; public const string Issue2758 = "Jpg/issues/issue-2758.jpg"; + public const string Issue2857 = "Jpg/issues/issue-2857-subsub-ifds.jpg"; + public const string Issue2948 = "Jpg/issues/issue-2948-sos.jpg"; + public const string Issue3118 = "Jpg/issues/issue3118-multiple-sof.jpg"; public static class Fuzz { @@ -452,24 +506,24 @@ public static class Bmp public const string BlackWhitePalletDataMatrix = "Bmp/bit1datamatrix.bmp"; public static readonly string[] BitFields = - { - Rgb32bfdef, + [ + Rgb32bfdef, Rgb32bf, Rgb16565, Rgb16bfdef, Rgb16565pal, - Issue735, - }; + Issue735 + ]; public static readonly string[] Miscellaneous = - { + [ Car, F, NegHeight - }; + ]; public static readonly string[] Benchmark = - { + [ Car, F, NegHeight, @@ -486,7 +540,7 @@ public static class Bmp Bit16, Bit16Inverted, Bit32Rgb - }; + ]; } public static class Gif @@ -507,6 +561,31 @@ public static class 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/peterdn/gif-test-suite.git + // Animated gif with 4 frames, looping forever, no transparency. + public const string AnimatedLoop = "Gif/animated_loop.gif"; + + // Animated gif with 4 frames, interlaced, looping forever, no transparency. + public const string AnimatedLoopInterlaced = "Gif/animated_loop_interlaced.gif"; + + // Transparent gif with 4 frames, loops forever. + public const string AnimatedTransparentLoop = "Gif/animated_transparent_loop.gif"; + + // Transparent gif with 4 frames, loops forever, first frame restore previous. + public const string AnimatedTransparentFirstFrameRestorePrev = "Gif/animated_transparent_firstframerestoreprev_loop.gif"; + + // Transparent gif with 4 transparent frames, loops forever, no dispose + public const string AnimatedTransparentNoRestore = "Gif/animated_transparent_frame_norestore_loop.gif"; + + // Transparent gif with 4 transparent frames, loops forever, restore previous. + public const string AnimatedTransparentRestorePrevious = "Gif/animated_transparent_frame_restoreprev_loop.gif"; + + // Static gif with no animation, no transparency. + public const string StaticNontransparent = "Gif/static_nontransparent.gif"; + + // Static transparent gif with no animation. + public const string StaticTransparent = "Gif/static_transparent.gif"; + // Test images from https://github.com/robert-ancell/pygif/tree/master/test-suite public const string ZeroSize = "Gif/image-zero-size.gif"; public const string ZeroHeight = "Gif/image-zero-height.gif"; @@ -535,10 +614,16 @@ public static class Issues public const string Issue2450_B = "Gif/issues/issue_2450_2.gif"; public const string Issue2198 = "Gif/issues/issue_2198.gif"; public const string Issue2758 = "Gif/issues/issue_2758.gif"; + public const string Issue2866 = "Gif/issues/issue_2866.gif"; + public const string Issue2859_A = "Gif/issues/issue_2859_A.gif"; + public const string Issue2859_B = "Gif/issues/issue_2859_B.gif"; + public const string Issue2953 = "Gif/issues/issue_2953.gif"; + public const string Issue2980 = "Gif/issues/issue_2980.gif"; + public const string Issue3142 = "Gif/issues/issue_3142.gif"; } public static readonly string[] Animated = - { + [ M4nb, Giphy, Cheers, @@ -556,8 +641,9 @@ public static class Issues Issues.BadDescriptorWidth, Issues.Issue1530, Bit18RGBCube, - Global256NoTrans - }; + Global256NoTrans, + Issues.Issue3142 + ]; } public static class Tga @@ -636,6 +722,8 @@ public static class Tga 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 @@ -825,6 +913,20 @@ public static class Lossy public const string Issue2670 = "Webp/issues/Issue2670.webp"; public const string Issue2763 = "Webp/issues/Issue2763.png"; public const string Issue2801 = "Webp/issues/Issue2801.webp"; + public const string Issue2866 = "Webp/issues/Issue2866.webp"; + public const string Issue2925 = "Webp/issues/Issue2925.webp"; + public const string Issue2906 = "Webp/issues/Issue2906.webp"; + } + + public const string AlphaBlend = "Webp/alpha-blend.webp"; + public const string AlphaBlend2 = "Webp/alpha-blend-2.webp"; + public const string AlphaBlend3 = "Webp/alpha-blend-3.webp"; + public const string AlphaBlend4 = "Webp/alpha-blend-4.webp"; + + public static class Icc + { + public const string Perceptual = "Webp/icc-profiles/Perceptual.webp"; + public const string PerceptualcLUTOnly = "Webp/icc-profiles/Perceptual-cLUT-only.webp"; } } @@ -983,6 +1085,36 @@ public static class Tiff public const string QuadTile = "Tiff/quad-tile.tiff"; public const string TiledChunky = "Tiff/rgb_uncompressed_tiled_chunky.tiff"; public const string TiledPlanar = "Tiff/rgb_uncompressed_tiled_planar.tiff"; + public const string TiledRgbaDeflateCompressedWithPredictor = "Tiff/tiled_rgba_deflate_compressed_predictor.tiff"; + public const string TiledRgbDeflateCompressedWithPredictor = "Tiff/tiled_rgb_deflate_compressed_predictor.tiff"; + public const string TiledGrayDeflateCompressedWithPredictor = "Tiff/tiled_gray_deflate_compressed_predictor.tiff"; + public const string TiledGray16BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledGray16BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledGray32BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_32bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledGray32BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_gray_32bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgb48BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_48bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgb48BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_48bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba64BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_64bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba64BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_64bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgb96BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_96bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgb96BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgb_96bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba128BitLittleEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgba128BitBigEndianDeflateCompressedWithPredictor = "Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff"; + public const string TiledRgbaLzwCompressedWithPredictor = "Tiff/tiled_rgba_lzw_compressed_predictor.tiff"; + public const string TiledRgbLzwCompressedWithPredictor = "Tiff/tiled_rgb_lzw_compressed_predictor.tiff"; + public const string TiledGrayLzwCompressedWithPredictor = "Tiff/tiled_gray_lzw_compressed_predictor.tiff"; + public const string TiledGray16BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_gray_16bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledGray16BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_gray_16bit_big_endian_lzw_compressed_predictor.tiff"; + public const string TiledGray32BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_gray_32bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledGray32BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_gray_32bit_big_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgb48BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_rgb_48bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgb48BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_rgb_48bit_big_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgba64BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_rgba_64bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgba64BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_rgba_64bit_big_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgb96BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_rgb_96bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgb96BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_rgb_96bit_big_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgba128BitLittleEndianLzwCompressedWithPredictor = "Tiff/tiled_rgba_128bit_little_endian_lzw_compressed_predictor.tiff"; + public const string TiledRgba128BitBigEndianLzwCompressedWithPredictor = "Tiff/tiled_rgba_128bit_big_endian_lzw_compressed_predictor.tiff"; // Images with alpha channel. public const string Rgba2BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha2bit.tiff"; @@ -1037,6 +1169,7 @@ public static class Tiff public const string Cmyk = "Tiff/Cmyk.tiff"; public const string Cmyk64BitDeflate = "Tiff/cmyk_deflate_64bit.tiff"; public const string CmykLzwPredictor = "Tiff/Cmyk-lzw-predictor.tiff"; + public const string CmykJpeg = "Tiff/Cmyk-jpeg.tiff"; public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string Issues1891 = "Tiff/Issues/Issue1891.tiff"; @@ -1044,9 +1177,14 @@ public static class Tiff public const string Issues2149 = "Tiff/Issues/Group4CompressionWithStrips.tiff"; public const string Issues2255 = "Tiff/Issues/Issue2255.png"; public const string Issues2435 = "Tiff/Issues/Issue2435.tiff"; + public const string Issues2454_A = "Tiff/Issues/Issue2454_A.tif"; + public const string Issues2454_B = "Tiff/Issues/Issue2454_B.tif"; + public const string Issues3031 = "Tiff/Issues/Issue3031.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"; + public const string ExtraSamplesUnspecified = "Tiff/Issues/ExtraSamplesUnspecified.tif"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; @@ -1070,9 +1208,23 @@ public static class Tiff public const string InvalidIptcData = "Tiff/7324fcaff3aad96f27899da51c1bb5d9.tiff"; public const string IptcData = "Tiff/iptc.tiff"; - public static readonly string[] Multiframes = { MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; + public const string Issue2909 = "Tiff/Issues/Issue2909.tiff"; + public const string Issue2983 = "Tiff/Issues/Issue2983.tiff"; + public const string Issue3182ColorMap8Bit = "Tiff/Issues/Issue3182ColorMap8Bit.tiff"; + public const string Issue3182ColorMap16Bit = "Tiff/Issues/Issue3182ColorMap16Bit.tiff"; + + public static readonly string[] Multiframes = [MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ + ]; - public static readonly string[] Metadata = { SampleMetadata }; + public static readonly string[] Metadata = [SampleMetadata]; + + public static class Icc + { + public const string PerceptualCmyk = "Tiff/icc-profiles/Perceptual_CMYK.tiff"; + public const string PerceptualCieLab = "Tiff/icc-profiles/Perceptual_CIELAB.tiff"; + public const string PerceptualRgb8 = "Tiff/icc-profiles/Perceptual_RGB8.tiff"; + public const string PerceptualRgb16 = "Tiff/icc-profiles/Perceptual_RGB16.tiff"; + } } public static class BigTiff @@ -1108,6 +1260,7 @@ public static class Pbm public const string GrayscalePlainNormalized = "Pbm/grayscale_plain_normalized.pgm"; public const string GrayscalePlainMagick = "Pbm/grayscale_plain_magick.pgm"; public const string RgbBinary = "Pbm/00000_00000.ppm"; + public const string RgbBinaryWide = "Pbm/rgb_binary_wide.ppm"; public const string RgbBinaryPrematureEof = "Pbm/00000_00000_premature_eof.ppm"; public const string RgbPlain = "Pbm/rgb_plain.ppm"; public const string RgbPlainNormalized = "Pbm/rgb_plain_normalized.ppm"; @@ -1126,4 +1279,154 @@ public static class Qoi 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 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 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 const string IcoFake = "Icon/ico_fake.cur"; + } + + 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"; + } + + public static class Ani + { + public const string Work = "Ani/Work.ani"; + public const string MultiFramesInEveryIconChunk = "Ani/aero_busy.ani"; + public const string Help = "Ani/Help.ani"; + } + + public static class Exr + { + public const string Benchmark = "Exr/Calliphora_benchmark.exr"; + public const string Uncompressed = "Exr/Calliphora_uncompressed.exr"; + public const string UncompressedRgba = "Exr/Calliphora_uncompressed_rgba.exr"; + public const string UncompressedFloatRgb = "Exr/Calliphora_float_uncompressed.exr"; + public const string UncompressedUintRgb = "Exr/Calliphora_uint32_uncompressed.exr"; + public const string UintRgba = "Exr/rgba_uint_uncompressed.exr"; + public const string Zip = "Exr/Calliphora_zip.exr"; + public const string Zips = "Exr/Calliphora_zips.exr"; + public const string Rle = "Exr/Calliphora_rle.exr"; + public const string B44 = "Exr/Calliphora_b44.exr"; + public const string Pxr24Half = "Exr/Calliphora_half_pxr24.exr"; + public const string Pxr24Float = "Exr/Calliphora_float_pxr24.exr"; + public const string Pxr24Uint = "Exr/Calliphora_uint_pxr24.exr"; + public const string Rgb = "Exr/Calliphora_rgb.exr"; + public const string Gray = "Exr/Calliphora_gray.exr"; + } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index e35f36feec..7c45dd047c 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,10 +13,10 @@ namespace SixLabors.ImageSharp.Tests; internal readonly struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer, - IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer> + IEqualityComparer>, + IEqualityComparer> { private readonly float epsilon; @@ -47,13 +46,6 @@ public bool Equals(Vector2 x, Vector2 y) 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) @@ -87,4 +79,19 @@ public bool Equals(Vector256 x, Vector256 y) && this.Equals(x.GetElement(7), y.GetElement(7)); public int GetHashCode([DisallowNull] Vector256 obj) => obj.GetHashCode(); + + public bool Equals(Vector512 x, Vector512 y) + { + for (int i = 0; i < Vector512.Count; i++) + { + if (!this.Equals(x.GetElement(i), y.GetElement(i))) + { + return false; + } + } + + return true; + } + + public int GetHashCode([DisallowNull] Vector512 obj) => obj.GetHashCode(); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs index 8ff2abd90b..90a98d1c81 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs @@ -13,7 +13,7 @@ public static class ArrayHelper /// The concatenated array public static T[] Concat(params T[][] arrays) { - var result = new T[arrays.Sum(t => t.Length)]; + T[] result = new T[arrays.Sum(t => t.Length)]; int offset = 0; for (int i = 0; i < arrays.Length; i++) { @@ -33,7 +33,7 @@ public static T[] Concat(params T[][] arrays) /// The created array filled with the given value public static T[] Fill(T value, int length) { - var result = new T[length]; + T[] result = new T[length]; for (int i = 0; i < length; i++) { result[i] = value; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 592ebb4ebe..7191f3ba74 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -70,7 +70,7 @@ public override IEnumerable GetData(MethodInfo testMethod) if (!addedRows.Any()) { - addedRows = new[] { Array.Empty() }; + addedRows = [[]]; } bool firstIsProvider = this.FirstIsProvider(testMethod); diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs index d82d5cd910..349db60f20 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBasicTestPatternImagesAttribute.cs @@ -31,5 +31,6 @@ public WithBasicTestPatternImagesAttribute(string memberData, int width, int hei protected override string GetFactoryMethodName(MethodInfo testMethod) => "BasicTestPattern"; - protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => [this.Width, this.Height + ]; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImagesAttribute.cs index e9f57a7abe..ec637a7b6b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImagesAttribute.cs @@ -46,5 +46,6 @@ public WithBlankImagesAttribute(string memberData, int width, int height, PixelT protected override string GetFactoryMethodName(MethodInfo testMethod) => "Blank"; - protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => [this.Width, this.Height + ]; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs index cf472699a7..3392d97ab9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -39,7 +39,7 @@ public WithFileAttribute(string fileName, string dataMemberName, PixelTypes pixe this.fileName = fileName; } - protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.fileName }; + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => [this.fileName]; protected override string GetFactoryMethodName(MethodInfo testMethod) => "File"; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index 57949e7b11..87f6f0479d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -58,7 +58,7 @@ protected override IEnumerable GetAllFactoryMethodArgs(MethodInfo test Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); - var files = (IEnumerable)accessor(); + IEnumerable files = (IEnumerable)accessor(); return files.Select(f => new object[] { f }); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index cd27eaf0ba..59c092b409 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -29,7 +29,7 @@ public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { - return new object[] { testMethod.DeclaringType.FullName, this.memberMethodName }; + return [testMethod.DeclaringType.FullName, this.memberMethodName]; } protected override string GetFactoryMethodName(MethodInfo testMethod) => "Lambda"; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs index a4149f7356..cc4f6f7f3c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs @@ -160,7 +160,7 @@ public WithSolidFilledImagesAttribute( public byte A { get; } protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) - => new object[] { this.Width, this.Height, this.R, this.G, this.B, this.A }; + => [this.Width, this.Height, this.R, this.G, this.B, this.A]; protected override string GetFactoryMethodName(MethodInfo testMethod) => "Solid"; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImagesAttribute.cs index 4b016d5eae..2630acea80 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImagesAttribute.cs @@ -50,5 +50,6 @@ public WithTestPatternImagesAttribute(string memberData, int width, int height, protected override string GetFactoryMethodName(MethodInfo testMethod) => "TestPattern"; - protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => [this.Width, this.Height + ]; } diff --git a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs index d161b80197..216ed95b8b 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 @@ private string DumpToString(Type type) 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 @@ private Type LoadDump(string dump) 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 @@ public static string Serialize(IXunitSerializable serializable) 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/ByteArrayUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs index 963c3c7835..2fb75444e3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteArrayUtility.cs @@ -11,7 +11,7 @@ public static byte[] WithByteOrder(this byte[] bytes, bool isLittleEndian) { if (isLittleEndian != BitConverter.IsLittleEndian) { - var reversedBytes = new byte[bytes.Length]; + byte[] reversedBytes = new byte[bytes.Length]; Array.Copy(bytes, reversedBytes, bytes.Length); Array.Reverse(reversedBytes); return reversedBytes; diff --git a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs index 45e1425c75..548354450c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ByteBuffer.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; public class ByteBuffer { - private readonly List bytes = new List(); + private readonly List bytes = new(); private readonly bool isLittleEndian; public ByteBuffer(bool isLittleEndian) diff --git a/tests/ImageSharp.Tests/TestUtilities/CorruptedMetadataImageFactory.cs b/tests/ImageSharp.Tests/TestUtilities/CorruptedMetadataImageFactory.cs new file mode 100644 index 0000000000..c9d18510da --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/CorruptedMetadataImageFactory.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestDataIcc; + +namespace SixLabors.ImageSharp.Tests.TestUtilities; + +/// +/// Creates encoded images with malformed metadata payloads while keeping the surrounding container valid. +/// +internal static class CorruptedMetadataImageFactory +{ + /// + /// Creates an encoded single-pixel image with a malformed ICC profile. + /// + /// The encoder used to produce the image bytes. + /// The encoded image bytes with a malformed ICC profile payload. + public static byte[] CreateImageWithMalformedIccProfile(IImageEncoder encoder) + { + using Image image = new(1, 1); + image[0, 0] = new Rgba32(255, 0, 0, 255); + image.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.HeaderInvalidSizeSmallArray); + + using MemoryStream stream = new(); + image.Save(stream, encoder); + + return stream.ToArray(); + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index 5a9a72f967..4cd30fc571 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 T DeserializeForXunit(string value) /// 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 @@ -103,7 +104,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -127,6 +128,7 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -146,7 +148,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -170,6 +172,7 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -189,7 +192,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -214,6 +217,8 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -236,7 +241,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -261,6 +266,7 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -282,7 +288,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -307,6 +313,7 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -326,7 +333,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -350,6 +357,7 @@ public static void RunWithHwIntrinsicsFeature( /// 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. @@ -371,7 +379,7 @@ public static void RunWithHwIntrinsicsFeature( ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -395,10 +403,10 @@ public static void RunWithHwIntrinsicsFeature( 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,50 @@ internal static Dictionary ToFeatureKeyValueCollection(thi } /// -/// 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, + DisableSSE42 = 1L << 1, + DisableAVX = 1L << 2, + DisableAVX2 = 1L << 3, + DisableAVX512F = 1L << 4, + DisableAVX512v2 = 1L << 5, + DisableAVX512v3 = 1L << 6, + DisableAVX10v1 = 1L << 7, + DisableAVX10v2 = 1L << 8, + DisableAPX = 1L << 9, + DisableAES = 1L << 10, + DisableAVX512VP2INTERSECT = 1L << 11, + DisableAVXIFMA = 1L << 12, + DisableAVXVNNI = 1L << 13, + DisableAVXVNNIINT = 1L << 14, + DisableGFNI = 1L << 15, + DisableSHA = 1L << 16, + DisableVAES = 1L << 17, + DisableWAITPKG = 1L << 18, + DisableX86Serialize = 1 << 19, + + // Arm64 + DisableArm64Aes = 1L << 20, + DisableArm64Atomics = 1L << 21, + DisableArm64Crc32 = 1L << 22, + DisableArm64Dczva = 1L << 23, + DisableArm64Dp = 1L << 24, + DisableArm64Rdm = 1L << 25, + DisableArm64Sha1 = 1L << 26, + DisableArm64Sha256 = 1L << 27, + DisableArm64Sve = 1L << 28, + DisableArm64Sve2 = 1L << 29, + + // RISC-V64 + DisableRiscV64Zba = 1L << 30, + DisableRiscV64Zbb = 1L << 31, + AllowAll = 1L << 32, } diff --git a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs index 2a7b42f6b4..deac649e43 100644 --- a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs @@ -6,13 +6,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; public class GraphicsOptionsComparer : IEqualityComparer { public bool Equals(GraphicsOptions x, GraphicsOptions y) - { - return x.AlphaCompositionMode == y.AlphaCompositionMode - && x.Antialias == y.Antialias - && x.AntialiasSubpixelDepth == y.AntialiasSubpixelDepth - && x.BlendPercentage == y.BlendPercentage - && x.ColorBlendingMode == y.ColorBlendingMode; - } + => x.AlphaCompositionMode == y.AlphaCompositionMode + && x.Antialias == y.Antialias + && x.BlendPercentage == y.BlendPercentage + && x.ColorBlendingMode == y.ColorBlendingMode; public int GetHashCode(GraphicsOptions obj) => obj.GetHashCode(); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index aa8ab397d2..c6bb38dd83 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -9,14 +8,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; public class ExactImageComparer : ImageComparer { - public static ExactImageComparer Instance { get; } = new ExactImageComparer(); + public static ExactImageComparer Instance { get; } = new(); public override ImageSimilarityReport CompareImagesOrFrames( int index, ImageFrame expected, ImageFrame actual) { - if (expected.Size() != actual.Size()) + if (expected.Size != actual.Size) { throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); } @@ -24,10 +23,10 @@ public override ImageSimilarityReport CompareImagesOrFrames(); + List differences = []; Configuration configuration = expected.Configuration; Buffer2D expectedBuffer = expected.PixelBuffer; Buffer2D actualBuffer = actual.PixelBuffer; @@ -47,7 +46,7 @@ public override ImageSimilarityReport CompareImagesOrFrames> CompareImages where TPixelA : unmanaged, IPixel where TPixelB : unmanaged, IPixel { - List> result = new(); + List> result = []; int expectedFrameCount = actual.Frames.Count; if (predicate != null) @@ -147,7 +147,7 @@ public static void VerifySimilarityIgnoreRegion( IEnumerable> reports = comparer.CompareImages(expected, actual); if (reports.Any()) { - var cleanedReports = new List>(reports.Count()); + List> cleanedReports = new(reports.Count()); foreach (ImageSimilarityReport r in reports) { IEnumerable outsideChanges = r.Differences.Where( diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs index f5c70b0885..05f65cfbbc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs @@ -15,13 +15,10 @@ public static void CompareWithReferenceDecoder( float compareTolerance = 0.01f) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { - string path = TestImageProvider.GetFilePathOrNull(provider); - if (path == null) - { - throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); - } + string path = TestImageProvider.GetFilePathOrNull(provider) + ?? throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); - var testFile = TestFile.Create(path); + TestFile testFile = TestFile.Create(path); using Image magickImage = DecodeWithMagick(new FileInfo(testFile.FullPath)); if (useExactComparer) { @@ -38,25 +35,23 @@ public static Image DecodeWithMagick(FileInfo fileInfo) { Configuration configuration = Configuration.Default.Clone(); configuration.PreferContiguousImageBuffers = true; - using (var magickImage = new MagickImage(fileInfo)) - { - magickImage.AutoOrient(); - var result = new Image(configuration, magickImage.Width, magickImage.Height); + using MagickImage magickImage = new(fileInfo); + magickImage.AutoOrient(); + Image result = new(configuration, (int)magickImage.Width, (int)magickImage.Height); - Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels)); + Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels)); - using (IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe()) - { - byte[] data = pixels.ToByteArray(PixelMapping.RGBA); - - PixelOperations.Instance.FromRgba32Bytes( - configuration, - data, - resultPixels.Span, - resultPixels.Length); - } + using (IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe()) + { + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); - return result; + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels.Span, + resultPixels.Length); } + + return result; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs index c50ae5e219..1594258e04 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -61,7 +61,7 @@ public override string ToString() private string PrintDifference() { - var sb = new StringBuilder(); + StringBuilder sb = new(); if (this.TotalNormalizedDifference.HasValue) { sb.AppendLine(); @@ -103,7 +103,7 @@ public ImageSimilarityReport( } public static ImageSimilarityReport Empty => - new ImageSimilarityReport(0, null, null, Enumerable.Empty(), 0f); + new(0, null, null, Enumerable.Empty(), 0f); public new ImageFrame ExpectedImage => (ImageFrame)base.ExpectedImage; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs index 93ed4c6fff..47cf7e6a5d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -58,7 +57,7 @@ public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshol public override ImageSimilarityReport CompareImagesOrFrames(int index, ImageFrame expected, ImageFrame actual) { - if (expected.Size() != actual.Size()) + if (expected.Size != actual.Size) { throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); } @@ -66,12 +65,12 @@ public override ImageSimilarityReport CompareImagesOrFrames(); + List differences = []; Configuration configuration = expected.Configuration; Buffer2D expectedBuffer = expected.PixelBuffer; Buffer2D actualBuffer = actual.PixelBuffer; @@ -90,7 +89,7 @@ public override ImageSimilarityReport CompareImagesOrFrames this.PerPixelManhattanThreshold) { - var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]); + PixelDifference diff = new(new Point(x, y), aBuffer[x], bBuffer[x]); differences.Add(diff); totalDifference += d; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index 1e3ad3a5d5..ee1d274716 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; @@ -8,11 +8,14 @@ namespace SixLabors.ImageSharp.Tests; public abstract partial class TestImageProvider : IXunitSerializable { - public virtual TPixel GetExpectedBasicTestPatternPixelAt(int x, int y) + [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public TestImageProvider() { - throw new NotSupportedException("GetExpectedBasicTestPatternPixelAt(x,y) only works with BasicTestPattern"); } + public virtual TPixel GetExpectedBasicTestPatternPixelAt(int x, int y) + => throw new NotSupportedException("GetExpectedBasicTestPatternPixelAt(x,y) only works with BasicTestPattern"); + private class BasicTestPatternProvider : BlankProvider { private static readonly TPixel TopLeftColor = Color.Red.ToPixel(); @@ -36,7 +39,7 @@ public BasicTestPatternProvider() 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; @@ -46,16 +49,16 @@ public override Image GetImage() { 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); } }); @@ -71,17 +74,10 @@ public override TPixel GetExpectedBasicTestPatternPixelAt(int x, int y) { return x < midX ? TopLeftColor : TopRightColor; } - else - { - return x < midX ? BottomLeftColor : BottomRightColor; - } - } - private static TPixel GetBottomRightColor() - { - TPixel bottomRightColor = default; - bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f)); - return bottomRightColor; + return x < midX ? BottomLeftColor : BottomRightColor; } + + private static TPixel GetBottomRightColor() => TPixel.FromUnassociatedScaledVector4(new Vector4(1f, 0f, 1f, 0.5f)); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 5a31707bf9..c8c3e09c34 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -33,7 +33,7 @@ public BlankProvider() protected int Width { get; private set; } - public override Image GetImage() => new Image(this.Configuration, this.Width, this.Height); + public override Image GetImage() => new(this.Configuration, this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3652d77a1e..3f6e78a348 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -45,7 +45,7 @@ private static Dictionary GetDecoderParameters( { Type type = options.GetType(); - var data = new Dictionary(); + Dictionary data = new(); while (type != null && type != typeof(object)) { @@ -196,7 +196,7 @@ public override Image GetImage(IImageDecoder decoder, DecoderOptions opt return this.DecodeImage(decoder, options); } - var key = new Key(this.PixelType, this.FilePath, decoder, options, null); + Key key = new(this.PixelType, this.FilePath, decoder, options, null); Image cachedImage = Cache.GetOrAdd(key, _ => this.DecodeImage(decoder, options)); return cachedImage.Clone(this.Configuration); @@ -233,7 +233,7 @@ public override Image GetImage(ISpecializedImageDecoder decoder, T return this.DecodeImage(decoder, options); } - var key = new Key(this.PixelType, this.FilePath, decoder, options.GeneralOptions, options); + Key key = new(this.PixelType, this.FilePath, decoder, options.GeneralOptions, options); Image cachedImage = Cache.GetOrAdd(key, _ => this.DecodeImage(decoder, options)); return cachedImage.Clone(this.Configuration); @@ -270,7 +270,7 @@ private Image DecodeImage(IImageDecoder decoder, DecoderOptions options) { options.SetConfiguration(this.Configuration); - var testFile = TestFile.Create(this.FilePath); + TestFile testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); return decoder.Decode(options, stream); } @@ -280,7 +280,7 @@ private Image DecodeImage(ISpecializedImageDecoder decoder, T opti { options.GeneralOptions.SetConfiguration(this.Configuration); - var testFile = TestFile.Create(this.FilePath); + TestFile testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); return decoder.Decode(options, stream); } @@ -288,7 +288,7 @@ private Image DecodeImage(ISpecializedImageDecoder decoder, T opti public static string GetFilePathOrNull(ITestImageProvider provider) { - var fileProvider = provider as FileProvider; + FileProvider fileProvider = provider as FileProvider; return fileProvider?.FilePath; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs index 390195274f..6da5b75f0d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs @@ -54,7 +54,7 @@ public override void Deserialize(IXunitSerializationInfo info) private Func> GetFactory() { - var declaringType = Type.GetType(this.declaringTypeName); + Type declaringType = Type.GetType(this.declaringTypeName); MethodInfo m = declaringType.GetMethod(this.methodName); Type pixelType = typeof(TPixel); Type imgType = typeof(Image<>).MakeGenericType(pixelType); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 8c5101013f..d78edf5bc3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -51,7 +51,7 @@ public override string SourceFileOrDescription 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; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 8f22fb2b29..ab624126b9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -88,10 +88,10 @@ public static TestImageProvider Solid( public abstract Image GetImage(); public Image GetImage(IImageDecoder decoder) - => this.GetImage(decoder, new()); + => this.GetImage(decoder, new DecoderOptions()); public Task> GetImageAsync(IImageDecoder decoder) - => this.GetImageAsync(decoder, new()); + => this.GetImageAsync(decoder, new DecoderOptions()); public virtual Image GetImage(IImageDecoder decoder, DecoderOptions options) => throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 63307f7e21..12a7ad4e25 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 override Image GetImage() { 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 @@ private static void DrawTestPattern(Image image) /// /// 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 @@ private static void VerticalBars(Buffer2D pixels) if (x % stride == 0) { p++; - p = p % PinkBluePixels.Length; + p %= PinkBluePixels.Length; } pixels[x, y] = PinkBluePixels[p]; @@ -107,12 +110,13 @@ private static void VerticalBars(Buffer2D pixels) /// /// 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,64 @@ private static void BlackWhiteChecker(Buffer2D pixels) 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); + // Round-trip the primaries through TPixel so this fixture still demonstrates the format's color quantization, while + // keeping the working vectors unassociated because the loop replaces alpha independently of RGB. + Vector4 red = Color.Red.ToPixel().ToUnassociatedScaledVector4(); + Vector4 green = Color.Green.ToPixel().ToUnassociatedScaledVector4(); + Vector4 blue = Color.Blue.ToPixel().ToUnassociatedScaledVector4(); 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.FromUnassociatedScaledVector4(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.FromUnassociatedScaledVector4(green); for (int y = topBand; y < topBand + height; y++) { pixels[x, y] = c; } - topBand = topBand + height; - c.FromVector4(blue); + topBand += height; + c = TPixel.FromUnassociatedScaledVector4(blue); for (int y = topBand; y < bottom; y++) { pixels[x, y] = c; @@ -190,6 +195,7 @@ private static void TransparentGradients(Buffer2D pixels) /// 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 +205,14 @@ private static void Rainbow(Buffer2D pixels) 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 2243c852d2..c7883fac15 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; @@ -49,8 +50,8 @@ private string GetTestOutputFileNameImpl( } string fn = appendSourceFileOrDescription - ? Path.GetFileNameWithoutExtension(this.SourceFileOrDescription) - : string.Empty; + ? Path.GetFileNameWithoutExtension(this.SourceFileOrDescription) + : string.Empty; if (string.IsNullOrWhiteSpace(extension)) { @@ -62,7 +63,7 @@ private string GetTestOutputFileNameImpl( extension = ".bmp"; } - extension = extension.ToLower(); + extension = extension.ToLowerInvariant(); if (extension[0] != '.') { @@ -86,7 +87,7 @@ private string GetTestOutputFileNameImpl( } } - details = details ?? string.Empty; + details ??= string.Empty; if (details != string.Empty) { details = '_' + details; @@ -277,8 +278,7 @@ public static void ModifyPixel(ImageFrame img, int x, int y, byt where TPixel : unmanaged, IPixel { TPixel pixel = img[x, y]; - Rgba64 rgbaPixel = default; - rgbaPixel.FromScaledVector4(pixel.ToScaledVector4()); + Rgba64 rgbaPixel = Rgba64.FromUnassociatedScaledVector4(pixel.ToUnassociatedScaledVector4()); ushort change = (ushort)Math.Round((perChannelChange / 255F) * 65535F); if (rgbaPixel.R + perChannelChange <= 255) @@ -317,7 +317,6 @@ public static void ModifyPixel(ImageFrame img, int x, int y, byt rgbaPixel.A -= perChannelChange; } - pixel.FromRgba64(rgbaPixel); - img[x, y] = pixel; + img[x, y] = TPixel.FromRgba64(rgbaPixel); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs index de42ba0ccf..81f16cf6b9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs +++ b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs @@ -30,7 +30,7 @@ public void Measure(int times, Action action, [CallerMemberName] string operatio this.Output?.WriteLine($"{operationName} X {times} ..."); } - var sw = Stopwatch.StartNew(); + Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < times; i++) { @@ -60,7 +60,7 @@ public class MeasureGuard : IDisposable { private readonly string operation; - private readonly Stopwatch stopwatch = new Stopwatch(); + private readonly Stopwatch stopwatch = new(); public MeasureGuard(ITestOutputHelper output, string operation) { diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs index ae4af24f14..d1149dd004 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedMemoryStream.cs @@ -6,9 +6,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; /// -/// is a variant of that derives from instead of encapsulating it. -/// It is used to test decoder REacellation without relying on of our standard prefetching of arbitrary streams to -/// on asynchronous path. +/// is a variant of that derives from +/// instead of encapsulating it. +/// It is used to test decoder cancellation without relying on of our standard prefetching of arbitrary streams +/// to on asynchronous path. /// public class PausedMemoryStream : MemoryStream, IPausedStream { @@ -108,11 +109,11 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance public override bool CanWrite => base.CanWrite; - public override void Flush() => this.Await(() => base.Flush()); + public override void Flush() => this.Await(base.Flush); public override int Read(byte[] buffer, int offset, int count) => this.Await(() => base.Read(buffer, offset, count)); - public override long Seek(long offset, SeekOrigin origin) => this.Await(() => base.Seek(offset, origin)); + public override long Seek(long offset, SeekOrigin loc) => this.Await(() => base.Seek(offset, loc)); public override void SetLength(long value) => this.Await(() => base.SetLength(value)); @@ -124,7 +125,7 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance public override void WriteByte(byte value) => this.Await(() => base.WriteByte(value)); - public override int ReadByte() => this.Await(() => base.ReadByte()); + public override int ReadByte() => this.Await(base.ReadByte); public override void CopyTo(Stream destination, int bufferSize) { diff --git a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs index 3c780f3474..42ed6b0d5e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PausedStream.cs @@ -115,7 +115,7 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance public override long Position { get => this.innerStream.Position; set => this.innerStream.Position = value; } - public override void Flush() => this.Await(() => this.innerStream.Flush()); + public override void Flush() => this.Await(this.innerStream.Flush); public override int Read(byte[] buffer, int offset, int count) => this.Await(() => this.innerStream.Read(buffer, offset, count)); @@ -131,7 +131,7 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance public override void WriteByte(byte value) => this.Await(() => this.innerStream.WriteByte(value)); - public override int ReadByte() => this.Await(() => this.innerStream.ReadByte()); + public override int ReadByte() => this.Await(this.innerStream.ReadByte); protected override void Dispose(bool disposing) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 80b5536ebd..d72556b0e3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -5,6 +5,12 @@ using ImageMagick; using ImageMagick.Formats; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Exr; +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 +19,36 @@ 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(PngFormat.Instance); + + public static MagickReferenceDecoder Bmp { get; } = new(BmpFormat.Instance); + + public static MagickReferenceDecoder Jpeg { get; } = new(JpegFormat.Instance); + + public static MagickReferenceDecoder Tiff { get; } = new(TiffFormat.Instance); + + public static MagickReferenceDecoder WebP { get; } = new(WebpFormat.Instance); - public static MagickReferenceDecoder Instance { get; } = new(); + public static MagickReferenceDecoder Exr { get; } = new(ExrFormat.Instance); protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { + ImageMetadata metadata = new(); + Configuration configuration = options.Configuration; BmpReadDefines bmpReadDefines = new() { @@ -38,32 +61,44 @@ protected override Image Decode(DecoderOptions options, Stream s MagickReadSettings settings = new() { - FrameCount = (int)options.MaxFrames + FrameCount = options.MaxFrames }; settings.SetDefines(bmpReadDefines); settings.SetDefines(pngReadDefines); using MagickImageCollection magickImageCollection = new(stream, settings); - List> framesList = new(); + int imageWidth = (int)magickImageCollection.Max(x => x.Width); + int imageHeight = (int)magickImageCollection.Max(x => x.Height); + + List> framesList = []; foreach (IMagickImage magicFrame in magickImageCollection) { - ImageFrame frame = new(configuration, magicFrame.Width, magicFrame.Height); + ImageFrame frame = new(configuration, imageWidth, imageHeight); framesList.Add(frame); - MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; + Buffer2DRegion buffer = frame.PixelBuffer.GetRegion( + (int)(imageWidth - magicFrame.Width), + (int)(imageHeight - magicFrame.Height), + (int)magicFrame.Width, + (int)magicFrame.Height); using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe(); if (magicFrame.Depth is 12 or 10 or 8 or 6 or 5 or 4 or 3 or 2 or 1) { byte[] data = pixels.ToByteArray(PixelMapping.RGBA); - FromRgba32Bytes(configuration, data, framePixels); + FromRgba32Bytes(configuration, data, buffer); } - else if (magicFrame.Depth is 16 or 14) + else if (magicFrame.Depth is 14 or 16 or 32) { + 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); + FromRgba64Bytes(configuration, bytes, buffer); } else { @@ -71,7 +106,7 @@ protected override Image Decode(DecoderOptions options, Stream s } } - return new Image(configuration, new ImageMetadata(), framesList); + return ReferenceCodecUtilities.EnsureDecodedMetadata(new Image(configuration, metadata, framesList), this.imageFormat); } protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) @@ -80,35 +115,47 @@ protected override Image Decode(DecoderOptions options, Stream stream, Cancellat 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 ImageInfo(image.Size, metadata, new List(image.Frames.Select(x => x.Metadata))) + { + PixelType = metadata.GetDecodedPixelTypeInfo() + }; } - private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) + private static void FromRgba32Bytes( + Configuration configuration, + Span rgbaBytes, + Buffer2DRegion destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { Span sourcePixels = MemoryMarshal.Cast(rgbaBytes); - foreach (Memory m in destinationGroup) + for (int y = 0; y < destinationGroup.Height; y++) { - Span destBuffer = m.Span; + Span destBuffer = destinationGroup.DangerousGetRowSpan(y); PixelOperations.Instance.FromRgba32( configuration, sourcePixels[..destBuffer.Length], destBuffer); + sourcePixels = sourcePixels[destBuffer.Length..]; } } - private static void FromRgba64Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) + private static void FromRgba64Bytes( + Configuration configuration, + Span rgbaBytes, + Buffer2DRegion destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { - foreach (Memory m in destinationGroup) + for (int y = 0; y < destinationGroup.Height; y++) { - Span destBuffer = m.Span; + Span destBuffer = destinationGroup.DangerousGetRowSpan(y); PixelOperations.Instance.FromRgba64Bytes( configuration, rgbaBytes, destBuffer, destBuffer.Length); + rgbaBytes = rgbaBytes[(destBuffer.Length * 8)..]; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ReferenceCodecUtilities.cs new file mode 100644 index 0000000000..e48116fed3 --- /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/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 04f59979f7..84be9e3a9a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -27,7 +27,7 @@ internal static unsafe Image From32bppArgbSystemDrawingBitmap(Bi int w = bmp.Width; int h = bmp.Height; - var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + System.Drawing.Rectangle fullRect = new(0, 0, w, h); if (bmp.PixelFormat != PixelFormat.Format32bppArgb) { @@ -37,7 +37,7 @@ internal static unsafe Image From32bppArgbSystemDrawingBitmap(Bi } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); - var image = new Image(w, h); + Image image = new(w, h); try { byte* sourcePtrBase = (byte*)data.Scan0; @@ -86,7 +86,7 @@ internal static unsafe Image From24bppRgbSystemDrawingBitmap(Bit int w = bmp.Width; int h = bmp.Height; - var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + System.Drawing.Rectangle fullRect = new(0, 0, w, h); if (bmp.PixelFormat != PixelFormat.Format24bppRgb) { @@ -96,7 +96,7 @@ internal static unsafe Image From24bppRgbSystemDrawingBitmap(Bit } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); - var image = new Image(w, h); + Image image = new(w, h); try { byte* sourcePtrBase = (byte*)data.Scan0; @@ -138,8 +138,8 @@ internal static unsafe Bitmap To32bppArgbSystemDrawingBitmap(Image this.imageFormat = imageFormat; + + public static SystemDrawingReferenceDecoder Png { get; } = new(PngFormat.Instance); + + public static SystemDrawingReferenceDecoder Bmp { get; } = new(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 ImageInfo(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 @@ protected override Image Decode(DecoderOptions options, Stream s 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 d8dda2eea8..f9769b891e 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; @@ -14,9 +15,9 @@ public class SystemDrawingReferenceEncoder : IImageEncoder public SystemDrawingReferenceEncoder(ImageFormat imageFormat) => this.imageFormat = imageFormat; - public static SystemDrawingReferenceEncoder Png { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Png); + public static SystemDrawingReferenceEncoder Png { get; } = new(ImageFormat.Png); - public static SystemDrawingReferenceEncoder Bmp { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Bmp); + public static SystemDrawingReferenceEncoder Bmp { get; } = new(ImageFormat.Bmp); public bool SkipMetadata { get; init; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ScaledRgbaVectorP.cs b/tests/ImageSharp.Tests/TestUtilities/ScaledRgbaVectorP.cs new file mode 100644 index 0000000000..aed4ef243d --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ScaledRgbaVectorP.cs @@ -0,0 +1,271 @@ +// 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; + +/// +/// A test pixel format that stores associated scaled components as 32-bit floating-point values. +/// +/// +/// Its native vector range differs from its scaled range so processor tests also detect missing +/// conversion boundaries without introducing packed-pixel quantization. +/// +[StructLayout(LayoutKind.Sequential)] +internal struct ScaledRgbaVectorP : IPixel +{ + private Vector4 scaled; + + public ScaledRgbaVectorP(Vector4 scaled) => this.scaled = Numerics.Clamp(scaled, Vector4.Zero, Vector4.One); + + public readonly Rgba32 ToRgba32() + { + Vector4 unassociated = this.scaled; + Numerics.UnPremultiply(ref unassociated); + return Rgba32.FromScaledVector4(unassociated); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.scaled; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => (this.scaled * 2F) - Vector4.One; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedScaledVector4() + { + Vector4 vector = this.scaled; + Numerics.UnPremultiply(ref vector); + return vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToUnassociatedVector4() + { + Vector4 vector = this.ToUnassociatedScaledVector4(); + + // The native vector is an affine view of the scaled data, so association changes occur before mapping the result. + return (vector * 2F) - Vector4.One; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToAssociatedVector4() => this.ToVector4(); + + public static PixelTypeInfo GetPixelTypeInfo() + => PixelTypeInfo.Create( + PixelComponentInfo.Create(4, 32, 32, 32, 32), + PixelColorType.RGB | PixelColorType.Alpha, + PixelAlphaRepresentation.Associated); + + public static PixelOperations CreatePixelOperations() => new ScaledPixelOperations(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromScaledVector4(Vector4 source) => new(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromVector4(Vector4 source) => new((source + Vector4.One) * .5F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromAssociatedScaledVector4(Vector4 source) + { + // Clamp logical color before restoring association so out-of-range alpha cannot permanently darken it. + Numerics.UnPremultiply(ref source); + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + Numerics.Premultiply(ref source); + return new(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromUnassociatedVector4(Vector4 source) + { + // The native vector is affine, so map it to scaled space before changing alpha representation. + source = (source + Vector4.One) * .5F; + return FromUnassociatedScaledVector4(source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromAssociatedVector4(Vector4 source) + { + // The native vector is affine, so map it to scaled space before changing alpha representation. + source = (source + Vector4.One) * .5F; + return FromAssociatedScaledVector4(source); + } + + public static ScaledRgbaVectorP FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public static ScaledRgbaVectorP FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4()); + + public override readonly bool Equals(object obj) => obj is ScaledRgbaVectorP other && this.Equals(other); + + public readonly bool Equals(ScaledRgbaVectorP other) => this.scaled.Equals(other.scaled); + + public override readonly int GetHashCode() => this.scaled.GetHashCode(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScaledRgbaVectorP FromUnassociatedScaledVector4(Vector4 source) + { + source = Numerics.Clamp(source, Vector4.Zero, Vector4.One); + Numerics.Premultiply(ref source); + return new ScaledRgbaVectorP(source); + } + + /// + /// Provides representation-aware bulk operations for the floating-point test pixel. + /// + private sealed class ScaledPixelOperations : AssociatedAlphaPixelOperations + { + /// + protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Span vectors = destination[..source.Length]; + + // The test pixel contains one Vector4 field, so the cast copies its associated storage without changing representation. + MemoryMarshal.Cast(source).CopyTo(vectors); + Numerics.UnPremultiply(vectors); + + // Association is defined in scaled space, so map the unassociated result to the test format's native [-1, 1] range afterwards. + for (int i = 0; i < vectors.Length; i++) + { + vectors[i] = (vectors[i] * 2F) - Vector4.One; + } + } + + /// + protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Span vectors = destination[..source.Length]; + MemoryMarshal.Cast(source).CopyTo(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + vectors[i] = (vectors[i] * 2F) - Vector4.One; + } + } + + /// + protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Span vectors = destination[..source.Length]; + MemoryMarshal.Cast(source).CopyTo(vectors); + Numerics.UnPremultiply(vectors); + } + + /// + protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + MemoryMarshal.Cast(source).CopyTo(destination); + } + + /// + protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + MapNativeToScaled(source); + Numerics.Clamp(MemoryMarshal.Cast(source), 0F, 1F); + Numerics.Premultiply(source); + MemoryMarshal.Cast(source).CopyTo(destination); + } + + /// + protected override void FromAssociatedVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + MapNativeToScaled(source); + Numerics.UnPremultiply(source); + Numerics.Clamp(MemoryMarshal.Cast(source), 0F, 1F); + Numerics.Premultiply(source); + MemoryMarshal.Cast(source).CopyTo(destination); + } + + /// + protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // This test format stores floating-point alpha without quantization, so clamping and premultiplication can use the shared SIMD span operations directly. + Numerics.Clamp(MemoryMarshal.Cast(source), 0F, 1F); + Numerics.Premultiply(source); + + // Source now matches the pixel's sole associated Vector4 field and can be copied without a scalar conversion loop. + MemoryMarshal.Cast(source).CopyTo(destination); + } + + /// + protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + destination = destination[..source.Length]; + + // Recovering the logical color before clamping preserves it when alpha lies outside the scaled range; premultiplication then restores the associated representation. + Numerics.UnPremultiply(source); + Numerics.Clamp(MemoryMarshal.Cast(source), 0F, 1F); + Numerics.Premultiply(source); + MemoryMarshal.Cast(source).CopyTo(destination); + } + + /// + /// Maps vectors from the test format's native numeric range to its scaled range. + /// + /// The vectors to convert in place. + private static void MapNativeToScaled(Span vectors) + { + // Native values use [-1, 1], so the affine inverse maps them to [0, 1] before changing alpha representation. + for (int i = 0; i < vectors.Length; i++) + { + vectors[i] = (vectors[i] + Vector4.One) * .5F; + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/SixLaborsXunitTestFramework.cs b/tests/ImageSharp.Tests/TestUtilities/SixLaborsXunitTestFramework.cs index edae08ce16..d8b0ff7856 100644 --- a/tests/ImageSharp.Tests/TestUtilities/SixLaborsXunitTestFramework.cs +++ b/tests/ImageSharp.Tests/TestUtilities/SixLaborsXunitTestFramework.cs @@ -18,7 +18,7 @@ public class SixLaborsXunitTestFramework : XunitTestFramework public SixLaborsXunitTestFramework(IMessageSink messageSink) : base(messageSink) { - var message = new DiagnosticMessage(HostEnvironmentInfo.GetInformation()); + DiagnosticMessage message = new(HostEnvironmentInfo.GetInformation()); messageSink.OnMessage(message); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs index 31de3909e1..53f0f06486 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -20,7 +20,7 @@ internal static class TestDataGenerator /// The . public static float[] GenerateRandomFloatArray(this Random rnd, int length, float minVal, float maxVal) { - var values = new float[length]; + float[] values = new float[length]; RandomFill(rnd, values, minVal, maxVal); @@ -45,7 +45,7 @@ public static void RandomFill(this Random rnd, Span destination, float mi /// The . public static Vector4[] GenerateRandomVectorArray(this Random rnd, int length, float minVal, float maxVal) { - var values = new Vector4[length]; + Vector4[] values = new Vector4[length]; for (int i = 0; i < length; i++) { @@ -69,7 +69,7 @@ public static Vector4[] GenerateRandomVectorArray(this Random rnd, int length, f /// The . public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int length, float minVal, float maxVal) { - var values = new float[length]; + float[] values = new float[length]; for (int i = 0; i < length; i++) { @@ -87,14 +87,14 @@ public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int lengt /// The . public static byte[] GenerateRandomByteArray(this Random rnd, int length) { - var values = new byte[length]; + byte[] values = new byte[length]; rnd.NextBytes(values); return values; } public static short[] GenerateRandomInt16Array(this Random rnd, int length, short minVal, short maxVal) { - var values = new short[length]; + short[] values = new short[length]; for (int i = 0; i < values.Length; i++) { values[i] = (short)rnd.Next(minVal, maxVal); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 9508de2469..8a40bd6720 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Exr; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; @@ -63,21 +64,22 @@ private static Configuration CreateDefaultConfiguration() new TgaConfigurationModule(), new WebpConfigurationModule(), new TiffConfigurationModule(), + new ExrConfigurationModule(), new QoiConfigurationModule()); IImageEncoder pngEncoder = IsWindows ? SystemDrawingReferenceEncoder.Png : new ImageSharpPngEncoderWithDefaultConfiguration(); IImageEncoder bmpEncoder = IsWindows ? SystemDrawingReferenceEncoder.Bmp : new BmpEncoder(); - // Magick codecs should work on all platforms + // 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/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index 8818830351..5eb5be0d9a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -19,9 +19,9 @@ public static partial class TestEnvironment private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; - private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl); + private static readonly Lazy SolutionDirectoryFullPathLazy = new(GetSolutionDirectoryFullPathImpl); - private static readonly Lazy NetCoreVersionLazy = new Lazy(GetNetCoreVersion); + private static readonly Lazy NetCoreVersionLazy = new(GetNetCoreVersion); static TestEnvironment() => PrepareRemoteExecutor(); @@ -52,8 +52,7 @@ public static partial class TestEnvironment internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; - private static readonly FileInfo TestAssemblyFile = - new FileInfo(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); + private static readonly FileInfo TestAssemblyFile = new(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); private static string GetSolutionDirectoryFullPathImpl() { @@ -215,7 +214,7 @@ private static void EnsureRemoteExecutorIs32Bit() string args = $"{remoteExecutorTmpPath} /32Bit+ /Force"; - var si = new ProcessStartInfo() + ProcessStartInfo si = new() { FileName = corFlagsFile.FullName, Arguments = args, @@ -224,7 +223,7 @@ private static void EnsureRemoteExecutorIs32Bit() RedirectStandardError = true }; - using var proc = Process.Start(si); + using Process proc = Process.Start(si); proc.WaitForExit(); string standardOutput = proc.StandardOutput.ReadToEnd(); string standardError = proc.StandardError.ReadToEnd(); @@ -266,7 +265,7 @@ static FileInfo Find(DirectoryInfo root, string name) private static Version GetNetCoreVersion() { Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; - string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + string[] assemblyPath = assembly.Location.Split(['/', '\\'], StringSplitOptions.RemoveEmptyEntries); int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 3c74b48938..b51e4e2d44 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -59,11 +59,6 @@ public static Image DebugSave( bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) { - if (TestEnvironment.RunsWithCodeCoverage) - { - return image; - } - provider.Utility.SaveTestOutputFile( image, extension, @@ -107,18 +102,15 @@ public static Image DebugSaveMultiFrame( ITestImageProvider provider, object testOutputDetails = null, string extension = "png", + IImageEncoder encoder = null, bool appendPixelTypeToFileName = true, Func predicate = null) where TPixel : unmanaged, IPixel { - if (TestEnvironment.RunsWithCodeCoverage) - { - return image; - } - provider.Utility.SaveTestOutputFileMultiFrame( image, extension, + encoder: encoder, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName, predicate: predicate); @@ -277,6 +269,46 @@ public static Image CompareFirstFrameToReferenceOutput( return image; } + public static Image CompareDebugOutputToReferenceOutputMultiFrame( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + IImageEncoder encoder = null, + bool appendPixelTypeToFileName = true, + Func predicate = null) + where TPixel : unmanaged, IPixel + { + image.DebugSaveMultiFrame( + provider, + testOutputDetails, + extension, + encoder, + appendPixelTypeToFileName, + predicate: predicate); + + using Image debugImage = GetDebugOutputImageMultiFrame( + provider, + image.Frames.Count, + testOutputDetails, + extension, + appendPixelTypeToFileName, + predicate: predicate); + + using Image referenceImage = GetReferenceOutputImageMultiFrame( + provider, + image.Frames.Count, + testOutputDetails, + extension, + appendPixelTypeToFileName, + predicate: predicate); + + comparer.VerifySimilarity(referenceImage, debugImage); + + return image; + } + public static Image CompareToReferenceOutputMultiFrame( this Image image, ITestImageProvider provider, @@ -343,7 +375,55 @@ public static Image GetReferenceOutputImageMultiFrame( appendPixelTypeToFileName, predicate); - List> temporaryFrameImages = new(); + List> temporaryFrameImages = []; + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0].FileName); + + for (int i = 0; i < frameFiles.Length; i++) + { + string path = frameFiles[i].FileName; + if (!File.Exists(path)) + { + throw new FileNotFoundException("Reference output file missing: " + path); + } + + using FileStream stream = File.OpenRead(path); + Image tempImage = decoder.Decode(DecoderOptions.Default, stream); + temporaryFrameImages.Add(tempImage); + } + + Image firstTemp = temporaryFrameImages[0]; + + Image result = new(firstTemp.Width, firstTemp.Height); + + foreach (Image fi in temporaryFrameImages) + { + result.Frames.AddFrame(fi.Frames.RootFrame); + fi.Dispose(); + } + + // Remove the initial empty frame: + result.Frames.RemoveFrame(0); + return result; + } + + public static Image GetDebugOutputImageMultiFrame( + this ITestImageProvider provider, + int frameCount, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true, + Func predicate = null) + where TPixel : unmanaged, IPixel + { + (int Index, string FileName)[] frameFiles = [.. provider.Utility.GetTestOutputFileNamesMultiFrame( + frameCount, + extension, + testOutputDetails, + appendPixelTypeToFileName, + predicate: predicate)]; + + List> temporaryFrameImages = []; IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0].FileName); @@ -425,7 +505,7 @@ public static void CompareBuffers(Span expected, Span actual) public static void CompareBuffers(Buffer2D expected, Buffer2D actual) where T : struct, IEquatable { - Assert.True(expected.Size() == actual.Size(), "Buffer sizes are not equal!"); + Assert.True(expected.Size == actual.Size, "Buffer sizes are not equal!"); for (int y = 0; y < expected.Height; y++) { @@ -692,29 +772,11 @@ internal static AllocatorBufferCapacityConfigurator LimitAllocatorBufferCapacity 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); - - 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; - var v = new Vector4(value, value, value, 1f); - pixels[i].FromVector4(v); - } - - return image; - } - private class MakeOpaqueProcessor : IImageProcessor { public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) @@ -735,7 +797,7 @@ protected override void OnFrameApply(ImageFrame source) 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, @@ -764,14 +826,17 @@ public void Invoke(in RowInterval rows, Span span) for (int y = rows.Min; y < rows.Max; y++) { Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.bounds.Left, this.bounds.Width); - PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale); + PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); + for (int i = 0; i < span.Length; i++) { ref Vector4 v = ref span[i]; v.W = 1F; } - PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, PixelConversionModifiers.Scale); + // Changing opacity must operate on logical RGB. The representation-aware bulk conversion prevents associated + // formats from retaining the darker RGB values that belonged to the old alpha. + PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index fe94cffc43..f9a4c0a6a8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -37,7 +37,7 @@ public void EnableNonThreadSafeLogging() this.returnLog = new List(); } - public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + protected override AllocationTrackedMemoryManager AllocateCore(int length, AllocationOptions options = AllocationOptions.None) { T[] array = this.AllocateArray(length, options); return new BasicArrayBuffer(array, length, this); @@ -46,7 +46,7 @@ public override IMemoryOwner Allocate(int length, AllocationOptions option private T[] AllocateArray(int length, AllocationOptions options) where T : struct { - var array = new T[length + 42]; + T[] array = new T[length + 42]; this.allocationLog?.Add(AllocationRequest.Create(options, length, array)); if (options == AllocationOptions.None) @@ -110,7 +110,7 @@ public ReturnRequest(int hashCodeOfBuffer) /// /// Wraps an array as an instance. /// - private class BasicArrayBuffer : MemoryManager + private class BasicArrayBuffer : AllocationTrackedMemoryManager where T : struct { private readonly TestMemoryAllocator allocator; @@ -159,7 +159,7 @@ public override void Unpin() } /// - protected override void Dispose(bool disposing) + protected override void DisposeCore(bool disposing) { if (disposing) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs index 50e086d579..7f25fd5aa1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs @@ -34,7 +34,7 @@ public override void Unpin() public static TestMemoryManager CreateAsCopyOf(Span copyThisBuffer) { - var pixelArray = new T[copyThisBuffer.Length]; + T[] pixelArray = new T[copyThisBuffer.Length]; copyThisBuffer.CopyTo(pixelArray); return new TestMemoryManager(pixelArray); } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index fb879c7697..174766dafb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -35,14 +35,9 @@ public TestPixel(float red, float green, float blue, float alpha) 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.FromUnassociatedScaledVector4(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 0b792b7fb0..2e2416952d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -19,14 +19,14 @@ 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 }) + .Except([PixelTypes.Undefined, PixelTypes.All]) .ToArray(); static TestUtils() @@ -52,7 +52,7 @@ static 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 byte[] GetRandomBytes(int length, int seed = 42) 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 bool IsEquivalentTo(this Image a, Image b, 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 bool IsEquivalentTo(this Image a, Image b, } 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)) { @@ -136,7 +133,7 @@ public static IEnumerable> ExpandAllTypes(this Pi { if (pixelTypes == PixelTypes.Undefined) { - return Enumerable.Empty>(); + return []; } else if (pixelTypes == PixelTypes.All) { @@ -144,7 +141,7 @@ public static IEnumerable> ExpandAllTypes(this Pi return PixelTypes2ClrTypes; } - var result = new Dictionary(); + Dictionary result = new(); foreach (PixelTypes pt in AllConcretePixelTypes) { if (pixelTypes.HasAll(pt)) @@ -167,7 +164,7 @@ internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck) 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 @@ internal static void RunBufferCapacityLimitProcessorTest( 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 void RunValidatingProcessorTestOnWrappedMemoryImage( 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 @@ internal static void RunRectangleConstrainedValidatingProcessorTest( 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 IResampler GetResampler(string name) 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/BasicSerializerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs index 52447b6c2c..1b5b43f033 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs @@ -51,7 +51,7 @@ public override void Serialize(IXunitSerializationInfo info) [Fact] public void SerializeDeserialize_ShouldPreserveValues() { - var obj = new DerivedObj() { Length = 123.1, Name = "Lol123!", Lives = 7, Strength = 4.8 }; + DerivedObj obj = new() { Length = 123.1, Name = "Lol123!", Lives = 7, Strength = 4.8 }; string str = BasicSerializer.Serialize(obj); BaseObj mirrorBase = BasicSerializer.Deserialize(str); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 34337600eb..ee3d02670e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -14,9 +14,9 @@ public class FeatureTestRunnerTests public static TheoryData Intrinsics => new() { - { HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, new[] { "EnableAES", "AllowAll" } }, - { HwIntrinsics.DisableHWIntrinsic, new[] { "EnableHWIntrinsic" } }, - { HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, new[] { "EnableSSE42", "EnableAVX" } } + { HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, ["EnableAES", "AllowAll"] }, + { HwIntrinsics.DisableHWIntrinsic, ["EnableHWIntrinsic"] }, + { HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, ["EnableSSE42", "EnableAVX"] } }; [Theory] @@ -47,7 +47,7 @@ public void AllowsAllHwIntrinsicFeatures() } FeatureTestRunner.RunWithHwIntrinsicsFeature( - () => Assert.True(Vector.IsHardwareAccelerated), + () => Assert.True(Vector.IsHardwareAccelerated, "Vector hardware acceleration should be enabled when AllowAll is specified."), HwIntrinsics.AllowAll); } @@ -56,21 +56,21 @@ public void CanLimitHwIntrinsicBaseFeatures() { static void AssertDisabled() { - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); } FeatureTestRunner.RunWithHwIntrinsicsFeature( @@ -88,90 +88,70 @@ static void AssertHwIntrinsicsFeatureDisabled(string intrinsic) switch (Enum.Parse(intrinsic)) { case HwIntrinsics.DisableHWIntrinsic: - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); - Assert.False(AdvSimd.IsSupported); - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); - Assert.False(Crc32.IsSupported); - Assert.False(Dp.IsSupported); - Assert.False(Sha1.IsSupported); - Assert.False(Sha256.IsSupported); - break; - case HwIntrinsics.DisableSSE: - Assert.False(Sse.IsSupported); - break; - case HwIntrinsics.DisableSSE2: - Assert.False(Sse2.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(AdvSimd.IsSupported, "Arm64 AdvSimd should be disabled when DisableHWIntrinsic is set."); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableHWIntrinsic is set."); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableHWIntrinsic is set."); break; case HwIntrinsics.DisableAES: - Assert.False(Aes.IsSupported); - break; - case HwIntrinsics.DisablePCLMULQDQ: - Assert.False(Pclmulqdq.IsSupported); - break; - case HwIntrinsics.DisableSSE3: - Assert.False(Sse3.IsSupported); - break; - case HwIntrinsics.DisableSSSE3: - Assert.False(Ssse3.IsSupported); - break; - case HwIntrinsics.DisableSSE41: - Assert.False(Sse41.IsSupported); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableAES is set."); +#if NET10_0_OR_GREATER + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableAES is set (paired disable)."); +#endif break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse42.IsSupported); - break; - case HwIntrinsics.DisablePOPCNT: - Assert.False(Popcnt.IsSupported); +#if NET10_0_OR_GREATER + Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); + Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); + Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); + Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); +#else + Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set."); +#endif break; case HwIntrinsics.DisableAVX: - Assert.False(Avx.IsSupported); - break; - case HwIntrinsics.DisableFMA: - Assert.False(Fma.IsSupported); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set."); break; case HwIntrinsics.DisableAVX2: - Assert.False(Avx2.IsSupported); - break; - case HwIntrinsics.DisableBMI1: - Assert.False(Bmi1.IsSupported); - break; - case HwIntrinsics.DisableBMI2: - Assert.False(Bmi2.IsSupported); - break; - case HwIntrinsics.DisableLZCNT: - Assert.False(Lzcnt.IsSupported); - break; - case HwIntrinsics.DisableArm64AdvSimd: - Assert.False(AdvSimd.IsSupported); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableAVX2 is set."); +#if NET10_0_OR_GREATER + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableAVX2 is set (paired disable)."); +#endif break; case HwIntrinsics.DisableArm64Aes: - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableArm64Aes is set."); break; case HwIntrinsics.DisableArm64Crc32: - Assert.False(Crc32.IsSupported); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableArm64Crc32 is set."); break; case HwIntrinsics.DisableArm64Dp: - Assert.False(Dp.IsSupported); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableArm64Dp is set."); break; case HwIntrinsics.DisableArm64Sha1: - Assert.False(Sha1.IsSupported); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableArm64Sha1 is set."); break; case HwIntrinsics.DisableArm64Sha256: - Assert.False(Sha256.IsSupported); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableArm64Sha256 is set."); break; } } @@ -189,12 +169,12 @@ static void AssertHwIntrinsicsFeatureDisabled(string serializable) { Assert.NotNull(serializable); Assert.NotNull(FeatureTestRunner.DeserializeForXunit(serializable)); - Assert.False(Sse.IsSupported); + Assert.False(Sse42.IsSupported, "SSE42 should be disabled when DisableSSE42 is set (sanity check using serializable param overload)."); } FeatureTestRunner.RunWithHwIntrinsicsFeature( AssertHwIntrinsicsFeatureDisabled, - HwIntrinsics.DisableSSE, + HwIntrinsics.DisableSSE42, new FakeSerializable()); } @@ -209,90 +189,69 @@ static void AssertHwIntrinsicsFeatureDisabled(string serializable, string intrin switch (Enum.Parse(intrinsic)) { case HwIntrinsics.DisableHWIntrinsic: - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); - Assert.False(AdvSimd.IsSupported); - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); - Assert.False(Crc32.IsSupported); - Assert.False(Dp.IsSupported); - Assert.False(Sha1.IsSupported); - Assert.False(Sha256.IsSupported); - break; - case HwIntrinsics.DisableSSE: - Assert.False(Sse.IsSupported); - break; - case HwIntrinsics.DisableSSE2: - Assert.False(Sse2.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(AdvSimd.IsSupported, "Arm64 AdvSimd should be disabled when DisableHWIntrinsic is set."); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableHWIntrinsic is set."); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableHWIntrinsic is set."); break; case HwIntrinsics.DisableAES: - Assert.False(Aes.IsSupported); - break; - case HwIntrinsics.DisablePCLMULQDQ: - Assert.False(Pclmulqdq.IsSupported); - break; - case HwIntrinsics.DisableSSE3: - Assert.False(Sse3.IsSupported); - break; - case HwIntrinsics.DisableSSSE3: - Assert.False(Ssse3.IsSupported); - break; - case HwIntrinsics.DisableSSE41: - Assert.False(Sse41.IsSupported); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableAES is set."); +#if NET10_0_OR_GREATER + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableAES is set (paired disable)."); +#endif break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse42.IsSupported); - break; - case HwIntrinsics.DisablePOPCNT: - Assert.False(Popcnt.IsSupported); +#if NET10_0_OR_GREATER + Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); + Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); + Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); + Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); +#endif + Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set."); break; case HwIntrinsics.DisableAVX: - Assert.False(Avx.IsSupported); - break; - case HwIntrinsics.DisableFMA: - Assert.False(Fma.IsSupported); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set."); break; case HwIntrinsics.DisableAVX2: - Assert.False(Avx2.IsSupported); - break; - case HwIntrinsics.DisableBMI1: - Assert.False(Bmi1.IsSupported); - break; - case HwIntrinsics.DisableBMI2: - Assert.False(Bmi2.IsSupported); - break; - case HwIntrinsics.DisableLZCNT: - Assert.False(Lzcnt.IsSupported); - break; - case HwIntrinsics.DisableArm64AdvSimd: - Assert.False(AdvSimd.IsSupported); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableAVX2 is set."); +#if NET10_0_OR_GREATER + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableAVX2 is set (paired disable)."); +#endif break; case HwIntrinsics.DisableArm64Aes: - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableArm64Aes is set."); break; case HwIntrinsics.DisableArm64Crc32: - Assert.False(Crc32.IsSupported); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableArm64Crc32 is set."); break; case HwIntrinsics.DisableArm64Dp: - Assert.False(Dp.IsSupported); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableArm64Dp is set."); break; case HwIntrinsics.DisableArm64Sha1: - Assert.False(Sha1.IsSupported); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableArm64Sha1 is set."); break; case HwIntrinsics.DisableArm64Sha256: - Assert.False(Sha256.IsSupported); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableArm64Sha256 is set."); break; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs index 4c1a740e2b..349dd258eb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs @@ -31,7 +31,7 @@ public void TolerantImageComparer_ApprovesPerfectSimilarity( { using (Image clone = image.Clone()) { - var comparer = ImageComparer.Tolerant(imageThreshold, pixelThreshold); + ImageComparer comparer = ImageComparer.Tolerant(imageThreshold, pixelThreshold); comparer.VerifySimilarity(image, clone); } } @@ -48,7 +48,7 @@ public void TolerantImageComparer_ApprovesSimilarityBelowTolerance(TestI { ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 1); - var comparer = ImageComparer.Tolerant(); + ImageComparer comparer = ImageComparer.Tolerant(); comparer.VerifySimilarity(image, clone); } } @@ -66,7 +66,7 @@ public void TolerantImageComparer_DoesNotApproveSimilarityAboveTolerance byte perChannelChange = 20; ImagingTestCaseUtility.ModifyPixel(clone, 3, 1, perChannelChange); - var comparer = ImageComparer.Tolerant(); + ImageComparer comparer = ImageComparer.Tolerant(); ImageDifferenceIsOverThresholdException ex = Assert.ThrowsAny( () => comparer.VerifySimilarity(image, clone)); @@ -90,7 +90,7 @@ public void TolerantImageComparer_TestPerPixelThreshold(TestImageProvide ImagingTestCaseUtility.ModifyPixel(clone, 1, 0, 1); ImagingTestCaseUtility.ModifyPixel(clone, 2, 0, 1); - var comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 257 * 3); + ImageComparer comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 257 * 3); comparer.VerifySimilarity(image, clone); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index 81ea77b6b6..b818e80b05 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.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 void MagickDecode_8BitDepthImage_IsEquivalentTo_SystemDrawingResult 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 3a3fcefdbe..a145e7365e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs @@ -22,20 +22,20 @@ public class ReferenceDecoderBenchmarks public const int DefaultExecutionCount = 50; public static readonly string[] PngBenchmarkFiles = - { - TestImages.Png.CalliphoraPartial, + [ + TestImages.Png.CalliphoraPartial, TestImages.Png.Kaboom, TestImages.Png.Bike, TestImages.Png.Splash, TestImages.Png.SplashInterlaced - }; + ]; public static readonly string[] BmpBenchmarkFiles = - { - TestImages.Bmp.NegHeight, + [ + TestImages.Bmp.NegHeight, TestImages.Bmp.Car, TestImages.Bmp.V5Header - }; + ]; public ReferenceDecoderBenchmarks(ITestOutputHelper output) { @@ -47,7 +47,7 @@ public ReferenceDecoderBenchmarks(ITestOutputHelper output) 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 void BenchmarkMagickPngDecoder(TestImageProvider provider 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 void BenchmarkSystemDrawingPngDecoder(TestImageProvider p 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,12 +71,12 @@ public void BenchmarkMagickBmpDecoder(TestImageProvider provider 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) { - var measure = new MeasureFixture(this.Output); + MeasureFixture measure = new(this.Output); measure.Measure( times, () => diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs index e080bda9b6..f0a5f4eb42 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SemaphoreReadMemoryStreamTests.cs @@ -5,8 +5,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests; public class SemaphoreReadMemoryStreamTests { - private readonly SemaphoreSlim continueSemaphore = new SemaphoreSlim(0); - private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new SemaphoreSlim(0); + private readonly SemaphoreSlim continueSemaphore = new(0); + private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new(0); private readonly byte[] buffer = new byte[128]; [Fact] diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs index a89feb3c35..555041890a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Drawing; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; @@ -35,7 +36,7 @@ public void From32bppArgbSystemDrawingBitmap(TestImageProvider d { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); - using var sdBitmap = new System.Drawing.Bitmap(path); + using Bitmap sdBitmap = new(path); using Image image = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap); image.DebugSave(dummyProvider); } @@ -49,7 +50,7 @@ private static string SavePng(TestImageProvider provider, PngCol sourceImage.Mutate(c => c.MakeOpaque()); } - var encoder = new PngEncoder { ColorType = pngColorType }; + PngEncoder encoder = new() { ColorType = pngColorType }; return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); } @@ -65,7 +66,7 @@ public void From32bppArgbSystemDrawingBitmap2(TestImageProvider string path = SavePng(provider, PngColorType.RgbWithAlpha); - using var sdBitmap = new System.Drawing.Bitmap(path); + using Bitmap sdBitmap = new(path); using Image original = provider.GetImage(); using Image resaved = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap(sdBitmap); ImageComparer comparer = ImageComparer.Exact; @@ -80,7 +81,7 @@ public void From24bppRgbSystemDrawingBitmap(TestImageProvider pr string path = SavePng(provider, PngColorType.Rgb); using Image original = provider.GetImage(); - using var sdBitmap = new System.Drawing.Bitmap(path); + using Bitmap sdBitmap = new(path); using Image resaved = SystemDrawingBridge.From24bppRgbSystemDrawingBitmap(sdBitmap); ImageComparer comparer = ImageComparer.Exact; comparer.VerifySimilarity(original, resaved); @@ -93,7 +94,7 @@ public void OpenWithReferenceDecoder(TestImageProvider dummyProv { 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 3dceaf2524..72ed27e8e8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -2,11 +2,12 @@ // Licensed under the Six Labors Split License. using System.Collections.Concurrent; -using SixLabors.ImageSharp.Advanced; 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 @@ -26,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; @@ -81,9 +82,9 @@ public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache( 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); @@ -302,12 +303,11 @@ public void Use_WithSolidFilledImagesAttribute(TestImageProvider 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); @@ -337,7 +337,7 @@ private static void EnsureCustomConfigurationIsApplied(TestImageProvider { using (provider.GetImage()) { - var customConfiguration = Configuration.CreateDefaultInstance(); + Configuration customConfiguration = Configuration.CreateDefaultInstance(); provider.Configuration = customConfiguration; using Image image2 = provider.GetImage(); @@ -367,13 +367,17 @@ public static void DoTestThreadSafe(Action action) 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 ImageInfo(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) @@ -410,13 +414,17 @@ public static void DoTestThreadSafe(Action action) 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 ImageInfo(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) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 33ee68068f..714dbd223d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -21,20 +21,17 @@ public TestUtilityExtensionsTests(ITestOutputHelper output) 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.FromUnassociatedVector4(v); } } diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-3-3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-3-3.png new file mode 100644 index 0000000000..385ac042c5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-3-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d75205909d532dc98da52389c804ff99cb3b796b5657afb521659fe221c2b8f0 +size 122 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-4-4.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-4-4.png new file mode 100644 index 0000000000..ef4aa03b92 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-4-4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a75beaec77378de4abb09317afa56b8e99ecba0d1c8571cad31aa790afb1a687 +size 123 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png index 53ac0ff89f..7fad1fcba1 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbe1ffaf7b801fd92724438cc810fd0c5506e0a907b970c4f0bf5bec3627ca2a -size 551 +oid sha256:60b050406fda4ff347660e71cb28a9dfceb4b39532f62ee96cb61d2671d3cf00 +size 340 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png new file mode 100644 index 0000000000..0d610ae036 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74bf3b8655c7548f28c25b1e467992f691dc429f4b06e85cfd04a3b541825811 +size 478 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png index 2480164d60..b22cbb0196 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b45933471a1af1b6d4112240e1bc6b6187065a872043ddbf917200ce9e8cc84b -size 371 +oid sha256:fbfb3143d96070c58c949e8d1e8d9ddbcf1e7863514489ea2defc65863c84e73 +size 276 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png index da8e446c02..df64eea180 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b66a5f9d8a7f3f2a78b868bec6c7d1deea927b82d81aa6d1677e0461a3920dc9 -size 3800 +oid sha256:120b661bef4adac64d362d8c987b3427cd8140ccac7404d09a16765ba1199434 +size 5191 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png index 4c45ba8c6c..b1e8764c1c 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5fdc46ee866e088e0ec3221145a3d2d954a0bcb6d25cbb4d538978272f34949 -size 4871 +oid sha256:0d668ebe5f8857fd21d7eb9ae86860751a6f3061f6c9f76705ff49216dc07870 +size 6215 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png index 480c07da48..6067f0ccc0 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ae57ca0658b1ffa7aca9031f4ec065ab5a9813fb8a9c5acd221526df6a4f729 -size 9747 +oid sha256:2fb676b3af585e7cbe2efdb893157d5f4e152cf810d0693cafb81596e941e121 +size 9697 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png index d1ea99cf90..69f862a5a9 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5 -size 10470 +oid sha256:afe7ddbff155b918a4eff91af31e01100355c146cb9c8a12ab2496da8b22821d +size 10446 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png index 227f546515..5b88ba5088 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e4cc16c2f1b439f8780dead04db01fed95f8e20b68270ae8e7a988af999e3db -size 10561 +oid sha256:ad76301984e5b54eae374adfe130693667053fbed181847b4c68688fb74c9818 +size 10518 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png index b93742a858..7f08d0dfdf 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06e3966550f1c3ae72796e5522f7829cf1f86daca469c479acf49e6fae72e3d0 -size 13227 +oid sha256:fbd57af1fa982f9090f57d820a9b927f894914e5f54774e9cd6fdcfe14e5f761 +size 13139 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png index 57c3b02ba7..2d4ad649fe 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ce5fefe04cc2a036fddcfcf038901a7a09b4ea5d0621a1e0d3abc8430953ae3 -size 20778 +oid sha256:c4bbc28c203550baf885cefba95c48a3f91dfb5242c09acbf3a8509b7258048e +size 20768 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png index b3bfc7ee51..08182dcfbd 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b653c0fe761d351cb15b09f35da578a954d103dea7507e2c1d7c4ebf3bdac49a -size 10943 +oid sha256:566e85b1a527f48c953bcc7bc6c58ebd1fe0b14972c38edd596b025e0dd48624 +size 10940 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index a295c016d5..392d3462f6 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8 -size 13536 +oid sha256:aa5b0d5de93f26c0a7a03b57a00d4a49cda62f4a4b98b6d374261467c03a8357 +size 13500 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png index 63214687d5..56f2a31278 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8970378312c0d479d618e4d5b8da54175c127db517fbe54f9057188d02cc735 -size 4165 +oid sha256:62267d8d56af3e1452f0e25144f2cfe352b88def98af28e819a3a6982040a4ca +size 4102 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index a295c016d5..5919cce39e 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8 -size 13536 +oid sha256:878d5c53b84af4d133825206a327fd4cd02a43831ecabf5c61c5d89181c5a107 +size 13499 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png index ef37b3e2d6..fa01f13cc1 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bbf7ef00f98b410f309b3bf70ce87d3c6455666a26e89cd004744145a10408a -size 12559 +oid sha256:f0aa3c19852632e603ec425aeecc5243d4c6c24a1ac6e3906d29913bf7ead2df +size 12535 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index 93a0ce6c54..98ad27d78f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f9ab86abad276d58bb029bd8e2c2aaffac5618322788cb3619577c7643e10d2 -size 14223 +oid sha256:ec648c2e8006d478ace4a78d2434a4ef7f10d4a3502468cd8b9e2b1f154620b6 +size 14278 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index c2ca6bf57b..feb217ee9f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05c4dc9af1fef422fd5ada2fa1459c26253e0fb5e5a13226fa2e7445ece32272 -size 17927 +oid sha256:6cb06152d5a0765ad86e8005d6ddac469914ccced89d5ee37d77e7d030b97c9e +size 17281 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index ade9a1ccd8..fd1cf7e774 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82b47e1cad2eea417b99a2e4b68a5ba1a6cd6703f360e8402f3dca8b92373ecc -size 18945 +oid sha256:38ea8596a682be0075bb31ed176b1fe04b518eb887235d551a574e338d45880b +size 18869 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index cf04e20363..6194834211 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b15ce5a201ee6b946de485a58d3d8e779b6841457e096b2bd7a92968a122f9af -size 20844 +oid sha256:965f42f021c63a0f2ccc691723c4ad7f92119294aec407c7ffd46a6238c8f268 +size 20792 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index 6be0fc0ff8..7201a5f159 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1622a48b3f4790d66b229ed29acd18504cedf68d0a548832665c28d47ea663b -size 13857 +oid sha256:86f1b9e8f1e38070ce862d87c927313904ceaa9e6080f5acead90e82d164738c +size 13879 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png index 0064e973ff..77c61443b3 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74df7b82e2148cfc8dae7e05c96009c0d70c09bf39cdc5ef9d727063d2a8cb3f -size 4154 +oid sha256:270f9c2bf5d15fcb21796b3b9eb393e0cc00d9c337036637295ad1efb56781b1 +size 4114 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index 5dd0c52255..22dc949c3c 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc740ccd76910e384ad84a780591652ac7ee0ea30abf7fd7f5b146f8ff380f07 -size 13991 +oid sha256:d413162a83c223124a2f29f8154e4bdc08d94bd3e15569ec6cffaa13bdda72c8 +size 13953 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index a6e120e904..3d86b73ab8 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccdc54e814604d4d339f6083091abf852aae65052ceb731af998208faddb5b0b -size 13744 +oid sha256:941ea7b4d1f2c187f58920546e2f19fc275505929439cc389edcc59e652e8787 +size 13777 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index d32c11d44c..060fdc8092 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd24e0a52c7743ab7d3ed255e3757c2d5495b3f56198556a157df589b1fb67ca -size 14889 +oid sha256:cc5e6a607ef2343cb74c5227dbc7861840db956951f1fc4703fe53dbccda0974 +size 14808 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 72782b0b99..5240721602 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:878f1aab39b0b2405498c24146b8f81248b37b974e5ea7882e96174a034b645f -size 12374 +oid sha256:2ac06a9ba2b2c8bef7e0117ac52fbb790101c0f89313dc49feb1f5a1d929ab02 +size 12381 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index 6cedab729b..669aeba9ab 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcc2bf4f7e0ab3d56ee71ac1e1855dababeb2e4ec167fd5dc264efdc9e727328 -size 17027 +oid sha256:ad0f483fa7fda620860858c4f330ba914480fba15d70b408fb1aa3fed52dbfc1 +size 16839 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png index 7368a3b007..438450e535 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c733878f4c0cc6075a01fbe7cb471f8b3e91c2c5eaf89309ea3c073d9cc4921 -size 854 +oid sha256:ba501a7fc32a68f8989965aa6457b3860ec42947e2bcd4526c7570ff743f38fc +size 841 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png index da66b26768..f7f2101d78 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af872886136893938aee82b1ac73e7a1820666a9a5f4bbf34159c09b3283169a -size 5520 +oid sha256:8265c5b2e8edd5eaf0aeeccf86cac486e7beec581e696d3b4f4cfee8f4be9b2b +size 5554 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_issue735.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_issue735.png new file mode 100644 index 0000000000..48281cebc0 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_issue735.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15de93db72e2de0ad1a21b67931df57c1a129f062fe1ed3cb7f083761af4fe36 +size 61931 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565.png new file mode 100644 index 0000000000..2e44b7ec58 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a568f9f12adc5606e5b74d25a4bf083e2b565d73635704e205400d5a073fcd3 +size 9895 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565pal.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565pal.png new file mode 100644 index 0000000000..2e44b7ec58 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16-565pal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a568f9f12adc5606e5b74d25a4bf083e2b565d73635704e205400d5a073fcd3 +size 9895 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16bfdef.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16bfdef.png new file mode 100644 index 0000000000..e91c987b0d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb16bfdef.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c05e0b14a389d11f531d4238900106a81f25a741a55bbbcd160b8b67eb32adb6 +size 7019 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bf.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bf.png new file mode 100644 index 0000000000..71e2079d03 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bf.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d577ac117b8260ad08e6f051c949d9187f04699eed0f063476aaf42eada2366 +size 20640 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bfdef.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bfdef.png new file mode 100644 index 0000000000..71e2079d03 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeBitfields_Rgba32_rgb32bfdef.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d577ac117b8260ad08e6f051c949d9187f04699eed0f063476aaf42eada2366 +size 20640 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_16Bit_Inverted_Rgba32_test16-inverted.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_16Bit_Inverted_Rgba32_test16-inverted.png new file mode 100644 index 0000000000..7cf41fb5bc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_16Bit_Inverted_Rgba32_test16-inverted.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53a33da2c1a84c0e8426e1a2ba24e2fe2de17a7bc75760184fb10bf6dc96954a +size 7896 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rlecut.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rlecut.png new file mode 100644 index 0000000000..db7f30b415 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rlecut.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0c97446dec6af009423dcae431c44d23963179c3c3bfbeaf3f3b52022a33f68 +size 2414 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rletrns.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rletrns.png new file mode 100644 index 0000000000..c2959addd8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_pal4rletrns.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a651b81764696cd4fe42113d5ac26006f54820d2df8b2103793e82c66597b2b0 +size 2837 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_rle4-delta-320x240.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_rle4-delta-320x240.png new file mode 100644 index 0000000000..0e52f53326 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta_Rgba32_rle4-delta-320x240.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55672d405a3d085c72cfc9fb26bc3955bf858e525c54c6c4505f093f011f378d +size 3188 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_Inverted_Rgba32_RunLengthEncoded-inverted.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_Inverted_Rgba32_RunLengthEncoded-inverted.png new file mode 100644 index 0000000000..1c88282561 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_Inverted_Rgba32_RunLengthEncoded-inverted.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e774cba4dda2fe9d3cdff141e7a8c1de7f3e9c8014093abf8697a34e6cc7144 +size 5379 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDeltaAndEOL_MagickRefDecoder_Rgba32_pal8rlecut.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDeltaAndEOL_MagickRefDecoder_Rgba32_pal8rlecut.png new file mode 100644 index 0000000000..86efdd207e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDeltaAndEOL_MagickRefDecoder_Rgba32_pal8rlecut.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44d281c31cd264dcb812df8cb5dc9d5042e915d64e8013af5577dbfea6cbb1cf +size 3955 diff --git a/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithHexadecatreeQuantizer_rgb32.bmp b/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithHexadecatreeQuantizer_rgb32.bmp new file mode 100644 index 0000000000..7f79c6f3a5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithHexadecatreeQuantizer_rgb32.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fff91e78a87c2b2db661609dd70af2d7a74e6a0de18924ab2b6272e12c60977d +size 9270 diff --git a/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithOctreeQuantizer_rgb32.bmp b/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithOctreeQuantizer_rgb32.bmp deleted file mode 100644 index 2b8e05b070..0000000000 --- a/tests/Images/External/ReferenceOutput/BmpEncoderTests/Encode_8BitColor_WithOctreeQuantizer_rgb32.bmp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:11375b15df083d98335f4a4baf0717e7fdd6b21ab2132a6815cadc787ac17e7d -size 9270 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C1_G3.png index 564a76f90c..3b4c12a24a 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3a6e47d880b7276702e6bf4b1ba1b4781abfa2cd99ee9321a56169440fc318c -size 49844 +oid sha256:0c047a274060b1bf73af9c922bff4f2ea627bd0fad023b3c3a5852f49d026c6b +size 50025 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C2_G3.png index 4f2dc77b06..7dad93fba1 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18b4837dceb9a065c5b27133b67ef257a0f050fa1342d02a7e06d3501e068f47 -size 44966 +oid sha256:aa1b0b58ddb6a5c29ccd78f1bc2e773a2005493b92efe5c0deb8056af6fc0208 +size 45605 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R8_C1_G1.png index 57ce8295ee..a9b287c6b1 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_BikeGrayscale_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e652706e1aec475c8eec08392d65be1bd888cc79eccc4019eb3826db71c6dac0 -size 54744 +oid sha256:ead63aaaf5b185342f83317c2400b7ada29a82223e35e9a7382f690101ae40f7 +size 54798 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C1_G3.png index a0e9c1b248..a4ac9718aa 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffc82427285a2bfeb3226e3e87ac316e41a2a8f853bb406b88ba2ae7bfae099d -size 164923 +oid sha256:025751d22eeeac10878e79d2bf1987e0a055cf9f54903048673189a385036744 +size 157237 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C2_G3.png index caf152f9b9..2c18a147dc 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea08065be7271e7ff75ed49e63544701bc14a03bd44d754fb056370559dd105 -size 149483 +oid sha256:77cae565842c0c8b0314d1c10c72f4b52483d002c614a33734b534f743d06b5d +size 146574 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R8_C1_G1.png index 5ad60a4bf9..0e31e223b2 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Bike_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a61bca9de7e0b6bf6f88c4d01b9e3f1611f6866db10e4a1168550ab84804f42 -size 178531 +oid sha256:223027dcd3a8e5ded5f98b3aa47b0e18bc40b569e47f6bfdab1126f4420e5977 +size 171283 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C1_G3.png index d1b15eb8e3..be4b535c78 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e2f6c6c42ddd15c0730edcd402bf8fd05a1116cb72495e3156a545988ff0230 -size 85901 +oid sha256:db64cd4e0369fa2db33aa1f21a94466ca39a191b0e0adbbbfbcbca44284c61ab +size 86151 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C2_G3.png index 3acb240b32..5ddb7ce12b 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8193ce8690dbb99517cd56e5a866268c0a5c971c992058afb818393658781b91 -size 77762 +oid sha256:afc3a020178a68715ab0dafa30ee1db81630b760fde3df3271048acc627f178c +size 77614 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R8_C1_G1.png index 335375205e..db03e0fb43 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_CalliphoraPartial_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:412c302f92500d855658d466aa1a686386ed998f6a3d5fe7326cd148a6f829ae -size 116229 +oid sha256:5ea7e0c3e16acf40491354191278a09af2db5ac6e0e7b92ba377f8fc9d52e305 +size 117067 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C1_G3.png index ffa9624d04..4d9777bd87 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d753a7dc732d6f01f7bce8c6de50d70158639aa5e0eb18d168e417fe36492731 -size 135 +oid sha256:ba9b046556b04556632f4b651dc9d8ebc4d27699e53567179ef15a850567fee9 +size 85 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C2_G3.png index ffa9624d04..4d9777bd87 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d753a7dc732d6f01f7bce8c6de50d70158639aa5e0eb18d168e417fe36492731 -size 135 +oid sha256:ba9b046556b04556632f4b651dc9d8ebc4d27699e53567179ef15a850567fee9 +size 85 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R8_C1_G1.png index ffa9624d04..4d9777bd87 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_Solid50x50_(255,0,0,255)_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d753a7dc732d6f01f7bce8c6de50d70158639aa5e0eb18d168e417fe36492731 -size 135 +oid sha256:ba9b046556b04556632f4b651dc9d8ebc4d27699e53567179ef15a850567fee9 +size 85 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C1_G3.png index e155d618df..9edb930aa8 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:570cb16b4026d38cbf586592c34d0fe303f2c1d99baeb531670c4ba25956f9c3 -size 15489 +oid sha256:20bb78a7539049ced92c936c2a78fdeb3809ffe0cb1dfae034e55e326de094a8 +size 14285 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C2_G3.png index 3f93014b6d..ce093b38aa 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27813f975640c7ac15e78af910efc796ada950cd7efcd9a4fe045d531de24ec8 -size 15197 +oid sha256:a55e413991f9817ce9cecaf9d3ed31f5cd7559f9d527ff2e03c8bea5be091887 +size 13943 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R8_C1_G1.png index 198baadf7d..a8d549dc66 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern200x100_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e10a91175585b3843b25b710dc45f734ba706b60fc56a21748da355f27b8dcf5 -size 12776 +oid sha256:2afdab1f7f430c3f38fa7a6cdac60fbeb03ebcdda00a1bc79b289680a6811388 +size 12056 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C1_G3.png index 9140cdb0b4..5104bad4b6 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c0aca87ea94ec94c4d9e458dd33d366e0166533ee3e3c39066ee9d8be63a74e -size 1393 +oid sha256:028cd8201f9e7cd4988d63cc80d9afc96d0c739c65d807f7c755947e49e65111 +size 1326 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C2_G3.png index 32778c0a7f..f96d779283 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d486512f51a57c90de6c3126791fbd3b7e7dd756932b5bd716660ee9b72f010 -size 1168 +oid sha256:213e3bd008292b807e2a2ec216ec017a31cd165f4d2a8bc86b3deea3daffc81c +size 1109 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R8_C1_G1.png index a60c7f0cbf..6656f75640 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern23x31_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f344abd9f1cfe6e8c2f0be52fd9498ee3ee6370cea94244d90fdaba7fa5e71 -size 1488 +oid sha256:2ceeb731111403f6354c460e403a30a2269ae1b2e2a9ef9b3819bdcadb50b859 +size 1424 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C1_G3.png index ae9b64afad..da9a0c2e3c 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90a45e7212ce97754be0e1432fffdc62ef5d2bdc30fdff0477a1951754cc0929 -size 1113 +oid sha256:fa9d77a2f1b727a42323a8ac19cb4cabdef0e7f0257bc92bb0100cd280f87613 +size 1076 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C2_G3.png index 1e9dc61e6e..e0ccdbb5d7 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c61766f05dc48413c21baea8af1994895aa354732109fd5701a42f746f1f412 -size 1005 +oid sha256:fc9986cd6042d8cad709a5768dc464fc171a3cb5d3457406c11c46ef55388daf +size 956 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R8_C1_G1.png index 99a249b1df..4f1d40e2ce 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_TestPattern30x20_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be002593c2874eab803d0dc9b5646fb51b94ea92e17158797609219f46c2f723 -size 1518 +oid sha256:7e92efee845abd30705bb9a9cf61997092fca89b48e20d8383a0c08ab159a0af +size 1491 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C1_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C1_G3.png index d4ff4e16bc..404dc83b18 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C1_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C1_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11edba36c7f73271d9575b16cc7663a1e75c3b34560df51b7430cfde3ce1ea08 -size 6576 +oid sha256:21e8dce4256aecb250e0079c8bd60deb8b2ca55848fb0fd647ed3115afcf82af +size 8401 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C2_G3.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C2_G3.png index a02c2d6702..525eedcd82 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C2_G3.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R16_C2_G3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75320c25e8977e5d56ff32f054e15773bd797e4ba745cd7bf3a2fd4c7000e3df -size 6400 +oid sha256:c4b1b7f904b23b6d7bd7b0fc4c533d3c213f38bcab689e9dac1936b05b71e9cf +size 8268 diff --git a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R8_C1_G1.png b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R8_C1_G1.png index 5fbabed0ac..2585ff479d 100644 --- a/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R8_C1_G1.png +++ b/tests/Images/External/ReferenceOutput/BokehBlurTest/BokehBlurFilterProcessor_cross_R8_C1_G1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bb334d24245359fda8b64a74cbd30267cbdd1238e0aeae674b57be5371cd7db -size 6266 +oid sha256:33ca697815ebf4c74343e0abc7a418af8ffdd8fa0830285770249a1f20929eae +size 8444 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..ffa8e7cd8a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b311f117167e17b4611d525aef57146a6757db08f2a36fa093f45f237df9e1a2 +size 326809 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..682fd8b49b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0eabd5f7dd2f258d04d3f1db8ee4d958754541e0009ae183d6e512f408e3201 +size 249497 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..55d592a60f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/InBox_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:048b4a1679852d34d6f74f01b750461ec9db2d3e7e3aa3d2d89d48e5725aa560 +size 142367 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..f01496ad76 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_CalliphoraPartial_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6f63d117433a93e30b9b41f68f676bb53eb1761f3c665f178ef07ec2c9626c3 +size 338527 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..99e9737b51 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_Car_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d72a3994fc6dcc461da3af5176d5bf62c95b7abeffcbf5547172377db3b0d9ac +size 287158 diff --git a/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png new file mode 100644 index 0000000000..ecc09e70ff --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Convolution/ConvolutionTests/OnFullImage_Rgba32_blur_-1_-1_-1_-1_16_-1_-1_-1_-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3fd7906f11e87a2b0043cde179317f53e9a2bf02d9e64763be8f9923d60f057 +size 257492 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial - Copy.png b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial - Copy.png new file mode 100644 index 0000000000..43e414da6d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial - Copy.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a899a84c6af24bfad89f9fde75957c7a979d65bcf096ab667cb976efd71cb560 +size 271171 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png index cc2327b23f..70acb3f32e 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Atkinson.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e22401dddf6552cd91517c1cdd142d3b9a66a7ad5c80d2e52ae07a7f583708e -size 57657 +oid sha256:e44c49a8f2ab1280c38e6ba71da29a93803b2aa4cf117e1e919909521b0373e6 +size 57636 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png index e3ae6508e1..af35177491 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Burks.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:819a0ce38e27e2adfa454d8c5ad5b24e818bf8954c9f2406f608dcecf506c2c4 -size 59838 +oid sha256:359a44bb957481c85d5acd65559b43ffc0acf806d4f4e57d6a791ca65b28295b +size 59839 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png index 2b897a5d6d..a14c2cb1f6 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_FloydSteinberg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:007ac609ec61b39c7bdd04bc87a698f5cdc76eadd834c1457f41eb9c135c3f7b +oid sha256:7fb3743098a8147fd24294d933d93a61ec0155d754f52544650f6589719905be size 60688 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png index 10ba90ae86..683f59ea1e 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_JarvisJudiceNinke.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46892c07e9a93f1df71f0e38b331a437fb9b7c52d8f40cf62780cb6bd35d3b13 -size 58963 +oid sha256:41fa7d92a10db450f3b3729ab9e36074224baaefeda21cffd0466e37a111e138 +size 59113 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png index 9608289e84..813289a26d 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b83345ca3de8d1fc0fbb5d8e68329b94ad79fc29b9f10a1392a97ffe9a0733e -size 58985 +oid sha256:bebf3b3762b339874891e3d434511e5f2557be90d66d6d7fe827b50334ede6c2 +size 58976 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png index 79d2c5eb14..d4da100376 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Sierra3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c775a5b19ba09e1b335389e0dc12cb0c3feaff6072e904da750a676fcd6b07dc -size 59202 +oid sha256:fd4358826739db2c22064e8aa90597f8b6403b9d7e2866ec280e743c51d2f41f +size 59203 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png index edec46a92a..fa8eea57a9 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DiffusionFilter_WorksWithAllErrorDiffusers_CalliphoraPartial_Stucki.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cc216ed952216d203836dc559234216614f1ed059651677cc0ea714010bd932 -size 58855 +oid sha256:174ee39c08eb9a174b48b19dc618d043bf6b71eee68ab7127407eb713e164e61 +size 58934 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png index e2e4147f68..2c67b3bf23 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7 -size 871 +oid sha256:1110b46ec3296a1631420e0bb915f6fdc3d1cead4b0fc5a63a7a280fbf841ea2 +size 870 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png index aa0e9a4824..1305c5ede9 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb3e3b9b3001e76505fb0e2db7ad200cad2a016c06f1993c60c3cab42c134863 -size 867 +oid sha256:e51abcab66201997deda99637de604330ef977fd2d1dbebaa0416c621d03b8f9 +size 869 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png index e2e4147f68..2c67b3bf23 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7 -size 871 +oid sha256:1110b46ec3296a1631420e0bb915f6fdc3d1cead4b0fc5a63a7a280fbf841ea2 +size 870 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png index e2e4147f68..2c67b3bf23 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3253003b088c9975725cf321c2fc827547a5feb199f2d1aa515c69bde59deb7 -size 871 +oid sha256:1110b46ec3296a1631420e0bb915f6fdc3d1cead4b0fc5a63a7a280fbf841ea2 +size 870 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png index 4175cf40b7..da1f62b728 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9316cbbcb137ae6ff31646f6a5ba1d0aec100db4512509f7684187e74d16a111 -size 51074 +oid sha256:eb86f2037a0aff48a84c0161f22eb2e2495daadbfa9c33185ddfd7b8429a4ea9 +size 51266 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png index ac56fa9236..03848e81ce 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d2289ed4fa0c679f0f120d260fec8ab40b1599043cc0a1fbebc6b67e238ff87 -size 51428 +oid sha256:ef033a419e2e1b06b57a66175bad9068f71ae4c862a66c5734f65cdaae8a27f0 +size 51461 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png new file mode 100644 index 0000000000..9e3e48a528 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:003d5986b66f90e0841c3fdfa8c595563c8e237467b8218c6fd7fa283ba28b1d +size 21154 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png new file mode 100644 index 0000000000..7d8babf99f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1d148bd561f33c2435226c533dea539e1b21567d8f985a4059d501846e0bf30 +size 21761 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png new file mode 100644 index 0000000000..108ccd1f52 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38519ad5d1d50548fada577d2bd4a8be7f76d1c3071f07bb98f1227c4bc5d303 +size 20522 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png new file mode 100644 index 0000000000..28c79f8c2f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e922f83fd719ba961b8720417befa119000f2c8f3956d3ac8df60c79ff56fe59 +size 21291 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png new file mode 100644 index 0000000000..401939430d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b7b904032ff6c142632e8c1efffe3d71c28a89d5824d9fe13dbf4ceeddcf5e8 +size 21367 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png index 7e693a5839..3692f1a1e5 100644 --- a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ba180567e820b145a13c9b26db9c777e95126adfe8e8cacec0ffe1060dcfe8d -size 184124 +oid sha256:7f8a4db4facce1d68b363a3b59ea40c9da9fa3c989c736d97a703c84d8230660 +size 184595 diff --git a/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png new file mode 100644 index 0000000000..8f31f0d8d0 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f66bf45b5d80e412314341567b8cf240d56b5872f01ce9f3b0d6034d85e8e942 +size 163327 diff --git a/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Uint_Rgba32_Calliphora_uint32_uncompressed.png b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Uint_Rgba32_Calliphora_uint32_uncompressed.png new file mode 100644 index 0000000000..8f31f0d8d0 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgb_ExrPixelType_Uint_Rgba32_Calliphora_uint32_uncompressed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f66bf45b5d80e412314341567b8cf240d56b5872f01ce9f3b0d6034d85e8e942 +size 163327 diff --git a/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgba_ExrPixelType_Uint_Rgba32_rgba_uint_uncompressed.png b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgba_ExrPixelType_Uint_Rgba32_rgba_uint_uncompressed.png new file mode 100644 index 0000000000..96482b46e8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Uncompressed_Rgba_ExrPixelType_Uint_Rgba32_rgba_uint_uncompressed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46ef9cd2f212651a5ddc78aa1707df87c18ce227466b7b6f00d215f11271e61a +size 24590 diff --git a/tests/Images/External/ReferenceOutput/Filters/BlackWhiteTest/ApplyBlackWhiteFilter_Rgba32_TestPattern48x48.png b/tests/Images/External/ReferenceOutput/Filters/BlackWhiteTest/ApplyBlackWhiteFilter_Rgba32_TestPattern48x48.png index 4f57775dc0..aa561ace81 100644 --- a/tests/Images/External/ReferenceOutput/Filters/BlackWhiteTest/ApplyBlackWhiteFilter_Rgba32_TestPattern48x48.png +++ b/tests/Images/External/ReferenceOutput/Filters/BlackWhiteTest/ApplyBlackWhiteFilter_Rgba32_TestPattern48x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65d3a099dd24fcee2fc86c63b8664ebd81d7b3c530168da5f4e50ee0ca925496 -size 759 +oid sha256:d76ae5ac1a0aebb9d76b2fa5c6b1fc109e5fcbdc6c41d9681a76ade9c86fab79 +size 748 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/00.png new file mode 100644 index 0000000000..65b2c6ff72 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b980fb1927a70f88bd26b039c54e4ea20a6a1ad68aacd6f1a68a46eb1997a29 +size 1180 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/01.png new file mode 100644 index 0000000000..8c9e125ade --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4006374b88ff4c4ed665333608a19e693fc083ae72beb71850d0e39ad45c9943 +size 1144 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/02.png new file mode 100644 index 0000000000..a2fc2ab3bc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f5054e1e464c9e9fc999eec00b9949a6dc256ee062e9910b5718b6d4658661a +size 1303 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/03.png new file mode 100644 index 0000000000..9d5b54c718 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e8bcbcc5dc63fbd555f90a52b4e111cfc058f3adba2ca9c52dec966dbbae8f +size 1371 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/00.png new file mode 100644 index 0000000000..65b2c6ff72 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b980fb1927a70f88bd26b039c54e4ea20a6a1ad68aacd6f1a68a46eb1997a29 +size 1180 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/01.png new file mode 100644 index 0000000000..8c9e125ade --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4006374b88ff4c4ed665333608a19e693fc083ae72beb71850d0e39ad45c9943 +size 1144 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/02.png new file mode 100644 index 0000000000..a2fc2ab3bc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f5054e1e464c9e9fc999eec00b9949a6dc256ee062e9910b5718b6d4658661a +size 1303 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/03.png new file mode 100644 index 0000000000..9d5b54c718 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_Rgba32_animated_loop_interlaced.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e8bcbcc5dc63fbd555f90a52b4e111cfc058f3adba2ca9c52dec966dbbae8f +size 1371 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/00.png new file mode 100644 index 0000000000..f9d43792cc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07b63781e5481a46955fc26e9023b243aeada231c4957332c80241e1ad119733 +size 273 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/01.png new file mode 100644 index 0000000000..da7411347b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bd0b25eafd2fb3f55f593a5243fa1e3b6a7ec43a70b8d0c3a6eddd56fe65ae6 +size 114 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/02.png new file mode 100644 index 0000000000..5d2c892282 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2f7bb0aed90e52d7905014d790f0bcb5df3f05e5cb82b51dda88ac13dc5afcf +size 115 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/03.png new file mode 100644 index 0000000000..a9db965de1 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_firstframerestoreprev_loop.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b10d33fd285b8a200090bccc35541a608ed062edf1a055357c895935265d216b +size 116 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/00.png new file mode 100644 index 0000000000..f9d43792cc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07b63781e5481a46955fc26e9023b243aeada231c4957332c80241e1ad119733 +size 273 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/01.png new file mode 100644 index 0000000000..0d61407603 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9296af767fc47ee67249de4f473633f308a323e9e82676dc00952e05cc23f761 +size 341 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/02.png new file mode 100644 index 0000000000..e4bfadab57 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2926b6f27314950ff21f1357636f933694f8424e391a10d980a1492ef7b3f07 +size 421 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/03.png new file mode 100644 index 0000000000..a2df6ead1e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_norestore_loop.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:350624a4f22cbc47b07e5c8ffe0ea2e0f03687d53b77896c7701b04d7c93089d +size 431 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/00.png new file mode 100644 index 0000000000..f9d43792cc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07b63781e5481a46955fc26e9023b243aeada231c4957332c80241e1ad119733 +size 273 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/01.png new file mode 100644 index 0000000000..0d61407603 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9296af767fc47ee67249de4f473633f308a323e9e82676dc00952e05cc23f761 +size 341 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/02.png new file mode 100644 index 0000000000..2153fa5cf8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:726aff614b67ea2ee9ffd53fff8e304130019de99d3ae641bef97e0a24f756be +size 343 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/03.png new file mode 100644 index 0000000000..369f668fcd --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_frame_restoreprev_loop.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a645f615d592a1c24b94ad3a4fc63503cb8b0504c9464ee5089d9831b23c28e1 +size 336 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/00.png new file mode 100644 index 0000000000..f9d43792cc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07b63781e5481a46955fc26e9023b243aeada231c4957332c80241e1ad119733 +size 273 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/01.png new file mode 100644 index 0000000000..4fa3431032 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e90013c0ea6e60ef68143914fdf4b14f83e869cf90aff42d837b1340d867f77 +size 276 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/02.png new file mode 100644 index 0000000000..cdd623a6ad --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69c0e3fb7365e09f6acd26e334f3cb65ba8657b2de7b1022256f5aac91f257ac +size 296 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/03.png new file mode 100644 index 0000000000..36f963ccc6 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Animated_WithTransparency_Rgba32_animated_transparent_loop.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2be415d41972782f91ad513428796588b0040c4125d604f72d1288c5b3e3742f +size 282 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_nontransparent.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_nontransparent.png new file mode 100644 index 0000000000..65b2c6ff72 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_nontransparent.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b980fb1927a70f88bd26b039c54e4ea20a6a1ad68aacd6f1a68a46eb1997a29 +size 1180 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_transparent.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_transparent.png new file mode 100644 index 0000000000..193cde24d9 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_Static_No_Animation_Rgba32_static_transparent.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18b60d2066cb53d41988da37b8c521ddcb5355b995320a8413b95522a0492140 +size 687 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/00.png index c8b4db92cb..06a7b52d62 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/00.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2cc0d653e6f3e06b1d8828ff5794fd5f81526a9e411137a2d1a78f9d8894100 -size 7168 +oid sha256:f212a6b0f4e2ce7d38a489dfe0e050adf807a33f8305367ce377f45a6d7c4619 +size 15016 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/01.png index afeab25c31..ae776f82f3 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/01.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/01.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7f721df04021f246e9df9f6b91c3654e8b40ded575473c05d646f7bc632b958 -size 7558 +oid sha256:8612f3caef51ce4f0daf2d37f3bf959382a63770391f2ec242c2af46eab2b3b5 +size 15748 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/02.png index a03761d56c..82ba6c23db 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/02.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/02.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a40b7f8d2779e6fdf26e2720fdf24f8da03e9ef9d8b1ff68e9bb68f001814b79 -size 6956 +oid sha256:717938369a68299af27a2b42d58c97ddd5fcd2d8a93418af8f92b3d1c2e93064 +size 14485 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/03.png index 848c6be819..f83ea81d9a 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/03.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/03.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc7fdca7cfec1ae6d119dbccdc7ea78c19584076a197c54e494645b2c84e45e -size 7131 +oid sha256:6a0545794d1384f46acb32bbc4369a226d002eb8ae286e064855d55afd72f4c6 +size 15040 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/04.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/04.png index 11b93c1aa3..20ff589c97 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/04.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_giphy.gif/04.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4b59fa394cc6205e00440428e9a141a627b88b5e2562ec6b1dd0d48651da77f -size 7104 +oid sha256:853a4f2749ff5ca67bd7c49c2e2f0323772a828a215fac0fd02e9b7eb9d63051 +size 15291 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/00.png index 9236bef85a..7acd64ad92 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/00.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ac324bd9199113897bd986cffe8e7f3770fb2e68e6792d631071ff1db40075f -size 29351 +oid sha256:c41341baee0a461d811cbb8c6cd64d3276d7d4520e95732c5dd12f3834e0a6f3 +size 47309 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/01.png index 91b3c95dcf..5822b7e968 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/01.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/01.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9ec5a3228c71b54a2bccd8aaf929e11436d45cb56f43cd507cfd4e7bb288fb7 -size 30538 +oid sha256:7a0d6d652a7fc1bdf84bd5e1a785d97e1da08ba3397c3644973f221f3a75b59e +size 48583 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/02.png index d9c98dace4..e4536355c7 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/02.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/02.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa0c9f304a6afc62aba3b6567905160f7728f43e3b5ba3dc79bf743c9a5f49ab -size 30754 +oid sha256:2f8ed2197b5a9b9749c572096714251bf23be463b3bbd01329d47296078c5f23 +size 48824 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/03.png index 5573c15191..ca8f635481 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/03.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/03.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e61d8611c06c4f82c0000ebac4a1b3a53d1342e23f9efb10406ae06a510580c -size 31260 +oid sha256:6c75f616b6460d832ba7b6a0e0551f07d55c2cb0533e95ce16b9a1b9073f75f9 +size 49783 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/04.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/04.png index a595bb707d..87d44ffc64 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/04.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/04.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:168bcccba4dffeaec2f2e3978405f802451089790b6377d8c653cfaed7bae833 -size 31741 +oid sha256:0bb07ef91e0f8a82d457ad2ebfed9ca6940d228a4e9954e75c3c1826d4ac5f53 +size 50381 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/05.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/05.png index 29537a2f55..a7b15ecf9d 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/05.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/05.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c6675dcca4418fdc529e4de1bc0014a2463d6c6b197dd701b84044a00b31016 -size 30061 +oid sha256:7d80517e1d79f85a5a725d8678011ec5c05748ccc2eb7567573380b610183ea5 +size 47731 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/06.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/06.png index 380b5aad3b..86299abfa4 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/06.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/06.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d01cfb14c66662f8972f48d62a7cbb70c2ffa174d74a5533be61a2565d923a0 -size 31139 +oid sha256:07a406840c321421e6aa07ad69d14d96e2bb728364c8fabb98151c5693efa2fa +size 49847 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/07.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/07.png index 9c815c41b3..bfd4828ada 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/07.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/07.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6918449a54dea19a15f3f4ef9f5d0e890f7dc97e651d4c77bd60dfaa4f49d646 -size 31304 +oid sha256:72678c10f8cd852f87c19ec1c2a7acfb326291e1ae0f48588d5d7d999592bb32 +size 49850 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/08.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/08.png index 09521859c7..c7c53dadb4 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/08.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/08.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8927c2e34c9ab90c1fbc3d4f8be289f4a47fee5cfe9c4d72bc39eb78c94a3bf -size 32500 +oid sha256:7c9a61f0639bda2fd8690544ad41df7179ea29987b8827b4cae2e959998d27cf +size 51612 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/09.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/09.png index e38de41abe..accff0f9d4 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/09.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/09.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2ffb21368cf6c3e4d567ca19cc40187bee254ea6ebea83c6d6f9c2df02b56eb -size 32374 +oid sha256:ef326f40bdc7f0a43ad1edb51b000388f4f5ca1b7c24633447c03d999bd088c6 +size 51724 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/10.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/10.png index 4d104e2738..71c5577c3d 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/10.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b903f04b431ed0d9474ef89cb48d2b7b7fa8c124b5fb1f2562d826eaa2cd3ba -size 32692 +oid sha256:a4e8f6a96ce47d36c778d7fa243d723bca725438fe9d9174b61afb638c1cd815 +size 52070 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/11.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/11.png index 141d2df423..58daa50673 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/11.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/11.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:898566396d44cb9c495ed6c40c56928fe642434ed4888331bb92b17fb0cd4847 -size 33243 +oid sha256:b1f96f15c1a46db19726b6ce0d5a78800b1a430c4323a60e628e7e08aa795189 +size 52829 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/12.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/12.png index 590132fcaa..164a07efb5 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/12.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/12.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18a1888eb6d79f857f8336e76e50b5402127036418183e03869e58021b8f9c47 -size 32797 +oid sha256:d98e70649b2a1971f62eb41d53e064f898b45da47f05e000e4dd80464f5ac290 +size 52329 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/13.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/13.png index 9ab3afd9ed..1c1de607e8 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/13.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8384afabdc9133435c61c0cec97fe599719f7cc601fc1444b1b182383dfcbd40 -size 31306 +oid sha256:54ae733a92eb63174fa02708aee26d28cf0d1069ae4514ff244ebea333d87af8 +size 49767 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/14.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/14.png index 33bdd55d4c..061dda6679 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/14.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/14.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ea0c2d692d1a5b645af16726b54cc54a0d0166292cc3b3619a96f062996f13f -size 30782 +oid sha256:6a3bc3c870891b220efd95d6da83a166595564e0610766f105496bc77b090e9c +size 48893 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/15.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/15.png index 1ca1ab28ee..2958cfc366 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/15.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/15.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4bb35f4484cb66bf63d46f4311617dc7e675ead33b8f978f564045491eea5bc -size 31875 +oid sha256:e9afd78075471066aa12aeaf33ddda780571faa14a61b842963c53b688432569 +size 50818 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/16.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/16.png index 9dced1b137..27964b46ff 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/16.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/16.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21aa4d98c2870022104e44de7c63e16d4a8f7ae139c7a996c862b1cbd55bc3d7 -size 32317 +oid sha256:0caefffa407c227ca906cc21e959d3480a0a35a5a1720a00076a8d2c7ca1afb4 +size 51462 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/17.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/17.png index 110ce09672..8664f2ed28 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/17.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/17.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7550515971c3af24efb51b4a81c21e3e3a642c53b654512bc6c6ad67d049748 -size 33265 +oid sha256:27e0c6a2204395b0da83c8fed89cf72168641b9318815db2e1d7a169317739d2 +size 52691 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/18.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/18.png index 68406f480b..0c61aa82a7 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/18.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/18.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:161d80a458dc2418eea4ab8caa851c0632eadd64538843a2c2a5cdd0a54ff66b -size 32545 +oid sha256:ff6af0ab7aabc30f57b1801045f27fcc327103db8f910dcd031cb68bd3e13df9 +size 51515 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/19.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/19.png index b0c387fb85..66a5f9c157 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/19.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/19.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8342d403ab99e900932b52b55f6c6583aac0567c4215c4e187eccf264f4e2c2c -size 32210 +oid sha256:eff399d98ab583c5c8dd26e78bc8920b43e5ee1c142fb9553080850e0506817d +size 51044 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/20.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/20.png index 7178b780d4..6c18956a63 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/20.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/20.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a29b4ca76d42a067b3e4f27521e5a0b05c85668002e92a30d12f1a3784062ec -size 31574 +oid sha256:0bbd6daf4825780010d0da77e05ff505e594acef2588a804a4c98501501c8728 +size 50347 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/21.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/21.png index ffb8ced6e5..f4f27b24c1 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/21.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/21.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be2601503fadee016c5f88b137091b50c6a7451e577f253f166b212ee937b35a -size 31384 +oid sha256:e48f2f7c177ea47fa096d1fa37ef22c4cc818d43b18cdf64c26ed96c79f00b48 +size 49743 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/22.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/22.png index 1b0034ab58..8706c7cf0a 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/22.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/22.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2800403642452bff9858db473777b93030dde29c04b0280665a36aa9b57fcb18 -size 31855 +oid sha256:40cbbf0c4d939b696e01302614aab6affe22775783bf1902a33644fbb6ec5bdc +size 50602 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/23.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/23.png index ddf9fbf7a2..ed2a543c8a 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/23.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/23.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0225e8395085728e2988ec148e33dc9df009746d5fe0860b12539776337372a -size 32035 +oid sha256:7e7901c37835974abd5163eede0a3049db5e936358089b3de67e99699019582e +size 50810 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/24.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/24.png index 302c97696c..4ab5d089fb 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/24.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/24.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f348830b798dff6f62a5c279105181efb4e692fe83f3e42cd3cae9b5ca0af7ce -size 32097 +oid sha256:80f0028de9c58117e8a4179ffa890ba0771f7135016fc018a2157bb2cabaa920 +size 50971 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/25.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/25.png index 3c3e74adb9..6ac020e21a 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/25.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd5fb5b83f44bbbc1b32690f54f995eb1709937de780ca35e31598c399c7d8ef -size 31750 +oid sha256:a01abb68b8be21b7531455dbd94b256cd826a52c170a62f88955e2b7a55376e0 +size 50531 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/26.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/26.png index 1b77c89bcf..2119d24209 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/26.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Decode_VerifyAllFrames_Rgba32_kumin.gif/26.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a02cb3c82c90b05381126ff3f696e562200442aca34ba54830878701c51d932 -size 31647 +oid sha256:922fa84cc14f0795c8cb7459b6269dc5789018d1f0591560fba0a3c3e80b00b2 +size 50484 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png index 87c1fb7286..2449c3efdb 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_Decode_Resize_giphy_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfd3afda359646aa3d46e1cffbfa7060395fda4c36c419ed3a2d8865284d090d -size 4031 +oid sha256:43e15b35bd282ecbf5f3b0b08b0f6fdba2e9144b0c17875d27bb8366b213c999 +size 4020 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Argb32_trans.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Argb32_trans.png index 1cfc3adb88..08b9fb7244 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Argb32_trans.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Argb32_trans.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4c441acc8fe614c9b375e8e609e735e1c767918ecc1f8f2f028665fdcfcf34 -size 12441 +oid sha256:49eeef6073a5ef2494ecfb138071c22157870e11c903ca81b837b32a813732ac +size 20256 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Rgba32_trans.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Rgba32_trans.png index 1cfc3adb88..08b9fb7244 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Rgba32_trans.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_Rgba32_trans.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4c441acc8fe614c9b375e8e609e735e1c767918ecc1f8f2f028665fdcfcf34 -size 12441 +oid sha256:49eeef6073a5ef2494ecfb138071c22157870e11c903ca81b837b32a813732ac +size 20256 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_RgbaVector_trans.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_RgbaVector_trans.png index 1cfc3adb88..08b9fb7244 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_RgbaVector_trans.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/GifDecoder_IsNotBoundToSinglePixelType_RgbaVector_trans.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4c441acc8fe614c9b375e8e609e735e1c767918ecc1f8f2f028665fdcfcf34 -size 12441 +oid sha256:49eeef6073a5ef2494ecfb138071c22157870e11c903ca81b837b32a813732ac +size 20256 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png index 24f5e9c0cd..a52b27708a 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue1962_Rgba32_issue1962_tiniest_gif_1st.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f8c6d416f09671777934e57bc67fb52ccc97145dc6f1869e628d9ffd7d8f6e7 -size 119 +oid sha256:9ab8374e77865606a2426e3d22628f717914472431de1d9d8ee9690d319850a0 +size 118 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png index b07e806620..bad4e3803b 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:588d055a93c7b4fdb62e8b77f3ae08753a9e8990151cb0523f5e761996189b70 -size 142244 +oid sha256:05c72bee64dbf29fe16349d8dfdfbee565779241dbfa860f62a53b649be000e3 +size 1884 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png index f63cc98ada..75ffac6083 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f39b23217f1d095eeb8eed5ccea36be813c307a60ef4b1942e9f74028451c38 -size 81944 +oid sha256:bc99ff5deb71c9caff1a645b4175b720edff792982d7c0d4189c769405386a90 +size 9474 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png index f63cc98ada..75ffac6083 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f39b23217f1d095eeb8eed5ccea36be813c307a60ef4b1942e9f74028451c38 -size 81944 +oid sha256:bc99ff5deb71c9caff1a645b4175b720edff792982d7c0d4189c769405386a90 +size 9474 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png new file mode 100644 index 0000000000..d8c8df1263 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daa78347749c6ff49891e2e379a373599cd35c98b453af9bf8eac52f615f935c +size 12237 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png new file mode 100644 index 0000000000..05e9892479 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d41ed74bdeaf5fb851935411dbe95e83c9ccbb8d5bad2d73c43fc5de4c5d798 +size 1833 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png new file mode 100644 index 0000000000..9ac022ef4e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:052fc0cb71e18e6eb599f58bff8f4bfa822f536122f5dad77e6b8fa2c61bb207 +size 1271 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/00.png new file mode 100644 index 0000000000..7afb1c85bf --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f47b8bdb0159b3e9c6f3ba81a977fe198dec6cb0c47f33e8ba84203ef40dac9b +size 131 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/01.png new file mode 100644 index 0000000000..d892d615a9 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e3c543efd04b63ee11d0e771f6b2dd2cba244f9b4a63f78a5555adfef13d60c +size 171 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/02.png new file mode 100644 index 0000000000..cd8df14795 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ccae6eb76a1c3a8c7d75c78b7153964cbedc9223297bf67323dd2ec808ca96a +size 159 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/03.png new file mode 100644 index 0000000000..5a42c4c6f4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2980_Rgba32_issue_2980.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6df2819dfd822558f734b70adbcb8edd4afb63072610d5cb79579ca84958c324 +size 151 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 index 923fbc1225..46f520cb0e 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:800d1ec2d7c7c99d449db1f49ef202cf18214016eae65ebc4216d6f4b1f4d328 -size 537 +oid sha256:4abf8935cd9ed76e3e2fe92d106928ecb7ede58498a550e52d52f0f7d6561c8e +size 466 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 index 6c2134d8b8..d4a1f1d910 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94dcd97831b16165f3331e429d72d7ef546e04038cab754c7918f9cf535ff30a -size 542 +oid sha256:a5028c4c0250855b9f0f4ec81cb376d5ab6acd73c385c5fe5e6df6537aa95d32 +size 462 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 index 6f50397ea4..c9f0f00c9f 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 -size 540 +oid sha256:92e36c91cae2dbef6570792d25b9eb08080efc6be3f2e887c2da6d87411f784d +size 471 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 index 82061ba0aa..e44b6db71d 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 -size 542 +oid sha256:e9f6c6bee409938823b9a8ca106301142c44ec7479a72c3f6b3ea821ade30b72 +size 466 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 index 8902eb824a..bdc59e93c7 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b78516c9874cb15de4c4b98ed307e8105d962fc6bfa7aa3490b2c7e13b455a2d -size 544 +oid sha256:c7c3b861dca59f386fdc88acd8849f71c68ff0af45b2dfa712a55d2d865605d7 +size 462 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 index 82061ba0aa..e44b6db71d 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 -size 542 +oid sha256:e9f6c6bee409938823b9a8ca106301142c44ec7479a72c3f6b3ea821ade30b72 +size 466 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 index 6f50397ea4..c9f0f00c9f 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 -size 540 +oid sha256:92e36c91cae2dbef6570792d25b9eb08080efc6be3f2e887c2da6d87411f784d +size 471 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 index efba40c99d..fd9984855f 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5016a323018f09e292165ad5392d82dcbad5e79c2b6b93aff3322dffff80b309 -size 126 +oid sha256:ed091484d552b8c234ab75921e423e6d01172df61124b9b03dcdf2dadab34b85 +size 96 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/00.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/00.gif new file mode 100644 index 0000000000..b219975ade --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/00.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb4bbef09dc6618380e34c5dcf8612fa5a51ba81a09edc5500be9191f0554d9c +size 49665 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/08.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/08.gif new file mode 100644 index 0000000000..2d50761636 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/08.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81a0d629326bb39cfced1a261542e5f94b423527f95bc45422670091b91583b4 +size 50730 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/104.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/104.gif new file mode 100644 index 0000000000..b1b7781a21 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/104.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e64d9f2f7a8346f62c9b41a14b3e6b71f76a48e07fa42ac9e0d4a5b146a8a9da +size 58856 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/112.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/112.gif new file mode 100644 index 0000000000..f058764b4f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/112.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b01517c53b19f6b151a76cc75142ba3a8a45da8c6e94416447703cbd54ce1a8a +size 48282 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/16.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/16.gif new file mode 100644 index 0000000000..b9f1e2d099 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/16.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1db212159613778c962883de9067852da3bea5f3483dd9f967c0aabbcdc1b2f6 +size 64655 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/24.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/24.gif new file mode 100644 index 0000000000..c7a1368ce1 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/24.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d09ddfff1f26ed7842df5bf4b8938373700658322c85b154a878dd5e3a90dc1 +size 64432 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/32.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/32.gif new file mode 100644 index 0000000000..ffd61e5123 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/32.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18ca31ff631ecc33fe33a893e94e23af8b086a78c3684461e449c02800fffb2b +size 66510 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/40.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/40.gif new file mode 100644 index 0000000000..eb93ea4d4a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/40.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:823358342cbc25a9f7ae34abc2669096acd7c0e0c93a8a0b371e548822ed0897 +size 66912 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/48.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/48.gif new file mode 100644 index 0000000000..99f0e64dc6 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/48.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90558311a7b7127d9f970a17ae0630d81507be246f511f1cc3b10c6ee953a25c +size 61986 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/56.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/56.gif new file mode 100644 index 0000000000..8e6410f9bf --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/56.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9514b736d946d4e93ba3f59b586d2c29e0c031155f7824756ecf468ef87ea8e6 +size 61367 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/64.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/64.gif new file mode 100644 index 0000000000..2257625c41 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/64.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0334c551b9efcaa9f5c16c4599884b4aabe5129e3f023222be3214cf8623242e +size 60825 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/72.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/72.gif new file mode 100644 index 0000000000..efc9569f4d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/72.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32a9fecdad6508c1c6beae839717d1854cca1f7b247bff36a00a93cc953f608c +size 57370 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/80.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/80.gif new file mode 100644 index 0000000000..9f7ae53fb0 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/80.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d50d4ccba947ef95b9e8a2c4acd08f57c414f4e38a0d03d65b5fee093e4481a +size 67784 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/88.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/88.gif new file mode 100644 index 0000000000..22dc30784c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/88.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:840780f2916cb9d010a95802d9c123c3051bcee5dde7b173a50854e3b5f3636a +size 72552 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/96.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/96.gif new file mode 100644 index 0000000000..53e1a35cbf --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue2866_Rgba32_issue_2866.gif/96.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41db6ded3de84d43dec1175c1481f75a045c5ad126369e4e82ae29ec4bad0bc4 +size 76868 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/00.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/00.gif new file mode 100644 index 0000000000..4a2dcec8d8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/00.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba81598dc214845f18064f987a55eebdab0e53149df8fd67904733b42c760ad6 +size 7389 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/08.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/08.gif new file mode 100644 index 0000000000..4a2dcec8d8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/08.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba81598dc214845f18064f987a55eebdab0e53149df8fd67904733b42c760ad6 +size 7389 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/104.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/104.gif new file mode 100644 index 0000000000..a28b1dd603 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/104.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6bd47e66b07bab7edf53a07631016f8334ba337243d6b8d61b0c8a3cf7f3f0d +size 7974 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/112.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/112.gif new file mode 100644 index 0000000000..a28b1dd603 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/112.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6bd47e66b07bab7edf53a07631016f8334ba337243d6b8d61b0c8a3cf7f3f0d +size 7974 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/16.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/16.gif new file mode 100644 index 0000000000..016f9f9b6e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/16.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03d4e5cdaed2def942ca8480c9e45bb3d7914c6dbe3eac2b287afc8ff815b95d +size 8705 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/24.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/24.gif new file mode 100644 index 0000000000..285514e230 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/24.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ad084d64942ea1dec0c1b22a88400b47f14145d761aa22a8e7deb42b5bd9fb0 +size 13011 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/32.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/32.gif new file mode 100644 index 0000000000..34eff6c6c4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/32.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:714cf4be4fc6c4164494ca1950796211577ab61cbd939f7cc53a36c3f7f742c5 +size 16457 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/40.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/40.gif new file mode 100644 index 0000000000..7a732b4fed --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/40.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb04c45837b763abf2c326b3e831c5b772853d50c0bfc85fb7ac7ca62361292d +size 16872 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/48.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/48.gif new file mode 100644 index 0000000000..7a732b4fed --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/48.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb04c45837b763abf2c326b3e831c5b772853d50c0bfc85fb7ac7ca62361292d +size 16872 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/56.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/56.gif new file mode 100644 index 0000000000..6d60d49813 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/56.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c68bad5aebb65e86e4295956b9f0bc21e181a6d8dadd9dbfbd5febf0a5ba0d8 +size 17414 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/64.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/64.gif new file mode 100644 index 0000000000..9af6451a67 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/64.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d0672248b0e45ea700aa0c484ed0e342284ce18cad1cdc233700614636bed11 +size 16556 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/72.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/72.gif new file mode 100644 index 0000000000..282ef1f8ab --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/72.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b12d34adf8c531c0d4ab02f7b57507a585bb1d31fb82d828c8c52732cef9f18 +size 15590 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/80.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/80.gif new file mode 100644 index 0000000000..282ef1f8ab --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/80.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b12d34adf8c531c0d4ab02f7b57507a585bb1d31fb82d828c8c52732cef9f18 +size 15590 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/88.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/88.gif new file mode 100644 index 0000000000..108da2ce30 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/88.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a31b9aee61fbc7d5dd5f163f2c81cb36140a05d788a7ed2d15de550c0fe13232 +size 14561 diff --git a/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/96.gif b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/96.gif new file mode 100644 index 0000000000..161443e4f8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifEncoderTests/GifEncoder_CanDecode_AndEncode_Issue3142_Rgba32_issue_3142.gif/96.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bc49ba55ee33a3aff5069b7a6bf45cdb429ad691f8701093ad7fe04abe25ea1 +size 12156 diff --git a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png index de79ec729c..84c7e64ee0 100644 --- a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png +++ b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aada4a2ccf45de24f2a591a18d9bc0260ceb3829e104fee6982061013ed87282 -size 14107709 +oid sha256:4a4d2a3a1f320d3fcb121bd8edf716354b7e8ceb251a9eaa2087f443a3adbbc1 +size 10896218 diff --git a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png index ff5b35a5f7..679615d17e 100644 --- a/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png +++ b/tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dca9b5b890d3a79b0002b7093d254d484ada4207e5010d1f0c6248d4dd6e22db -size 13909894 +oid sha256:0c3f30908a803ac5956dbd5959b225bceab1fc673a1b89d55d1096ba024c7bcc +size 12519351 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AdobeRGB-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AdobeRGB-yes.png new file mode 100644 index 0000000000..46d728c8d6 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AdobeRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:650256637c7039c59e135bbdabd31bee1ad99c9d9ff014562300945d41b6ac3a +size 459515 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AppleRGB-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AppleRGB-yes.png new file mode 100644 index 0000000000..3c856c2c32 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-AppleRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e2d477de13a3767a364260b7b54b1f2a46c833e8df7aa1fded7990a3ac60563 +size 483943 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ColorMatch-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ColorMatch-yes.png new file mode 100644 index 0000000000..408f52e11e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ColorMatch-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ee8f5c7f658b11a380e6da2130dc8563da3255f3ff5d9fd9cc98ec87bb1852b +size 465348 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ProPhoto-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ProPhoto-yes.png new file mode 100644 index 0000000000..714340ad6f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-ProPhoto-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c702f52b08ac5be9eb65fd1f4e841fc46c897db45c709974954265b93d46b854 +size 478310 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-WideRGB-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-WideRGB-yes.png new file mode 100644 index 0000000000..c678bf52b3 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-WideRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f06f61eca4187823cfb76255caaa9f9f9d43e9ea56da3cd0fac830f6198d9d8a +size 469400 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-sRGB-yes.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-sRGB-yes.png new file mode 100644 index 0000000000..786937c2bb --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_Momiji-sRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc3657d9aa0e36beee1d24144ace5bdd1198c0249ac661f302ae38b7d478d374 +size 436479 diff --git a/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_issue-129.png b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_issue-129.png new file mode 100644 index 0000000000..52dd0e0f3a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/IccProfileConverterTests/CanConvertToSRGB_issue-129.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d423959ad2f5c9a3fc8df0ca1ff9b3d6d6751bb552cee65dbda29623581cb816 +size 2043144 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png index 07c29c0976..95ea3d4721 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:362eddc5e06d672b4654bfe7a1ded995934a1c59719a3f909773b2e61931ffac -size 1332495 +oid sha256:9a6f484f06c0b466d16591db78fec3cd2c3f6ebc7578896075cd4af95d8ceaab +size 1411818 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png index 1f38ec542f..8288801623 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7a1e89adcd70f792d678786a177bac927a15d6065001cd76aab0bf3cc1b7b4c -size 1034853 +oid sha256:88271ffea79e42914b8c37828da65afb691c7319b8e28fdd35942ecba4087baa +size 1060013 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png index 349ff64858..e72b1a0415 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9dbb00a4be909d4b7fd605f83f786ff85545da54272a256c100afd80d687e8f -size 93253 +oid sha256:3b160cc44490303c23c0abaa0dda827d68e11bfa67127e19da974073085abbe4 +size 96042 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png index 830a95bd74..ccdb19e4db 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24f637742951c438da5acbae6a93545830ea4d0065031572a3bdd1c96be15cce -size 48990 +oid sha256:7ce1a5d71cefd6527daecb8c1d9089f63189b833f1186971ddb89c6257d076ef +size 46745 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png index 0512251ab4..4f2aae2ed6 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0501e53f97ddb4252e15073dc75f4129428befe5594a07297c21ae9f89f075b2 -size 316278 +oid sha256:522b0a2f9a97689217af461ab2a63a41553c0ae6ca28cde046b01019c87e2393 +size 348228 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png index 27c22ecc6d..e24cc59690 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2253c83151238056caef2f3bca8800108a84360b2ff21979e4ff37fdddd2232 -size 419852 +oid sha256:5e81ac851f979c017082180a13ee4ddfe26c4151bfc082f7915252e57da44ec7 +size 416967 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png index 4032a32afb..ae30676b36 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b5e1d91fb6dc1ddb696fbee63331ba9c6ef3548b619c005887e60c5b01f4981 -size 27303 +oid sha256:79291e1eddee66548156fc40bb5c8b1209c7b0048fb56f2bb7890c7d89ce655e +size 27264 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png index 018ecda7a5..8ddb748802 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:733cc46271c4402974db2536a55e6ecae3110856df73031ca48dad03745d852d -size 35375 +oid sha256:5196d5a20f639d833cf1ad979a290c144c97b41897fa5f0fa7454f5f4e8d3c11 +size 28697 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png index e5ce7eb3d3..acca4b44e3 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6543f546fac9d05ebdac7a534b0cc422f31bfd81067212a19cb3a52ad24560a8 -size 3978 +oid sha256:9894983085f3d3d45b8c4ea9acc680932f236a4602230046386130aa2af916f1 +size 4936 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png index 830a95bd74..ccdb19e4db 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24f637742951c438da5acbae6a93545830ea4d0065031572a3bdd1c96be15cce -size 48990 +oid sha256:7ce1a5d71cefd6527daecb8c1d9089f63189b833f1186971ddb89c6257d076ef +size 46745 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png index 23f1941dcc..dc41461e01 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad57cf87eade9ee6663f575b358eafaa869a16b3b02d0fb00ca8c683422b85a0 -size 47601 +oid sha256:26ef4244aa761c7b6c51534e414f79438753b9dbd1fbf45d2f1e2fd2dd4ec4c1 +size 46636 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png index f35c40ed89..66337b5c94 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a17478333c6ef0943aeb96faba1c0ea560995d0612564fe033b72b2949f4869f -size 66748 +oid sha256:91a522b32485af3be304c2d98a60b9906112637a4627d69a072976474cef7485 +size 66932 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png index 5f41fb5239..76183356bd 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63f7105e9e2d0794b3f7225af2187a5717c12b806b68b33b98e08e0a0b1ffa79 -size 71051 +oid sha256:f374012655af194ebece2e9329da416be830d1cadd24a2bd584480d45751ea78 +size 58272 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png index 2386ae49a2..ecf22f93ca 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e5d84fa88ac8b552c21c042f155801924240d764da365440d28eb8133950925 -size 568177 +oid sha256:36b557cd6d55389b8f02b811ecd2b14f1f1c440c7e8dd5a5a3c55cea57547203 +size 564682 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png index 3de8b15578..c144554ca2 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb636a9f304cdef713b823adc7424108b4d44fada294c48a366278c9b9e7b4b5 -size 20163 +oid sha256:ff9554b1f421ca04e0580fbb505895f002b80298feb128a15cdc045a1625a490 +size 20111 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png index 07a7c3573e..30bfb538f1 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2935d3dc66eec334eec65c1ed9fbb04046404a9c28bed413ac635878e63ea6cf -size 114688 +oid sha256:c2eb2bd4db9b67466d96fe154cd771d6780a24a798a0c051f307425f1b0e3d15 +size 115708 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png index 8241c36ac3..e9a2a0bd86 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b48730a7a149e13dd87a24e62c0704895b08a5ed1f8ea48a6a6a7248450750d -size 301416 +oid sha256:c5c5b5711041f22e8ee1afb344137c00ee80ba51e777689513e8dfeea855befa +size 297941 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_CMYK_ICC_Jpeg_Rgba32_issue-129.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_CMYK_ICC_Jpeg_Rgba32_issue-129.png new file mode 100644 index 0000000000..4e554a34d3 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_CMYK_ICC_Jpeg_Rgba32_issue-129.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9750b54f5220020e0c3f5ce22e4f108cb70290750c613c29beeb49473d5c80a6 +size 1349665 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Issue3064_Rgba32_issue-3064.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Issue3064_Rgba32_issue-3064.png new file mode 100644 index 0000000000..12692cb9ea --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Issue3064_Rgba32_issue-3064.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73497e1eaddaa86cc915fe59a177e9ab6423d725ab4e6af21c8414c9edc6ceaf +size 347 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AdobeRGB-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AdobeRGB-yes.png new file mode 100644 index 0000000000..37e1e37cdc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AdobeRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcc32a36dcdfe3b9cfae17a81d3eb180248db4507ae93fdd4bb92e1e5e29950d +size 466215 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AppleRGB-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AppleRGB-yes.png new file mode 100644 index 0000000000..dbfa40378d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AppleRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae9a32eba21fa0bd2039803b5af8ffdba0409c104746455d5028741f214eb1a1 +size 479922 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ColorMatch-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ColorMatch-yes.png new file mode 100644 index 0000000000..5427b5c954 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ColorMatch-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2bf66bb6d7eb3ff402b8564fb51bc951792ad47dc17fc9c7c886744638a59bb +size 469096 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ProPhoto-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ProPhoto-yes.png new file mode 100644 index 0000000000..7870440681 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ProPhoto-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9c015e599b1710fd50c4ad61104fb03f27f211c13686cb162baba492ec4a935 +size 482675 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-WideRGB-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-WideRGB-yes.png new file mode 100644 index 0000000000..2ee122b8de --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-WideRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e25ba3afb3346633db52e91062c4266474bc9d9c0b3fcf686f1040b3a4d70d0d +size 476050 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-sRGB-yes.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-sRGB-yes.png new file mode 100644 index 0000000000..a1e876d2b2 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-sRGB-yes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b80e88a7fdb27491687202c247eafbeb9d1c024affd5dc4633bcf15602fed990 +size 464921 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual-cLUT-only.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual-cLUT-only.png new file mode 100644 index 0000000000..8a27087f51 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual-cLUT-only.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbe96e449dfeadc71bbfb0b48d2f56e6357d5b64a225153eed60263ea3356e71 +size 119987 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual.png new file mode 100644 index 0000000000..586bc96479 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e9e4da17b7270565fd592f4ff710ff317e1054794d966d33f11748defc7c35b +size 124944 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_sRGB_Gray.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_sRGB_Gray.png new file mode 100644 index 0000000000..5164defc14 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_sRGB_Gray.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0eaf217570e283b6fb9a7d52ba5087d760d9a98827e507cbae299915871e1c31 +size 2595 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_YCCK_ICC_Jpeg_Rgba32_issue_2723.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_YCCK_ICC_Jpeg_Rgba32_issue_2723.png new file mode 100644 index 0000000000..a73e5f31ef --- /dev/null +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_YCCK_ICC_Jpeg_Rgba32_issue_2723.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de73fa7a2cdd79763e40c51381f9a0cf5c7d43db2dcd7a86dad0a5b68377665f +size 421249 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png index e982d9034d..16df99e04e 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bb6ed717a2af582d60ccd6c1c9c1ac92df0f8662755530b7e9063724835b23b -size 27709 +oid sha256:9be82bf220095b5650c7cd0a0eba9ee2b0fbe39881ff2bbde465ca9d2f94490f +size 27803 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png index 65aac22e90..98aac2c55d 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5 -size 28178 +oid sha256:b7e4b589fdbad2688895317fc760a834624f76cc9a027a65ce3cd6b35563ec85 +size 28514 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png index 65aac22e90..98aac2c55d 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5 -size 28178 +oid sha256:b7e4b589fdbad2688895317fc760a834624f76cc9a027a65ce3cd6b35563ec85 +size 28514 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png index abe31b2be7..6659673306 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc67170d70378ad8b8c0e1c1695b5c268341f0d26a6c788d1a8dffa8c90482a0 -size 102165 +oid sha256:d28335f6ad2421b6bc7e420c2e61ead73cd64f63ba7e1c9bd98ac1c140188ddb +size 104381 diff --git a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png index 87087adc51..9c01562f16 100644 --- a/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png +++ b/tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae7f6ebfd9f2ddd85611827fda13eaf316d36d5187900458568f80b929effb9b -size 28291 +oid sha256:d0db0474f28d6302aae207b09f63e2de9e9ce6a9777a95dbac69ccb957d08daa +size 28381 diff --git a/tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb48_rgb_binary_wide.png b/tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb48_rgb_binary_wide.png new file mode 100644 index 0000000000..a1b407528f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PbmDecoderTests/DecodeReferenceImage_Rgb48_rgb_binary_wide.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bb443147c06e4d712421695f5cda1c9823c75ac8be7fbf6ce067c51d01445bf +size 5182 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png new file mode 100644 index 0000000000..023f346e03 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4347cd89196c09496288724afdd876b227063149bba33615c338ebb474a0cb19 +size 47260 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgb24_flecks_ImageThreshold-0_PerPixelManhattanThreshold-0.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgb24_flecks_ImageThreshold-0_PerPixelManhattanThreshold-0.png new file mode 100644 index 0000000000..a180f01186 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgb24_flecks_ImageThreshold-0_PerPixelManhattanThreshold-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10ee142fc1d3638ebe53fdd21c0a4c53008801befcc5e986051b796561cae887 +size 237676 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_Issue_410_ImageThreshold-0_PerPixelManhattanThreshold-0.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_Issue_410_ImageThreshold-0_PerPixelManhattanThreshold-0.png new file mode 100644 index 0000000000..020facf415 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_Issue_410_ImageThreshold-0_PerPixelManhattanThreshold-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:511cb90e72fcb837e4c9a31561a3c914f5201452d4ca63502cb5219cb4dc42be +size 619 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_clocks_ImageThreshold-0_PerPixelManhattanThreshold-0.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_clocks_ImageThreshold-0_PerPixelManhattanThreshold-0.png new file mode 100644 index 0000000000..0f184812b4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_clocks_ImageThreshold-0_PerPixelManhattanThreshold-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2dc73f1b4435a26125d910f005b5df7a540168a954f44c01a8e46df201adb1b6 +size 335851 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_colors_ImageThreshold-0_PerPixelManhattanThreshold-0.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_colors_ImageThreshold-0_PerPixelManhattanThreshold-0.png new file mode 100644 index 0000000000..74647f04fb --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_colors_ImageThreshold-0_PerPixelManhattanThreshold-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cdcc80a8c662c50d2f72ad8e123d595c6e80394538c05666b8d3531d651e71a +size 11270 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_screen_ImageThreshold-0_PerPixelManhattanThreshold-0.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_screen_ImageThreshold-0_PerPixelManhattanThreshold-0.png new file mode 100644 index 0000000000..c3a61bde84 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_AppleCgBI_Rgba32_screen_ImageThreshold-0_PerPixelManhattanThreshold-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00bb4c7b345389f5d95252c19d70fc2b654c4f0198e6a704b603da92b78e9a0a +size 102982 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png new file mode 100644 index 0000000000..ffc9839019 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b72c885278a066e63c013885c42b772275f25a5f0b2290aa38c87f3dbeac984b +size 81432 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png new file mode 100644 index 0000000000..25a97ca48d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:936261278b1a9f5bf9a2bb4f8da09f2a82e1b5c693790e137c5f98fa4d885735 +size 81785 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray.png new file mode 100644 index 0000000000..5a35cf5796 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf856e49e4ece7e59eea684f6fa533ba313a36955be4703894f16b100283cb4a +size 2687 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba32.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba32.png new file mode 100644 index 0000000000..270555a555 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:337e84b78fb07359a42e7eee0eed32e6728497c64aa30c6bd5ea8a3a5ec67ebc +size 5151 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba64.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba64.png new file mode 100644 index 0000000000..dc5f4a559c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_sRGB_Gray_Interlaced_Rgba64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:456ae30184b13aa2dc3d922db433017e076ff969862fe506436ed96c2d9be0a1 +size 6143 diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png index c697cd4c29..37323155f6 100644 --- a/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png +++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/PngDecoder_Decode_Resize_splash_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c87a09b28b3f857e13a2bb420d16caa131a67c45b0fe31404756042f30de6d0 -size 28139 +oid sha256:a6a0e244af51f47f725dfcf1ef60705dd3223269db98685a1078b57eb2273c31 +size 23988 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/00.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/00.png new file mode 100644 index 0000000000..17c8e35c6f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4aa1dea5da7a94fddd2259cd86e7171a57cb5cd2198decedd86149778b9aa20a +size 64030 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/08.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/08.png new file mode 100644 index 0000000000..f2ec385a88 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/08.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16d4159e492162372a16b08fb3a4fdf36908ce759c6a0ad72a0da4bbeb477b72 +size 68619 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/104.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/104.png new file mode 100644 index 0000000000..dc57ed717f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/104.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1095bc829b8fab3c5fda66b90cdd751b81439ad80e9fc7d564a5212c75155b2c +size 76100 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/112.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/112.png new file mode 100644 index 0000000000..0158b28407 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/112.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0212cd29319c0d90c18a5f90716156a888c9925e4a36a6d9bbbe8d67a706db8f +size 66282 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/16.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/16.png new file mode 100644 index 0000000000..52867f468e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:620ea89afc2e41b6199c47b2e4777f5ef9afe139e22bfabca9ec094b0adcbdc2 +size 73862 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/24.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/24.png new file mode 100644 index 0000000000..26ae147489 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/24.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d39997f18af5b97ed32ea30c15da879f70906b4f5cf12439ea6ff2d229e40199 +size 75734 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/32.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/32.png new file mode 100644 index 0000000000..6cd5074ecf --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b459edf763ffff8a8a016c1b7d9e13b7e99f6752e4452b52539d2eb0f7f31be +size 79505 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/40.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/40.png new file mode 100644 index 0000000000..8b9ea7c239 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/40.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1957c82709b22abbaa354ecd679159eeddf135aabb301c266518c23ff4918f9 +size 77588 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/48.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/48.png new file mode 100644 index 0000000000..4430290427 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/48.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae33b303bd1feae301cf645ee6596c499ad4eaa9164c52a37fca760169f9f363 +size 80317 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/56.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/56.png new file mode 100644 index 0000000000..8feae54c6a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/56.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d86360bd43de392a9cf4b671a4cf3cd7ba9f61952a6d0400a5baab7dbb5eeb6d +size 78578 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/64.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/64.png new file mode 100644 index 0000000000..284ad99233 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60e1694b121d0d3fa17d62f4477de349376aa6c2a98b83f9a4525da9ea471662 +size 75204 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/72.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/72.png new file mode 100644 index 0000000000..d09b77afca --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/72.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9348ef330a239bea501f41a3256107fc10a6bb323642118b1fcbe5227b742c88 +size 79048 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/80.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/80.png new file mode 100644 index 0000000000..d31fd5a808 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/80.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:452dd2339b6ad3e35528601c289d88eafcc7e9a6e717f25da2bbf4267b85be97 +size 81553 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/88.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/88.png new file mode 100644 index 0000000000..669dfb8600 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/88.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b67e3b57b62f9e1c924de6fbd2699f93a6594812ad8c4cafd6160323547b1a88 +size 86402 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/96.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/96.png new file mode 100644 index 0000000000..f9a5aee191 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_issue_2866.gif/96.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:887e94402f93a491820d3d25d641c5901cf796c1275a2feb9e1c072a6a63ce28 +size 87335 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/00.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/00.png new file mode 100644 index 0000000000..961e205f9f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6baac5db47ef2ee3ba72c7e8d5e04fc888c5e1460d4d7e348f63b597e1b5ff7 +size 23660 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/08.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/08.png new file mode 100644 index 0000000000..82cb6168e8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Encode_AnimatedFormatTransform_FromGif_Rgba32_leo.gif/08.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fad45a0682a70a1fb84eec76d4a80ee054e85df59e6729508f3698d39999e31 +size 29509 diff --git a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png index 4c78303750..c10227c6bd 100644 --- a/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png +++ b/tests/Images/External/ReferenceOutput/PngEncoderTests/Issue2469_Quantized_Encode_Artifacts_Rgba32_issue_2469.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1af50619f835b4470afac4553445176c121c3c9fa838dff937dcc56ae37941c3 -size 945821 +oid sha256:b33a960891c6b1e9cc6ac2ddc3cf49d8f49e0c749dfa7a67db49354988b129f6 +size 935007 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 index 7af5391f70..9c679acee9 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f934af128b85b9e8f557d71ac8b1f1473a0922d0754fc0c4ece0d0e3d8d94c39 -size 7702 +oid sha256:ad1630f3da7c2b997d04c75de2ac1465255c977454461f2fdc7026b3f87e11f1 +size 8232 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png new file mode 100644 index 0000000000..fc2f4b0c6b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac986987f25d25ab964a5bef710fe81166cb643d85511906218b4f0e72e9e840 +size 30532 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png new file mode 100644 index 0000000000..e0357c3f1a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0ada2a4d32a3a757b803dbf08148f113f5d358b31af79a77e97c660ce96c302 +size 1608 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png new file mode 100644 index 0000000000..18d91fe298 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffc30373989ec6857797b460931f011b30baaec633b095b6fc3d8fd5d43c77ec +size 2467 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png new file mode 100644 index 0000000000..c1b6694f55 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9828ef0faf1a6709673cfe39028ed4202920d346bcc172bda6683bb3d1d0a7a3 +size 36577 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index 3826753d53..a515475c9d 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206 -size 10951 +oid sha256:6bff913e6e67129325203fae91278ca17407b10d99c4e4f571e6cfe3b5b7f93c +size 10889 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png index f9aa1ffe03..779e437ffd 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d0cf291ebf5d8cebab1cd76e2830e5e2d2e0d9a050f7187da72680ead39110c -size 2757 +oid sha256:54b761b76d03216e7aa6238eee92755c03f7b016bffd1400be66ecf136b29c26 +size 2747 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index 3826753d53..e165b5f3c2 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206 -size 10951 +oid sha256:16da371a29269dade522b3d602beee8f769723c5712a348d960805b75619376d +size 10889 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png index 2f9109ba38..7d420da8cd 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57698b6666029a55edf8bd35a7ba96f68d224988cf01308a3af1c6606ae9d0b1 -size 10174 +oid sha256:b25b190603828131be8d82a27e019353c9bf80dcb38536e325abc5aa065762ed +size 10230 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index 7dfec78983..96e71e12ec 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc7c9da04142a679887c714c43f1838eba0092a869140d234fce3412673207c6 -size 13575 +oid sha256:0cc07a20532c52151388c42d7add4f9749913c4dd7629253400a51d40760df23 +size 13566 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index 6e3b97f2df..5e04f3fe3e 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8b973f41f8afa39b94c71b307b7eb393953e2d083d56e1f0e8f43d6ab1f342a -size 16821 +oid sha256:64aae32ec91233b6a139d2f515db4a3e609fa3ab6c660cb53b05d672e7f70e6f +size 16795 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index 6986c03912..8206fbdbe8 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:122c1501e09516244f0db36e1cca373ff68514a18e84f57ed3072d52d6112e36 -size 17022 +oid sha256:c6167a1fb585b49167f4c8fa1f19111f10c825ea7d41ae4e266680d22b2bb28e +size 17094 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index 76b53fabfb..76aaf324ae 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12181516bce69c9302f15bba928fd530796449599cb9744a2411cc796788ee3b -size 18066 +oid sha256:131e831cc2e2d8eb4f5860d0e685b31006ab846433e38440b6a85a445aed1a12 +size 17890 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index ae4242a42b..10301fd320 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1eb5accc5ada5b963ecef6ac15bfb1845f481e51aef63e06a522ea73bbeab945 -size 11194 +oid sha256:aedcc9342e0b37d60759330f62db446646c31da172e21d931ee8e8451ee720ae +size 11193 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png index efb6a2deed..1f736d0402 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0418f0ea38ec19b407f2b5046d7ff0ed207189ad71db1e50e82c419d83620543 -size 2759 +oid sha256:34f21056cac1ec3f1bd37a6c50466210e7ca7d8263963d2c503535b40e5b31d8 +size 2752 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index 976be43a3b..b5269595c5 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1233a9ab2c4b0b17b0248c3d40050c466330c22095287dfbdb8bf7dfbda4ff1f -size 11212 +oid sha256:b4e0cbe71672de45880111fb45c7b544203f67154060fa0707ba9216dfd6d883 +size 11217 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index 04fb2e87e0..81a20c24a6 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2912d4e42c7b76d9ff48a49921d6472e351662597d69b88bc3708683c7933e3 -size 11221 +oid sha256:e8208cf34114bc87c6244f83a73e7c9dd4455da2fb6d25c34e32ed2fef3cfc9a +size 11214 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index b35d68aaf8..4edc410d90 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51b05c38647e0c1d88cc722e4109a882305073a065d2a27ccd3bee82f727127d -size 11775 +oid sha256:c4ee4328adcc71b1d9b3786ab2c03906aa725fefadd1d521206d5a04af421d8d +size 11711 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 64b9c6aba4..f5877639e0 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b260e816b23a43d7efb7507897ba2f5dbb6a596dd67a5abd4c9a0c005e926ee0 -size 9748 +oid sha256:fbc694ac18a702c127c588bb9184bcc39a01c1b8be5ceecadeaab4477260afec +size 9984 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index 29b95bf525..49633ed225 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50b03d627bb53048f5e56380500f116da4c503f5bb6a1b1d3c0d67ee4256d8f6 -size 15977 +oid sha256:8e85f331d7c4304fcd8ea8788da04982d9a5e43951be642bd7dbacd8907c3151 +size 15784 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png index 54dca26397..65e75b83eb 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96454548849147d7c7f0ca507c8521a7d5eaa7771f9f383cc836858870b52c57 -size 280 +oid sha256:92153056f19a20cc1d6ff65dd36ffd215eb50509cc3544e338e76c8d5665fb27 +size 278 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png index 41f94c9c7b..7a9f2794cc 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e94d224fdb284b6f1ba21b8caa66174edd7e6a3027f9dd03f4757e08296e6508 -size 212 +oid sha256:0db75a869ae36fbca7f57daa4495f2c16050b226474d203aba98cb8e0766d3fb +size 249 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png index 49cd1c8375..4f01d8c13f 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1162be9fa1f31bee8d3cba05c1422a1945621a412be11cce13d376efd5c679c -size 173 +oid sha256:09a80b11d888da121313d5f00ab0ec79ccf7bc49800135aa5eb411bd15fc6b86 +size 204 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png index 59f928178a..8985e64760 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ed262e9b885af773a4a40a4506e678630670e208bf7f9ec10307e943b166bed -size 258 +oid sha256:1ff446c4bb62d4492fc561a9dd48c4c0d95d8f4bcd9bbadf1675b2621baf9fa1 +size 212 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png index 57ee3dc2ff..1f9f0557b1 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a24f2cfc225d01294b8bbc5ca7d7f1738fb0b79217046eb9edf04e4c4c01851 -size 201 +oid sha256:2d76e8055ddbdaaa8f21117ab5e06d3e7d0f5da9d21dd2a992d82e284f028606 +size 235 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png index 7e47f43ff9..c8c5696b1f 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:938186fb3d0f468176988a9530efd22e66241a1361fff027005ec8a8ae323ff3 -size 197 +oid sha256:c7e594fd12ea7863d297d66852d3f80d5d3645636cdd39611c7b6fae068a6dc9 +size 230 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png index 0f756e7813..434457ffc3 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bc4b8ea7e7f10676d8de612fe6bc5144e100b95ff3fe7a1e3d4066a7684ce4d -size 239 +oid sha256:50f7f407d040b1071f7f6fbad96d6cfb2907d87060c671e74f6122ac5d381c30 +size 208 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png index b2d420886b..d3810b9d61 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:345337f7dffa48d95251503ee2ae8e91db98b5cbe06b579d73c38a018c781544 -size 182 +oid sha256:f305e35f0f4fac1c099b5d9f0b2775c1bb17f382aba13a068dedff45eda4632d +size 215 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png index 4f0ad9d045..74d204d7ce 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de4e2b71dade9dfb750a2c614a684963d6962958db79145c87fd23d9f0f8c005 -size 180 +oid sha256:c41784431d5a50746f66b3c34e966cb21fa8a088de797825633349cb077b4f97 +size 212 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png index 78bdb8bbbe..308fe34546 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d8b651663366e7543211635f337c229e2f88f1142886ea3a9b69587daaada97 -size 288 +oid sha256:7a6b2c8e072993c00a96692f10c7ebe6fcc6f4cfdcb9dff2d0aa6c65db54e1c4 +size 267 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png index 7015a05571..6111bcae16 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab8df31f1716c05bb8687f79c7d1154f6cc6f65e3917abe60ecc42d0df173dc -size 215 +oid sha256:018fe6af5dd7ed2edd163bf3da5e22f8333d3e3287629a2065ebfd15b7a2a8b3 +size 256 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png index 67a765e8db..a0935a1ce6 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a1671da9ea7702a37a866fabfb3ca0d746233ee108594198f23cb563af43ae6 -size 180 +oid sha256:530501bed8301799b68ba3dc8d50c877ed3f58073ab1a8a283e8f776e761cd28 +size 200 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png index 4b2bb99d96..81863d3ef5 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e7dedec16ccd66543a1d44052a104957ba62099ba2f2ccc72285c233c2ae3fa -size 4411 +oid sha256:73a18d217d0db5e6ed55cbf52801b7f083e083ca2f0a6ac20d320fd29961e6e0 +size 4399 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_ErrorDither.png new file mode 100644 index 0000000000..d2b62e63ac --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_ErrorDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27f6e8e195c4431dc7354a379152d3a8664582bc2bb1c8960ebf4088aa6505e2 +size 248709 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_NoDither.png new file mode 100644 index 0000000000..ecbf328d36 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_NoDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46b5751dc43e9ad5541913cf851ef1b061aa474a95283c712511531202d7015e +size 239326 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_OrderedDither.png new file mode 100644 index 0000000000..816fdf704b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_HexadecatreeQuantizer_OrderedDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ee35a7c21e90a2de1bf953e1c0be96d4f63492d0c8b2809fe9f39a9d0e64191 +size 266755 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png deleted file mode 100644 index 4948c7adee..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_ErrorDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a51d04953c1c82d99884af62912d2271108c6bc62f18d4b32d0b5290c01fa7f7 -size 247462 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_NoDither.png deleted file mode 100644 index d993923d48..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_NoDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f165908729d723818b6c5843bd75298d987448e2cd4278dfe3f388a62025add -size 238396 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png deleted file mode 100644 index 223d3bc012..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34eaa0696da00838e591b2c48e7797641521f7f3feb01abbd774591c4dd6f200 -size 265546 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png index 922c2bf9b2..28db1b73ac 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f1462733e02d499b0d8c61ab835a27c7fee560fdc7fc521d20ec09bb4ccc80f -size 216030 +oid sha256:af40e835e2f3cf0f406e15248169d058dc1ae69219f2bc5c3413ecea4eb4985f +size 215873 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_NoDither.png index 922c2bf9b2..28db1b73ac 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f1462733e02d499b0d8c61ab835a27c7fee560fdc7fc521d20ec09bb4ccc80f -size 216030 +oid sha256:af40e835e2f3cf0f406e15248169d058dc1ae69219f2bc5c3413ecea4eb4985f +size 215873 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png index 29c93d14e2..078c75c45a 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e6d91a3ec4f974af675dc360fd5fd623ec8773cdbc88c0a3a6506880838718a -size 226727 +oid sha256:5eb87f02c7924b764bbd2c951047b7204c56a0a1a0d6853a0fb3d30a56ed0184 +size 226633 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png index dbfab2b508..e80b9b8b12 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c68eba122814b5470e5f2e03e34190ff79e84e4b431ad8227355ce7ffcd4a6a7 -size 220192 +oid sha256:84b55eefd699cd74a1a7de958762b095f196275d2bbde2750936aed9a47f68f3 +size 220099 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_NoDither.png index dbfab2b508..e80b9b8b12 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c68eba122814b5470e5f2e03e34190ff79e84e4b431ad8227355ce7ffcd4a6a7 -size 220192 +oid sha256:84b55eefd699cd74a1a7de958762b095f196275d2bbde2750936aed9a47f68f3 +size 220099 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png index 86655af42b..ad899553d7 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dbd3189b559941f91dd6e0aa15b34a3e5081477400678c2396c6a66d398876f -size 230883 +oid sha256:c4548abed72e4f833b33eed14392206d7232112fc651becb2351fdee27da5bc1 +size 230687 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png index 82d5e5d592..a30d69d177 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4df5b1bc2c291ec1cf599580d198b447278412576ab998e099cc21110e82b3d -size 263152 +oid sha256:832173c8ca6bd7a8bf417d83b459ccddb541daed1c31539bf596cacea455441d +size 263018 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_NoDither.png index d8a1178adc..e5591852bf 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df63a3d12e2998d5242b64169ac86e3df7ab4be585a80daddc3e3888dfcb7095 -size 262298 +oid sha256:15a6dc485f0c3fd4c9fbbdb6b50437d58d68210790e37f8aab32e66a864e2746 +size 261872 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png new file mode 100644 index 0000000000..2e815d4d1c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6eeed563b407940e2a05f068c42b52738e6e1217a1500c9230f7068ca4e9f1e +size 304162 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png new file mode 100644 index 0000000000..a8f30e5f5b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dc7dc55af4ef0741a66c569876ad8a2df27164a653baa5bae536e6d121b2c11 +size 300528 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png new file mode 100644 index 0000000000..3ece7ee289 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b65e7903fbfa1ed0682221fdd86c6f0448b3f6a886cae5379720cce881a1f1e +size 305962 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png deleted file mode 100644 index ebb9ff6b00..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_ErrorDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f414473561bfa792c2e6342ff5e5dddffbdec5286932781b11a093803593b52a -size 313787 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_NoDither.png deleted file mode 100644 index 7e3080562c..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_NoDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0203ecb9e4665e7c3992b7da4777c6d35b539790506fc9ca2acbcbc2bdb5db18 -size 303979 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png deleted file mode 100644 index 5626fa1b83..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:62cdce27fc46a38a16995df8ed1501f65091d69315288479b1d613b1d87c8239 -size 321123 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png index 0205626738..03b9a37f73 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a2aae04edebcaca9b95f30963201794887fa0eac954b64c68bfe529b14fa9be -size 269397 +oid sha256:97c277005703b029a9e791e4c9dc3adcbe06054885fdd31e361e8a0a0222a291 +size 268504 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png index 0205626738..03b9a37f73 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a2aae04edebcaca9b95f30963201794887fa0eac954b64c68bfe529b14fa9be -size 269397 +oid sha256:97c277005703b029a9e791e4c9dc3adcbe06054885fdd31e361e8a0a0222a291 +size 268504 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png index 68d91fc437..a1d28a1697 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f3e9a338a5ae37c88ce0c348e0b655429220da051db3352779c277bb2dcb441 -size 270622 +oid sha256:b5fa657236e12cbb2a8d2cd747029723a6b3829b475f28626d7647d7b2150918 +size 271579 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png index 324bd92539..eba58870f4 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:752760327cc1416c171a920f1e0e95e34eae6d78bd0c7393a3be427bf3c8e55c -size 284481 +oid sha256:532fa8044bb424b451343f89bf7cb954311641056bdbd5685cd7c4fa4ad8f3c8 +size 284056 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png index 324bd92539..eba58870f4 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:752760327cc1416c171a920f1e0e95e34eae6d78bd0c7393a3be427bf3c8e55c -size 284481 +oid sha256:532fa8044bb424b451343f89bf7cb954311641056bdbd5685cd7c4fa4ad8f3c8 +size 284056 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png index 52bf2a163f..de30e7574e 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:293459538454e07bc9ea1e9df1fa5b0eb986fde7de42f6c25b43e4c8859bd28a -size 285370 +oid sha256:61ed5f4d77428be46357609d80a66e884dedbb8c255fdcc71d49eeba0eed2bf2 +size 285037 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png index 05be1395ab..c56a90ad27 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90a2b7b3872c6eb1f1f039558d9f6ace92891c86951c801da01ad55b055fd670 -size 316544 +oid sha256:1cc2ef3cb819b5a82e0af32c3ab44aff0206530e291b00bdade58da2ebe4494a +size 308246 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_NoDither.png index d94d57759f..c3ab7996db 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff094e6bafe81e818bcbac69018dcfe29366389dfca0d63d8e05ef42896ffe1d -size 317309 +oid sha256:575c8d81152642fa0eec0ea9901d1941fea58b7686cfaac1d01e0bf59f393c4b +size 308330 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png index e016e3de69..47616cd31a 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee0778aac671365dd0afae06cdcf8f36243bd9815f684b975f83e297bb694e63 -size 323979 +oid sha256:ba295a5ddb79bc61f0be9a28a636fdcc63055c26c46872d407fe20ff785f11ed +size 310415 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.25.png new file mode 100644 index 0000000000..2f939d957d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.25.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8a5da54da08f7450ffb5b49c412e654215e2c2e72c32919abc78b77dc828f5 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.5.png new file mode 100644 index 0000000000..9e8002ad19 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9a87ef109c08411ca61d91ddcf010c272303a17abd90b6ba2204eac021055e5 +size 13665 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.75.png new file mode 100644 index 0000000000..45e770bd96 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.75.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81496d88b42edf4b39ab723d0b5414b56140892f45d30fc2435904b630fa9af5 +size 13886 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.png new file mode 100644 index 0000000000..2f939d957d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8a5da54da08f7450ffb5b49c412e654215e2c2e72c32919abc78b77dc828f5 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_1.png new file mode 100644 index 0000000000..c84edd138f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_ErrorDither_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:997e5281abd8cf3a587984ec1b7e31487ec5ddf16326d025124833d536e4ac27 +size 13910 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.25.png new file mode 100644 index 0000000000..5431ffdefd --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.25.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd8b648b89f9420a0004a5f95dd54dc3769d1f78816b6708c5c6e1c14e8533a1 +size 17802 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.5.png new file mode 100644 index 0000000000..02ade5b868 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e559263bd8c293797d59166e723fdaf8b1b6ff9ae20fabac0efc86d8306123e +size 19266 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.75.png new file mode 100644 index 0000000000..cfbb9c2d5c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.75.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ae4ba533dd19271937d7e02e8ed3d243f164820b7d3bb20a4390e2deaa1d0d6 +size 19639 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.png new file mode 100644 index 0000000000..2f939d957d --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8a5da54da08f7450ffb5b49c412e654215e2c2e72c32919abc78b77dc828f5 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_1.png new file mode 100644 index 0000000000..ce546cbfe1 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_HexadecatreeQuantizer_OrderedDither_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac17dd1abc6405cb84e9d8a6404da2c2bdb220b3390d09613d3644baad6e8e99 +size 20128 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png deleted file mode 100644 index 82b965123d..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.25.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744 -size 13853 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png deleted file mode 100644 index 571b0db4b9..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.5.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c8c8393708002f06f9d8ed1ff8979db820035585c08b66ae463d94724fa64d3 -size 14330 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png deleted file mode 100644 index a1b3da6816..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.75.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fda13875f4c762a95001426487cc04c9add39821eb793168fdbe5cc18e705643 -size 14566 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png deleted file mode 100644 index 82b965123d..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744 -size 13853 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png deleted file mode 100644 index e0fc792026..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_ErrorDither_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb826afb127fe4175e6e47253b8a8313b9d10aee193c316731f34e5d327a2591 -size 14580 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png deleted file mode 100644 index 491847e491..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37018ecc499651833208d846a0f446db94cc11eae002ab6e7ce45b3e7c09e86c -size 17734 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png deleted file mode 100644 index 013bb4a3b7..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2f9ed902882f58704b22460bc64a7b27bc6f47fc2c822ee09f52345cc0d6ebf -size 19255 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png deleted file mode 100644 index 31fd7a5445..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aad3f26f2939f3679afa2b6165db29885fff40bbb1d171d5ffecc7861b5fac31 -size 19654 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png deleted file mode 100644 index 82b965123d..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bed69d43856ebd4b1af4055f8d3aacabd50c361a4e1e1f9cad080d799d6b744 -size 13853 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png deleted file mode 100644 index e2a05b9bd5..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d21029fa22dbe72cdc60b90c758cb9becd9fce03a33580d9466c1aedd323c1c -size 20000 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png index 878a36a477..f288b3c8b7 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2bd11fa19fab712b5cd6c2b36d673c7dce904b5032b860d257b00e095e4aadf -size 13432 +oid sha256:dd31b6fc59e1f9f88230d57b39362b76cedd0bd94e15904f69071ba3f465e48d +size 13656 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png index dba9232097..62b1fb0558 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7 -size 13853 +oid sha256:0e88f74acac9cfa1a47a4402aa032975ec4bf698d51e6eb1ae103480e2e10489 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png index ea062d5be6..d2d2e3e4b9 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79e48506430f3a9b25f484ef191fd820819c438392a4e588c2ecafb6db9a2210 -size 13775 +oid sha256:dd738ee2a397bb1ee305f03c70e185dea6f67827dc15b9df1966cfe8c0f28040 +size 13177 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png index ae90ea9b5f..c444923a29 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f56c884a0e4666cd662d36ec3a0d4e751c899c0122595378154507fffc69fda4 -size 14010 +oid sha256:2a2df64f89df17428415932c2ef0028d8ad408b5276264d99e6038b70473ebde +size 13417 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png index dba9232097..62b1fb0558 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7 -size 13853 +oid sha256:0e88f74acac9cfa1a47a4402aa032975ec4bf698d51e6eb1ae103480e2e10489 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png index 1e1795063b..74cbbd7581 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_ErrorDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4095927693b3cd49df58c0c1d7c5430255350c9ae595408a52ad83b1a65614ac -size 14269 +oid sha256:234854be2a3f774a58baf79f20e68c7331b6caff486ab4b1e509a96e2a3d70b9 +size 13455 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png index 29a3ed7ffd..a9bb2c3163 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d062d4b79ee01942776ae13467e9bcbb529a7eeb5ad7c28ff3d0ccd3d88dcde6 -size 15962 +oid sha256:ef65ce360293ca5659730747087c15735c15df1143204acb60120a5b68cd7cd4 +size 15905 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png index 5d1030e6b8..ceb9188005 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6679d6d6f7c8b44461956b54654cea71180a2b0d43712d3775e60cbedd90cc82 -size 17520 +oid sha256:6618f169cf4b585979f8e9261af88fe4a61c3c40b453a159cb643cc062a6a9dc +size 17517 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png index dba9232097..62b1fb0558 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:faa91657288e6a6797d8459d41d5fecca3c0a2e8e63317ebaf47df28688d13d7 -size 13853 +oid sha256:0e88f74acac9cfa1a47a4402aa032975ec4bf698d51e6eb1ae103480e2e10489 +size 13160 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png index 567e5d6a3b..f6cb173678 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5af5d16f875172d73f8426928fc8edaa4a6cab321a968b6c29fca32d0fba0df5 -size 18182 +oid sha256:4acf21f23978c83c9872bb2575ab45e4f0bbc86c8610c99479b1469fc12df5f2 +size 18112 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_ErrorDither.png new file mode 100644 index 0000000000..b17bce9e3e --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_ErrorDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:822f4ae1d8676e2231a0c938296931be42a74c0a51b4ec63fdf6dcd17d64dc7e +size 83924 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_NoDither.png new file mode 100644 index 0000000000..5e1556dc45 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_NoDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af9e6c3b9e9e90186fb66be188bad9f3f0738d558aab915b3c8dd78652010674 +size 55419 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_OrderedDither.png new file mode 100644 index 0000000000..601abdeb4c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_HexadecatreeQuantizer_OrderedDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aba024f962e5dc96e118b68c19903045f43a327c6cf45643da195382ef79a778 +size 84368 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png deleted file mode 100644 index 09c471914a..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_ErrorDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a40b319d264f046159722cb57599eda51de9ba3795272b3785901cdc51053fab -size 83010 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png deleted file mode 100644 index 3bd7cbabbb..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_NoDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3bc93509a983e20986614f4937f66d5d979bbb433a30a7736150934cf14b452a -size 55213 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png deleted file mode 100644 index 34490e602d..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b92f3320120d53444cefc79b4684933cfe2b933dc79c2414496785743b5c8f18 -size 80808 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png index 10daff76b2..fd565c0a12 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8ba00e2948337f77d935d98349958c6a520958671e9ec714ff1bfadfb130e72 -size 44622 +oid sha256:4ded8db323023a7c7620bba3b2259a549571442fe0a37883c7755ac69ae9d6d5 +size 44646 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png index 747ca70c1d..c342e3a230 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d58c425ce5b1ca56450095a66dea24b379935b0087aec7b4102f15a99f95a017 -size 101999 +oid sha256:83c8403f5d0e5457721d992c1e6980134e8a65a1f646163a4f091cf34583ca02 +size 101417 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png index de464b94cc..d07231c185 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93a4822e39babba059a88536a965e4f3207e4402d2b92d7d18485fec5e9e69da -size 84378 +oid sha256:e5412b892143bb433804c662750a64a1660b2072520db53d76ec6897c636ec50 +size 81742 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png index ce54548279..7d2070820d 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35969c8dc96de4dacc3048ae760a0681278a2011993a0edbceaacc93d6fc3a67 -size 102713 +oid sha256:a88a48586502de786aca0b36341cf6033fb3ec3ce7924ce1e2819fd14791ffe4 +size 102235 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png new file mode 100644 index 0000000000..79711e2ebc --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_ErrorDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22920fb2379dee7d12fee52f6a39b8e46e1e99f77b91f879c51bb33a981dfdcb +size 98851 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png new file mode 100644 index 0000000000..fa6d4cb432 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_NoDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c7137e1b87d317d7e139cde8499deafa89f27bddba146cc5736f9c0566778c5 +size 81609 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png new file mode 100644 index 0000000000..d253a321d5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_HexadecatreeQuantizer_OrderedDither.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fa41128fd3f7a6b4b74b758186494ce7ed780561b9825277fae0c345116b1d7 +size 95067 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png deleted file mode 100644 index 5efcaedc94..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_ErrorDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:40d012f4ecb4e36c94d086f8ec7bc199fbfd9fb30a9427a07b35df1b1e430a71 -size 95601 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png deleted file mode 100644 index 916dc37566..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_NoDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fa64863f73dfd1c5daef645c54e9275136f66513a87750bee0ec8e13ac357da5 -size 79649 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png deleted file mode 100644 index f039dd222e..0000000000 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8f5138589c606de20ba193d4279f049ee1ecb3f1801b949d3436995bbf242cbe -size 92683 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png index 2b897a5d6d..a14c2cb1f6 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:007ac609ec61b39c7bdd04bc87a698f5cdc76eadd834c1457f41eb9c135c3f7b +oid sha256:7fb3743098a8147fd24294d933d93a61ec0155d754f52544650f6589719905be size 60688 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png index ac56fa9236..03848e81ce 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d2289ed4fa0c679f0f120d260fec8ab40b1599043cc0a1fbebc6b67e238ff87 -size 51428 +oid sha256:ef033a419e2e1b06b57a66175bad9068f71ae4c862a66c5734f65cdaae8a27f0 +size 51461 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png index 8b79a19e05..0d548ca79d 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef2b6073b75a2de97a78d47d3b3e40c264687c5756f153d3d85bc5b2714cf85a -size 68226 +oid sha256:e2f4f4e2237925403fd0228344f9fce9be96c0f26e3465775763aca775779763 +size 68222 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png index 88cf83a306..0fe3d30bfb 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:513844ed95c2b50e792d3346398256846b8b280dbadf7ef3f4e11d58c1e679c0 -size 69529 +oid sha256:aee197677c3276d4abb8fc027358b38be26462374e364841781626f0aa67e1a4 +size 69769 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png index a3eefcba20..ef86e7c48d 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_ErrorDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32b269d62d4eebe555d5d9f12b9958b41206848504bb985dcd1ff9c81a5003c6 -size 117073 +oid sha256:0b3c8dc7e653ef1846c7359e9a0f719bee91549846f160abb547cd0aab6a8a59 +size 110711 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png index 3b0c46ac38..c65381c052 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_NoDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12f58b00a16913cd85ffa18fcea580a59550dcc201295b060d55a870230f37f7 -size 113995 +oid sha256:4b95721a963def9e82dd32e277ed4594213920d7808ad26696d01e4f8fda842e +size 103855 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png index 328f863307..eb2c2a0c98 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:867d7b727de278cbc01b7d2b8e968f1fc0d0a81a3e4af636ce4a6598a8709be6 -size 114630 +oid sha256:cb174c104cdcf35433c98522a1d9d52ccf42e8927e0b59fec3556aeee8b15a47 +size 110505 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 e12985f7ae..0000000000 --- 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 726721824e..0000000000 --- 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 diff --git a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png index f3b7b0e809..8f63e3277e 100644 --- a/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png +++ b/tests/Images/External/ReferenceOutput/TgaDecoderTests/TgaDecoder_Decode_Resize_rgb_a_rle_UL_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb67006d156cff0fa8d4d1b131ac0eaa98279ae6dcc477818b2fc65f2dfb77aa -size 23396 +oid sha256:d0ce3abf8024859d1375721c18049513106ade2f9529ec20864621e310ca0b1b +size 18786 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CIELAB.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CIELAB.png new file mode 100644 index 0000000000..3863e7202b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CIELAB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dd7d54f362b33c2d2d4b4b3cb3bbece23c14138073403234db7b53012939101 +size 383 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CMYK.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CMYK.png new file mode 100644 index 0000000000..bc97709c19 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_CMYK.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:370af73b800622a671c0718b2c137ead8401adf20c39b45f153d2c9bb09b40ed +size 385 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB16.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB16.png new file mode 100644 index 0000000000..3fcea773ac --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de9cfa53a82b169c533999908c7ace43aa35a8b456d5f0378b539669c7857d1c +size 386 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB8.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB8.png new file mode 100644 index 0000000000..3fcea773ac --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual_RGB8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de9cfa53a82b169c533999908c7ace43aa35a8b456d5f0378b539669c7857d1c +size 386 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_Issue3031.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_Issue3031.png new file mode 100644 index 0000000000..d5a017475f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_Issue3031.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c33a2f63836975bdc7631f26634b3fb0ae98bfaff730300877339cb568141cde +size 124 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_lsb.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_lsb.png new file mode 100644 index 0000000000..a3317a6b48 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_lsb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e50dfa103459d21642df5e1ca760081fbdfe3b7244624d9d87d8a20f45b51bbe +size 117953 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_msb.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_msb.png new file mode 100644 index 0000000000..a3317a6b48 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_64Bit_WithAssociatedAlpha_Rgba64_RgbaAssociatedAlpha16bit_msb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e50dfa103459d21642df5e1ca760081fbdfe3b7244624d9d87d8a20f45b51bbe +size 117953 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_BigEndian_AssociatedAlpha_Rgba16161616_Rgba64_Issue3031.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_BigEndian_AssociatedAlpha_Rgba16161616_Rgba64_Issue3031.png new file mode 100644 index 0000000000..d5a017475f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_BigEndian_AssociatedAlpha_Rgba16161616_Rgba64_Issue3031.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c33a2f63836975bdc7631f26634b3fb0ae98bfaff730300877339cb568141cde +size 124 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_Cmyk_Rgba32_Cmyk-jpeg.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_Cmyk_Rgba32_Cmyk-jpeg.png new file mode 100644 index 0000000000..6f35ecb553 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_Cmyk_Rgba32_Cmyk-jpeg.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1ff0ce13bf0521b8a6f0d77806473c837c45428391786f751186a53fd951c0c +size 394 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 0000000000..aded240a1e --- /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:a94178ba1f0bd2edecdfd14c93196d1e195912f98875d875e1d79c8632676d03 +size 46525 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_A.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_A.png new file mode 100644 index 0000000000..addaa27bfe --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_A.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13f51f69b061303d06b73fa7e626d7252a9fd14aa84c961ea894f6e7cd7218cc +size 7603818 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_B.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_B.png new file mode 100644 index 0000000000..445cad99c8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_B.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6a35853e44d06a6f74e3aaf558e6a4b9c3209c6c3fa2c8f3ee650bf049ac0d3 +size 7624359 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_A.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_A.png new file mode 100644 index 0000000000..e88a98fa48 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_A.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e91f6ae534b95a9e052158043e929e1da22e0851b4d6dfdb8b802605396758f +size 7888373 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_B.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_B.png new file mode 100644 index 0000000000..29134afcaa --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_B.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81bb1f05a6f72aebee41941b2390ca0a35c4368758af2d27b3e81bbee8901c56 +size 7956102 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png index 412af0ac7a..2f546df5b5 100644 --- a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_Decode_Resize_RgbaUnassociatedAlpha3bit_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53b9dabffaae6a9250bf16f201ef8c9e933327874e8be91785c4fb6cd7e787a8 -size 10767 +oid sha256:10b471070b26a6aedfc0fce762dd71090409494ce0728790278d8584636672a2 +size 9940 diff --git a/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png b/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png index 331b8b30a6..1a052e4449 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png +++ b/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a68aa183691be3240e881057cb2f5785e50228c9c5dd98163ba766b5cafa8b55 -size 88601 +oid sha256:1f312445263030ea79e2d1a05c62d19ade816c9cda9f0d3494ab76fde1e03ace +size 92534 diff --git a/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png b/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png index c375fff78b..769e172b7d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png +++ b/tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53a7bc1aa7279cce0b73568b6ed9b141377f7654b73dcf926c43c22dd908039a -size 88502 +oid sha256:672f167d514f01c625145701ea0d45ced503a6e84cae75fbdb67e6b0db179aee +size 92318 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png index 65bb77977b..c8b2ee0ce8 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3594547265b23603b1a76ff6bc6f0eab4af55d6e0070e53356123dfc7ae256f8 -size 9034 +oid sha256:881daeef5b8db99af8b89e7d4e968fb4c43e13c904e936aefa1d0156b767803e +size 9051 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png index 7c54b1b074..ccd3d8fa5b 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ae9ef073f3338b71d2a40fcf2e89d9b6ab62204d6de9b6a1f75f4705ee197f0 -size 10704 +oid sha256:86032d5b7e49574655c1cd54886ac57d6385714481ba6bd72176d858f884cd1a +size 10720 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png index b6e930224e..30a81a5b98 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:994dda7da034595aa77d107652bea06c86077d24ef8a6883b18f1f509bb19928 -size 8906 +oid sha256:cb823498eacf5bab12f7607d230523a3488e26fcc10fe1b3ab51de900a9dff97 +size 8835 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png index d1ea99cf90..69f862a5a9 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5 -size 10470 +oid sha256:afe7ddbff155b918a4eff91af31e01100355c146cb9c8a12ab2496da8b22821d +size 10446 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png index 2f3f0f17fe..b2f18f8553 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29c5f48f1ece0b12854b4c44fba84fdfc9ac5751cdf564a32478dcdaed43b2a4 -size 9798 +oid sha256:be885eca2d9771be335a29d3da533a17376efa4c563996ac4cb37138fd1899eb +size 9826 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png index 5242a9d985..39c4304bed 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7de58474c3f386c4ec31a9088d561a513f82c08d1157132d735169b847b9680 -size 11579 +oid sha256:afea3d7ec03c945d695b3cd85e9ea1d79cb35745256efe1e32273eb08789e5ce +size 11476 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png index 2af9d2fc27..e98d3115fa 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ef9b7051d7a5733dfe2534fddefdc28dfbc49d087355f46c4d945b04f0e3936 -size 9672 +oid sha256:096eba716663179933fe6eb526822ac9dedf83c3633d43df328ed33fb16900f6 +size 9687 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png index 83c02764fa..380b4b7d58 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:825770c9b2e9f265d834eab6b40604df5508bf9bc5b4f82f5d3effd6d5a26935 -size 11434 +oid sha256:c7367063704e10827bb81f46ebb56b6fe87a816eb8ec258ca95d8e26d9276f0f +size 11408 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png index d6dba3f889..e8822425d5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e283463b0f450dd72cf303acccf3dd1ff7a31fe401ff0f288d67c4baefca240 -size 8742 +oid sha256:37acdbbcebe56ab98e17b025312e5860c471d807603c2ce6f4a50fd5f219c0f7 +size 8732 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png index 76bb244d52..0a798ce06a 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:485d9d9ef955a04af43d17e6bc3952e9bf65a9752b6cf8ba9cbbe8f772f05a18 -size 8995 +oid sha256:722d191e930331e296a548641a80ac163430bdb4017e3d68e4638fbb1d6ed45c +size 9021 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png index c1c1d814fd..8d4d2fd9bb 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d749ac365764051ea16bc39d1aff84c06faf282359805b58bb97c9eed7f0bb -size 6400 +oid sha256:ce2bbe927b718a1b4de05b2baad7016b69490d1b5dfb085420192b7ac6c0ec5d +size 6367 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png index 27608881ed..2d5fd9e9fc 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d82f2a15502b0a29aa4df1077ec90c88f9211f283fdc0edd7b059ed9b387441 -size 6334 +oid sha256:d79a7044e95a1ca032124366e4705bd93a866609547ebb489ff7d2228547cea5 +size 6330 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png index 340455428a..a9de3011d3 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e8afa56c5abb0e4b5895f35415db1178d041120d9f8306902f554cfaaada88d -size 26540 +oid sha256:677e4419a1cac8692da2d852f6e169c4a66ef8f164fffa65550fccb71bb54563 +size 26606 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png index 9ef7866924..2ae230ac52 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 -size 24827 +oid sha256:0f700e854d2d4ee9c12150ef300e0041fa4706ae595eeea6869894a1e2947eaf +size 24963 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png index 14f7748537..63f5f99595 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b56ceae2f350a1402beecc5b5e2930be1011a95fbf224cccf73b96f3931b646 -size 26531 +oid sha256:965248b773ea4b3f8e870ff598ccf88784dd700d9aa063b70b0146a00fc806bc +size 26613 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png index c8204eacf3..bf1e063323 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:049ee7fc2bb758609a64149c338bfae2eab44755f53e6b7c25a5e8b8725ed8ac -size 24416 +oid sha256:cd8e29141de12f1c1a2b3fb057a58b5a017b6db3498023a8f77dc8ac02d7845c +size 24396 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png index 2bc57092a2..c903716e7d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72c487a2fa3d608021b26a4d6b4517f8548fdcfc62fbafdd8649015dbec8ff87 -size 26504 +oid sha256:a28ac3e9bd2edc2aa7163275006f701672cd7f0b92454a0453a6e73387c70138 +size 26611 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png index fee364e217..5749ec1ac5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:099733c9d4490c86cfbb10a016e2dd073539a95f9d5ff9018cf9b5be5404fa13 -size 33435 +oid sha256:2a57780c4145cf1eef6028d7e58ef94ab6d47f80a024f43fe98297f2d398a7fc +size 33409 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png index 30325ccc6f..5f98b41849 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27f2a2b21f8ae878e15120ea5a4a983bde7984b3468dc8426055885efc278fe6 -size 35547 +oid sha256:129f1770502c71fcbe67da35a6966a4c72e4d17ee70a8e951a5229db99d03089 +size 35607 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png index ff81256a70..979b0366a9 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b5cbe60e26e123e5a5cdf5a4e88305340f76d32a9c64a220c1fa7512f84e786 -size 39442 +oid sha256:0b7a58c5219ce2b6c91f98b7a75a9c89292e69d816451425b9b829d6a2b1711c +size 39420 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png index 263dd7426d..6cfbcee615 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:102cceb79acb1dfd7ec8887b4906e33456c774d48320d1624b3c73975d26f145 -size 25981 +oid sha256:b80944705b204ab37b1a1a0b7d0705fba4f4eae4ea9453ae8ed0e39104765ee9 +size 25836 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png index 9ef7866924..2ae230ac52 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 -size 24827 +oid sha256:0f700e854d2d4ee9c12150ef300e0041fa4706ae595eeea6869894a1e2947eaf +size 24963 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png index 85bbd5ec38..8aeed9c8db 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e61629aeefac7e0a1a6b46b44ad86ed4a5ba0908bb3febc18bb5f9f3ded1c08d -size 25751 +oid sha256:314e15b8d690b17aa67ee856bcaf55afc62ce86c1b1d86b0f17dffe8739a2d17 +size 25739 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png index f200a5f955..e7e2c554bc 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45b1b48e1df393f4c435189c71a6bd3bccfe7a055d76d414a8d0c009b59fa0a0 -size 26145 +oid sha256:abf063ddd974a8eef6b24da9e58a10143d9fefa29369b11f7b27685312a0c74b +size 26084 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png index 434bb32a81..0d11b5565a 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6d186f9e547f658b719bc033e3b110d64cf2a02caecc510d4e2f88359e69746 -size 24176 +oid sha256:f417e1771b2a367f857888ae90bd75f9922d9e5fc8826009ded592d9da010a44 +size 24155 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png index e3be1ffe5a..0f305ddd3c 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:339b3299984f1450f1a8200e487964c0338b511b82e459d67a3583d0bd46b805 -size 24013 +oid sha256:6aebe33dad1ea3a709f54f234cb779956f5b561a9a5ecf23a95e7ec42d91c535 +size 23905 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png index 7dbeeaf357..835d729076 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5335c6184829fdc405475bd34d2fae60cf6d5ae050b4d671ac5dd25242ff1368 -size 31888 +oid sha256:bb7684478edd471057076a408c6b32102838db466476929ee58e248f5c20a2b2 +size 31872 diff --git a/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png b/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png new file mode 100644 index 0000000000..4a01423ee7 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual-cLUT-only.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a2cbd483eed31cf7b410ef1ee45ae78e77b36e5b9c31f87764a1dfdb9c4e5c8 +size 79768 diff --git a/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png b/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png new file mode 100644 index 0000000000..c46b369eff --- /dev/null +++ b/tests/Images/External/ReferenceOutput/WebpDecoderTests/Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile_Rgba32_Perceptual.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:547f3ddb5bdb3a3cb5c87440b65728be295c2b5f3c10a1b0b44299bb3d80e8d7 +size 79651 diff --git a/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png index 35b99df985..4592d77da1 100644 --- a/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png +++ b/tests/Images/External/ReferenceOutput/WebpDecoderTests/WebpDecoder_Decode_Resize_bike_lossless_150_150.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d73ae8bfad75c8b169fb050653eb4f8351edf173d1cc121ff1e23e3f1e594a97 -size 36342 +oid sha256:c372b6317bc4806ffdbddee45ded7b3a8c9f8ee7b7a462d8ff770b60ce00e176 +size 28629 diff --git a/tests/Images/Input/Ani/Help.ani b/tests/Images/Input/Ani/Help.ani new file mode 100644 index 0000000000..d623503cac --- /dev/null +++ b/tests/Images/Input/Ani/Help.ani @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c49cbb1ca0a3f268695a80df93b1ce2b2cba335a80e8244dd3a702863159bd99 +size 12998 diff --git a/tests/Images/Input/Ani/Work.ani b/tests/Images/Input/Ani/Work.ani new file mode 100644 index 0000000000..d576244bbd --- /dev/null +++ b/tests/Images/Input/Ani/Work.ani @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:740353739d3763addddd383614d125918781b8879f7c1ad3c770162a3e143a33 +size 1150338 diff --git a/tests/Images/Input/Ani/aero_busy.ani b/tests/Images/Input/Ani/aero_busy.ani new file mode 100644 index 0000000000..e2fa9d31a5 --- /dev/null +++ b/tests/Images/Input/Ani/aero_busy.ani @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff38afb523490e1a9f157c0447bc616b19c22df88bdb45c163243d834e9745f8 +size 556304 diff --git a/tests/Images/Input/Exr/Calliphora_b44.exr b/tests/Images/Input/Exr/Calliphora_b44.exr new file mode 100644 index 0000000000..ea008e0701 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_b44.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a6ac7fd879fc2dae646adaa14382aa10e5b8d02634af4668dd04bce09148151 +size 157973 diff --git a/tests/Images/Input/Exr/Calliphora_benchmark.exr b/tests/Images/Input/Exr/Calliphora_benchmark.exr new file mode 100644 index 0000000000..da416b3c82 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_benchmark.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3368860692927e709365f2d37b92411068e77a0f23624ff57af6089ec69f357 +size 2592888 diff --git a/tests/Images/Input/Exr/Calliphora_float_pxr24.exr b/tests/Images/Input/Exr/Calliphora_float_pxr24.exr new file mode 100644 index 0000000000..3c5d61e535 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_float_pxr24.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca6c29eb36395b923298d2bf4ba1f73fd7f081f7b1af2e72b94b92ad2acb638b +size 226997 diff --git a/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr b/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr new file mode 100644 index 0000000000..04a5c035dd --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:926996c647c1c54921db0a623e58e5edeb92b447cb4eceb53b8c0e86fd24f11e +size 720281 diff --git a/tests/Images/Input/Exr/Calliphora_gray.exr b/tests/Images/Input/Exr/Calliphora_gray.exr new file mode 100644 index 0000000000..2aaeafbbe8 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_gray.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a5daadcfd4ff0e45282d39d6c54f9a13651da3fd8841abda580e76661555470 +size 124245 diff --git a/tests/Images/Input/Exr/Calliphora_half_pxr24.exr b/tests/Images/Input/Exr/Calliphora_half_pxr24.exr new file mode 100644 index 0000000000..17a90699b0 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_half_pxr24.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:927778a73832b5ad68473e46df6be52076d98294271f2cd0ead66691db41b7bf +size 195063 diff --git a/tests/Images/Input/Exr/Calliphora_rgb.exr b/tests/Images/Input/Exr/Calliphora_rgb.exr new file mode 100644 index 0000000000..5dc90343db --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_rgb.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f54e5e57df1b8cdf1b26418d5696dd9cefbd7ed3bf31cdcde06fd9a7bf5e3724 +size 362681 diff --git a/tests/Images/Input/Exr/Calliphora_rle.exr b/tests/Images/Input/Exr/Calliphora_rle.exr new file mode 100644 index 0000000000..a956c9c400 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_rle.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccd25a952240a75d9c8b646be5001a2b006c053314e30cdf12e35ac865a31ae2 +size 292545 diff --git a/tests/Images/Input/Exr/Calliphora_uint32_uncompressed.exr b/tests/Images/Input/Exr/Calliphora_uint32_uncompressed.exr new file mode 100644 index 0000000000..867cffaa11 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_uint32_uncompressed.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bed68d2e491cad0bdd0fa668ce57328951927d722f130ba7c5c5475f6a3e0d96 +size 289183 diff --git a/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr b/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr new file mode 100644 index 0000000000..fd858abb04 --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc72685224ba818ac77d914f5e4c91162503f88f775e16b5c745baef6bc7b790 +size 187914 diff --git a/tests/Images/Input/Exr/Calliphora_uncompressed.exr b/tests/Images/Input/Exr/Calliphora_uncompressed.exr new file mode 100644 index 0000000000..5dc90343db --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_uncompressed.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f54e5e57df1b8cdf1b26418d5696dd9cefbd7ed3bf31cdcde06fd9a7bf5e3724 +size 362681 diff --git a/tests/Images/Input/Exr/Calliphora_uncompressed_rgba.exr b/tests/Images/Input/Exr/Calliphora_uncompressed_rgba.exr new file mode 100644 index 0000000000..86bc1d354a --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_uncompressed_rgba.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e9f6b5afa3c10c895ba67b51568295be40c2b1057224437600028487c581291 +size 481899 diff --git a/tests/Images/Input/Exr/Calliphora_zip.exr b/tests/Images/Input/Exr/Calliphora_zip.exr new file mode 100644 index 0000000000..82dc0dc39f --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_zip.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8b8e3b9a74179cb51a7a85031b3d177e109c20a56392804b10873273ff2163e +size 181871 diff --git a/tests/Images/Input/Exr/Calliphora_zips.exr b/tests/Images/Input/Exr/Calliphora_zips.exr new file mode 100644 index 0000000000..6556adf00f --- /dev/null +++ b/tests/Images/Input/Exr/Calliphora_zips.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c6962a5b06c677648d5e22ff30ba8a28e7801fffb32259984e23bbaa97061b1 +size 220159 diff --git a/tests/Images/Input/Exr/rgba_uint_uncompressed.exr b/tests/Images/Input/Exr/rgba_uint_uncompressed.exr new file mode 100644 index 0000000000..2738783b29 --- /dev/null +++ b/tests/Images/Input/Exr/rgba_uint_uncompressed.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b45345cb1e4539e5298ccce3ccfc0f27b7a469836bfd7aa13c4d9f6c52baa7a +size 482731 diff --git a/tests/Images/Input/Gif/animated_loop.gif b/tests/Images/Input/Gif/animated_loop.gif new file mode 100644 index 0000000000..5fad702a10 --- /dev/null +++ b/tests/Images/Input/Gif/animated_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8750149c953e9e910472684158c07a2cb551c1f7e95744ab48db1a67f63f342 +size 873 diff --git a/tests/Images/Input/Gif/animated_loop_interlaced.gif b/tests/Images/Input/Gif/animated_loop_interlaced.gif new file mode 100644 index 0000000000..9577a84658 --- /dev/null +++ b/tests/Images/Input/Gif/animated_loop_interlaced.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2bc2f895f03092b1c26381a32b5dd5838aacd3331e07f7e4dae55d5cbb4e149 +size 878 diff --git a/tests/Images/Input/Gif/animated_transparent_firstframerestoreprev_loop.gif b/tests/Images/Input/Gif/animated_transparent_firstframerestoreprev_loop.gif new file mode 100644 index 0000000000..5012324caf --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_firstframerestoreprev_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b0cf18d386dc979fcf853d6a9adac673a2709a8751d31a94930199dededa25f +size 536 diff --git a/tests/Images/Input/Gif/animated_transparent_frame_norestore_loop.gif b/tests/Images/Input/Gif/animated_transparent_frame_norestore_loop.gif new file mode 100644 index 0000000000..712f334aba --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_frame_norestore_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad7359801fa6ed89fb041de1e88faea856b1028d9f477fdc4eda774df6e5f1ce +size 685 diff --git a/tests/Images/Input/Gif/animated_transparent_frame_restorebackground_loop.gif b/tests/Images/Input/Gif/animated_transparent_frame_restorebackground_loop.gif new file mode 100644 index 0000000000..b6f675dcaf --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_frame_restorebackground_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12254f22eeee9eac6babbfbfb34b6ae9302342454fd6677e8c7c9937656cc127 +size 685 diff --git a/tests/Images/Input/Gif/animated_transparent_frame_restoreprev_loop.gif b/tests/Images/Input/Gif/animated_transparent_frame_restoreprev_loop.gif new file mode 100644 index 0000000000..be7fdf85d8 --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_frame_restoreprev_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cdfba6efea653bb94ede6edd0577ba6af1f7c130307b94903dd94f0f8bbc4f9 +size 685 diff --git a/tests/Images/Input/Gif/animated_transparent_loop.gif b/tests/Images/Input/Gif/animated_transparent_loop.gif new file mode 100644 index 0000000000..cb001ece8f --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3297987894ba27c2acc6a5c447c3d3a52cc169447b451409535decccc1743e55 +size 536 diff --git a/tests/Images/Input/Gif/animated_transparent_restoreprev_loop.gif b/tests/Images/Input/Gif/animated_transparent_restoreprev_loop.gif new file mode 100644 index 0000000000..f51d02433e --- /dev/null +++ b/tests/Images/Input/Gif/animated_transparent_restoreprev_loop.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9418561ef2f2307456bb068ecc1a9d5aa02da5e314e7aadd722985e27503926b +size 536 diff --git a/tests/Images/Input/Gif/issues/issue_2859_A.gif b/tests/Images/Input/Gif/issues/issue_2859_A.gif new file mode 100644 index 0000000000..f19a047525 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2859_A.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50a1a4afc62a3a36ff83596f1eb55d91cdd184c64e0d1339bbea17205c23eee1 +size 3406142 diff --git a/tests/Images/Input/Gif/issues/issue_2859_B.gif b/tests/Images/Input/Gif/issues/issue_2859_B.gif new file mode 100644 index 0000000000..109b0f8797 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2859_B.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db9b2992be772a4f0ac495e994a17c7c50fb6de9794cfb9afc4a3dc26ffdfae0 +size 4543 diff --git a/tests/Images/Input/Gif/issues/issue_2866.gif b/tests/Images/Input/Gif/issues/issue_2866.gif new file mode 100644 index 0000000000..0ead86bf89 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2866.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b2a9e3728c41e1b45d6f865e4692eadbed28dcaec65806e6bda22a9a16f930f +size 7526725 diff --git a/tests/Images/Input/Gif/issues/issue_2953.gif b/tests/Images/Input/Gif/issues/issue_2953.gif new file mode 100644 index 0000000000..98c06e5c58 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2953.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fa4a002b264a41677cc10f115f3572111852993a53ee8cea5f4ec0bf8dec195 +size 40 diff --git a/tests/Images/Input/Gif/issues/issue_2980.gif b/tests/Images/Input/Gif/issues/issue_2980.gif new file mode 100644 index 0000000000..9c41a0f0b2 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2980.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3737a411dde845e20fc151e610434b96fb0a47297341db6f435e1ea64ae789b +size 507 diff --git a/tests/Images/Input/Gif/issues/issue_3142.gif b/tests/Images/Input/Gif/issues/issue_3142.gif new file mode 100644 index 0000000000..6053eebcf5 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_3142.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:982c394dbee297bb3bc5cc532827742260acb6e5aa07e1dca3f0b17e57bc3bbe +size 386565 diff --git a/tests/Images/Input/Gif/static_nontransparent.gif b/tests/Images/Input/Gif/static_nontransparent.gif new file mode 100644 index 0000000000..17ab1e2ec7 --- /dev/null +++ b/tests/Images/Input/Gif/static_nontransparent.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0a5e1b2f0c5c1763eb950a1d92c5317f048875e04e88dca7f1a966552c2774c +size 678 diff --git a/tests/Images/Input/Gif/static_transparent.gif b/tests/Images/Input/Gif/static_transparent.gif new file mode 100644 index 0000000000..89039a732a --- /dev/null +++ b/tests/Images/Input/Gif/static_transparent.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f73f56bbe2206bd1cd8a4625b6a4d61506214b37b61ff3e8194e2030b28abca5 +size 341 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 0000000000..39fc9c521c --- /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 0000000000..6179678bce --- /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 0000000000..90138a08d5 --- /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 0000000000..1161a3f3cb --- /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 0000000000..d6524a31f2 --- /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 0000000000..73394156aa --- /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 0000000000..8dffe659f5 --- /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 0000000000..e281eb3785 --- /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 0000000000..c5e4677d3a --- /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 0000000000..89872b9595 --- /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 0000000000..1e47b45963 --- /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 0000000000..5152c75755 --- /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 0000000000..a1d5c09c04 --- /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 0000000000..9c5a227e30 --- /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 0000000000..c019914ee0 --- /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 0000000000..2f3fd28eb0 --- /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 0000000000..1c678ec40e --- /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 0000000000..6365a53df8 --- /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 0000000000..f8697e2b5b --- /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 0000000000..e6de107d78 --- /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 0000000000..2c37ffa8b0 --- /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 0000000000..f9137f61ae --- /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 0000000000..08f928c8e4 --- /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 0000000000..d6d472a255 --- /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 0000000000..5c585c582b --- /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 0000000000..3663b87673 --- /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 0000000000..4834b48c86 --- /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 0000000000..f2c11ccfec --- /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 0000000000..2d7880a03b --- /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 0000000000..a98c85c197 --- /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 0000000000..5dd3c57c2c --- /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 0000000000..d9622629e7 --- /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 0000000000..39be58ce45 --- /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 0000000000..9e7873eaf1 --- /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 0000000000..a64157a63b --- /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 0000000000..5abf2ad66a --- /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 0000000000..d1a37498b6 --- /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 0000000000..a7f94e94dc --- /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 0000000000..609a51826b --- /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 0000000000..13a71140f6 --- /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 0000000000..3f449eefea --- /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 0000000000..2229aee95e --- /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 0000000000..cbb64292b0 --- /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 0000000000..837e8f512b --- /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 0000000000..b359d4d844 --- /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 0000000000..01df9ae7dc --- /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 0000000000..8879d3f1f2 --- /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 0000000000..d800b21983 --- /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 0000000000..710c38a23b --- /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 0000000000..4223c1fc29 --- /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 0000000000..1e321acb6a --- /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 0000000000..b44fe22d5c --- /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 0000000000..682b148ed9 --- /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 0000000000..5925362903 --- /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 0000000000..3a0bb3dd00 --- /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 0000000000..334a0b75cd --- /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 0000000000..ce67e54cc1 --- /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 0000000000..f26d88b36d --- /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 0000000000..aa44dd4f16 --- /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 0000000000..7049b0b367 --- /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 0000000000..fa07400658 --- /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 0000000000..2b8d74afad --- /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 0000000000..86b71f36ca --- /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 0000000000..aa8fc09320 --- /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 0000000000..ad93685f23 --- /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 0000000000..781266a670 --- /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 0000000000..ffe599149e --- /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 0000000000..70f2db036d --- /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 0000000000..230d8e85fa --- /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 0000000000..7c4b9834bc --- /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 0000000000..b1f3050bff --- /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 0000000000..fc7751086c --- /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 0000000000..6b2babe8ed --- /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 0000000000..4394b9e432 --- /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 0000000000..086edb7454 --- /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 0000000000..e09d744472 --- /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 0000000000..9a5684b4d0 --- /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 0000000000..20961847a5 --- /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 0000000000..59b0bb63b3 --- /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 0000000000..bcbdf9c074 --- /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 0000000000..7c320d1839 --- /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 0000000000..af7581f35b --- /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 0000000000..3c8043fe67 --- /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 0000000000..075791998f --- /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 0000000000..af95b287d7 --- /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 0000000000..2e4f804951 --- /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 0000000000..65ca70be25 --- /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 0000000000..e02e8fb372 --- /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 0000000000..39be58ce45 --- /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 0000000000..8819490aec --- /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 0000000000..6b6bbdc9fd --- /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 0000000000..73cd34c733 --- /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/aero_arrow.cur b/tests/Images/Input/Icon/aero_arrow.cur new file mode 100644 index 0000000000..82cbbd33e6 --- /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/cur_fake.ico b/tests/Images/Input/Icon/cur_fake.ico new file mode 100644 index 0000000000..cad7542c85 --- /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 0000000000..cad7542c85 --- /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/flutter.ico b/tests/Images/Input/Icon/flutter.ico new file mode 100644 index 0000000000..4001f14268 --- /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 diff --git a/tests/Images/Input/Icon/ico_fake.cur b/tests/Images/Input/Icon/ico_fake.cur new file mode 100644 index 0000000000..5ab0405380 --- /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 0000000000..5d2e25fd3b --- /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 0000000000..2ebefbf330 --- /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 0000000000..ca34a25788 --- /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 0000000000..913f780ed3 --- /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 0000000000..7e697d3d28 --- /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 0000000000..cbd394fc6a --- /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 0000000000..f35027255c --- /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 0000000000..3efdcab740 --- /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 0000000000..65b504eef2 --- /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 0000000000..c34fdc638c --- /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 0000000000..2065bd638e --- /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 0000000000..c6ee58297b --- /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 0000000000..3d9fc96fbc --- /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 0000000000..8f2991acbe --- /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 0000000000..99948cf1e1 --- /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 0000000000..12b2bf66cf --- /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 0000000000..599168aea2 --- /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 0000000000..701b574dcc --- /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 0000000000..271ec92a5d --- /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 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg new file mode 100644 index 0000000000..077ee22beb --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb8bdcc137efa3e28db69e48612230b3a9fec17267de9ce29757d9bacc181d28 +size 42001 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg new file mode 100644 index 0000000000..188faa2bdd --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7129f5485e997b75cff143021522cc8ab94e2c3c1912689bc765ce2b3b937441 +size 72150 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg new file mode 100644 index 0000000000..befc3d1170 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe7fa60a53893836200c62f34492c7a0c931692dd073dffa4afc49fe3826e433 +size 44446 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg new file mode 100644 index 0000000000..645ad2869a --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb686b44e3253143a32db890823f63c79026c9ac9badc4ad9de21f6cb2fa2f2a +size 40703 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg new file mode 100644 index 0000000000..57727aaa29 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:928b854a9629d1532d37095c4744da6bc2fc986f878a76aea373f69490f4b586 +size 40505 diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg new file mode 100644 index 0000000000..4b7b612be0 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:faf67048c2b7bd3fb5fa9b69bd53943d63a216ef371c5dc9d062ac443c9d2d34 +size 47434 diff --git a/tests/Images/Input/Jpg/icc-profiles/Perceptual-cLUT-only.jpg b/tests/Images/Input/Jpg/icc-profiles/Perceptual-cLUT-only.jpg new file mode 100644 index 0000000000..7b2e57f659 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Perceptual-cLUT-only.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04e552f0bd68bddb40f35c456034b1bf1e590f37e990a28b2fe2e94753bbe685 +size 276191 diff --git a/tests/Images/Input/Jpg/icc-profiles/Perceptual.jpg b/tests/Images/Input/Jpg/icc-profiles/Perceptual.jpg new file mode 100644 index 0000000000..879fd05ad3 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/Perceptual.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74a0931e320ca938d7dc94c4ab7b27a15880732fc139718629a7234f34bdafba +size 297456 diff --git a/tests/Images/Input/Jpg/icc-profiles/issue-129.jpg b/tests/Images/Input/Jpg/icc-profiles/issue-129.jpg new file mode 100644 index 0000000000..98949f43f1 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/issue-129.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1728dd548d862ef3f960c82528716e0ad1b8eb0119a5eed4dfde51026d7bb74 +size 2903429 diff --git a/tests/Images/Input/Jpg/icc-profiles/issue-3064.jpg b/tests/Images/Input/Jpg/icc-profiles/issue-3064.jpg new file mode 100644 index 0000000000..477372c033 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/issue-3064.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e02fff450519423fd5746e610d65bd7296553252567e93de9c051250139e8adc +size 27537 diff --git a/tests/Images/Input/Jpg/icc-profiles/issue_2723.jpg b/tests/Images/Input/Jpg/icc-profiles/issue_2723.jpg new file mode 100644 index 0000000000..4a342e07ed --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/issue_2723.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56dc6400d8f4c42c505ec562e4ade0b23e613b2c16e1666eccbbaa8e997efcc7 +size 766523 diff --git a/tests/Images/Input/Jpg/icc-profiles/sRGB_Gray.jpg b/tests/Images/Input/Jpg/icc-profiles/sRGB_Gray.jpg new file mode 100644 index 0000000000..2abd976863 --- /dev/null +++ b/tests/Images/Input/Jpg/icc-profiles/sRGB_Gray.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22892d1b7965d973c7d8925ad7d749988c6a36b333b264a55d389f1e4faa0245 +size 36854 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 0000000000..18dc6f2e32 --- /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 diff --git a/tests/Images/Input/Jpg/issues/issue-2857-subsub-ifds.jpg b/tests/Images/Input/Jpg/issues/issue-2857-subsub-ifds.jpg new file mode 100644 index 0000000000..5e5288f22e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2857-subsub-ifds.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab45137ded01a42658aa94421165a358b184a536b6ab64427d8255e8d78c25b9 +size 2829977 diff --git a/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg new file mode 100644 index 0000000000..d210e87e52 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad9c3a6babb41c5aa2a46fbfe74ed5482028c73f48531cf144e1e324ca7988b3 +size 103789 diff --git a/tests/Images/Input/Jpg/issues/issue3118-multiple-sof.jpg b/tests/Images/Input/Jpg/issues/issue3118-multiple-sof.jpg new file mode 100644 index 0000000000..e8897a511b --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue3118-multiple-sof.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29d9073bc60cb8f5965993654ec5a949cdbacebe6365db9831ece860095ca85f +size 11541050 diff --git a/tests/Images/Input/Pbm/rgb_binary_wide.ppm b/tests/Images/Input/Pbm/rgb_binary_wide.ppm new file mode 100644 index 0000000000..20f11554f6 --- /dev/null +++ b/tests/Images/Input/Pbm/rgb_binary_wide.ppm @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10cb6013ed7f17fd29b857189e864b11057fc34cdbf6d719c14470f7f2c34743 +size 5235 diff --git a/tests/Images/Input/Png/animated/issue-animated-frame-count.png b/tests/Images/Input/Png/animated/issue-animated-frame-count.png new file mode 100644 index 0000000000..88427f4873 --- /dev/null +++ b/tests/Images/Input/Png/animated/issue-animated-frame-count.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af4e320f586ab26c55612a7ccfc98a8c99cd6a0efe8a70d379503751d06fe8bd +size 51542 diff --git a/tests/Images/Input/Png/cgbi/clocks.png b/tests/Images/Input/Png/cgbi/clocks.png new file mode 100644 index 0000000000..d7cf18367e --- /dev/null +++ b/tests/Images/Input/Png/cgbi/clocks.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc462d8c2697060cde9a2e975ffb828d822ee3b0d4d12e3c5f081114176c036b +size 389981 diff --git a/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png b/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png new file mode 100644 index 0000000000..18cfa9246d --- /dev/null +++ b/tests/Images/Input/Png/cgbi/colors-cgbi-bitdepth16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59610bc03f6ca867e5f71c574b3a0d1942c9e3a230c8a32bf3007cb82f286866 +size 12853 diff --git a/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png b/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png new file mode 100644 index 0000000000..f6406559b1 --- /dev/null +++ b/tests/Images/Input/Png/cgbi/colors-cgbi-palette.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a2f20c69ae423523a8f41887e3f37257a338f2220c2ea44d35c87daf8c3aa3 +size 12853 diff --git a/tests/Images/Input/Png/cgbi/colors.png b/tests/Images/Input/Png/cgbi/colors.png new file mode 100644 index 0000000000..9b3b371bd5 --- /dev/null +++ b/tests/Images/Input/Png/cgbi/colors.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f34436f755e3c6d15341f29c992331045ae16ad413144ca798ede5c085c8e6a +size 12853 diff --git a/tests/Images/Input/Png/cgbi/flecks.png b/tests/Images/Input/Png/cgbi/flecks.png new file mode 100644 index 0000000000..6400442296 --- /dev/null +++ b/tests/Images/Input/Png/cgbi/flecks.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6be7b478594ba5e4d37bc135c881c0a16cf1c804fece5440bab997c7b69182f1 +size 187703 diff --git a/tests/Images/Input/Png/cgbi/screen.png b/tests/Images/Input/Png/cgbi/screen.png new file mode 100644 index 0000000000..57eca3d463 --- /dev/null +++ b/tests/Images/Input/Png/cgbi/screen.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e9bfbac37a57b71fa27b38b21314bd49dbe1cb19a2eb9d0f272ec7be3b72a33 +size 94834 diff --git a/tests/Images/Input/Png/duplicate-header-chunk-resync.png b/tests/Images/Input/Png/duplicate-header-chunk-resync.png new file mode 100644 index 0000000000..50652a1067 --- /dev/null +++ b/tests/Images/Input/Png/duplicate-header-chunk-resync.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1183a3462f92784a0608fef2da95bef92d7f13f6275d4c628cc2310c772085cb +size 38937 diff --git a/tests/Images/Input/Png/icc-profiles/Perceptual-cLUT-only.png b/tests/Images/Input/Png/icc-profiles/Perceptual-cLUT-only.png new file mode 100644 index 0000000000..8ac1afde9f --- /dev/null +++ b/tests/Images/Input/Png/icc-profiles/Perceptual-cLUT-only.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c734cacc2c6e761bab088cac80ef09da7b56a545ce71c6cced4cac31e661795 +size 119811 diff --git a/tests/Images/Input/Png/icc-profiles/Perceptual.png b/tests/Images/Input/Png/icc-profiles/Perceptual.png new file mode 100644 index 0000000000..cac9bcb1e5 --- /dev/null +++ b/tests/Images/Input/Png/icc-profiles/Perceptual.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:208a325dedea4453b7accce1ec540452af2e9be0f8c1f636f1d61a463eb3a9ae +size 123151 diff --git a/tests/Images/Input/Png/icc-profiles/sRGB_Gray.png b/tests/Images/Input/Png/icc-profiles/sRGB_Gray.png new file mode 100644 index 0000000000..3326936ceb --- /dev/null +++ b/tests/Images/Input/Png/icc-profiles/sRGB_Gray.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c64e0f6cc38750c83e6ff0cf1911e210c342900bb2cd6c88d3daed30c854e863 +size 4531 diff --git a/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba32.png b/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba32.png new file mode 100644 index 0000000000..7afc51cfe3 --- /dev/null +++ b/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fc63cea5de188e76503bde2fce3ff84518af5064bb46d506420cd6d7e58285b +size 7237 diff --git a/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba64.png b/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba64.png new file mode 100644 index 0000000000..822aca4f53 --- /dev/null +++ b/tests/Images/Input/Png/icc-profiles/sRGB_Gray_Interlaced_Rgba64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64343871be4ad61451ef968fa9f07c6a11dee65d0f8fd718ae8c4941586aa60c +size 8227 diff --git a/tests/Images/Input/Png/iptc-profile.png b/tests/Images/Input/Png/iptc-profile.png new file mode 100644 index 0000000000..fa4199a0c8 --- /dev/null +++ b/tests/Images/Input/Png/iptc-profile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae7f5d11145762b6544b3e289fc6c3bcb13a5f4cd8511b02280da683bec4c96e +size 448011 diff --git a/tests/Images/Input/Png/issues/Issue_2882.png b/tests/Images/Input/Png/issues/Issue_2882.png new file mode 100644 index 0000000000..2d7a51dacb --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2882.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cebc98e62bcfe31df73ae7b6980382f4b56bdf7e7e6e9037946f5a84cb51c7d2 +size 1117 diff --git a/tests/Images/Input/Png/issues/Issue_2924.png b/tests/Images/Input/Png/issues/Issue_2924.png new file mode 100644 index 0000000000..0454642190 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_2924.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd366a2de522041c706729b01a6030df6c82a4e87ea3509cc78a47486f097044 +size 49376 diff --git a/tests/Images/Input/Png/issues/Issue_396_dragon.png b/tests/Images/Input/Png/issues/Issue_396_dragon.png new file mode 100644 index 0000000000..4e8b2d8917 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_396_dragon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:203aca58e0aa19cbb7ab0820ef55005af830bf4c9bfd1dd4d9967c7e1bc1d51a +size 494160 diff --git a/tests/Images/Input/Png/issues/Issue_396_hand_1.png b/tests/Images/Input/Png/issues/Issue_396_hand_1.png new file mode 100644 index 0000000000..1624bce043 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_396_hand_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ab791784ac46f9e50a63bd255f39ff8af236a922aab3510a29b473bd93bffd5 +size 16295 diff --git a/tests/Images/Input/Png/issues/Issue_396_hand_2.png b/tests/Images/Input/Png/issues/Issue_396_hand_2.png new file mode 100644 index 0000000000..d12fa097ba --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_396_hand_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbf54bcef6730b68b18770105a15448f984aaca6910693d4e65106a526af58d8 +size 17756 diff --git a/tests/Images/Input/Png/issues/issue_2469-i.png b/tests/Images/Input/Png/issues/issue_2469-i.png new file mode 100644 index 0000000000..bd651a3f2d --- /dev/null +++ b/tests/Images/Input/Png/issues/issue_2469-i.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4e0da0601ca5f479684359633c4dbd82881a35631d63477c01e8fd180e31482 +size 2521324 diff --git a/tests/Images/Input/Png/issues/issue_3000.png b/tests/Images/Input/Png/issues/issue_3000.png new file mode 100644 index 0000000000..d7d7ad4c63 --- /dev/null +++ b/tests/Images/Input/Png/issues/issue_3000.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f33e2f343140b01d9c5c913f4ea695748e3a93df145bc0bf2f73763f2d24cadc +size 385 diff --git a/tests/Images/Input/Tga/32bit_no_alphabits.tga b/tests/Images/Input/Tga/32bit_no_alphabits.tga index 903eca4594..206e8d7c5e 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 b21dad5e0d..153b0a055b 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 diff --git a/tests/Images/Input/Tga/issues/Issue2629.tga b/tests/Images/Input/Tga/issues/Issue2629.tga new file mode 100644 index 0000000000..4c87196ad5 --- /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 diff --git a/tests/Images/Input/Tiff/Cmyk-jpeg.tiff b/tests/Images/Input/Tiff/Cmyk-jpeg.tiff new file mode 100644 index 0000000000..e486403e4f --- /dev/null +++ b/tests/Images/Input/Tiff/Cmyk-jpeg.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abb923e457acc31a7f18c46a7d58fc5a42f5c3d197236403921e3ee623fa4fac +size 2046 diff --git a/tests/Images/Input/Tiff/Cmyk-planar-jpg.tiff b/tests/Images/Input/Tiff/Cmyk-planar-jpg.tiff new file mode 100644 index 0000000000..e486403e4f --- /dev/null +++ b/tests/Images/Input/Tiff/Cmyk-planar-jpg.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abb923e457acc31a7f18c46a7d58fc5a42f5c3d197236403921e3ee623fa4fac +size 2046 diff --git a/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif b/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif new file mode 100644 index 0000000000..b5835c1ed8 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/ExtraSamplesUnspecified.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71d28f17d2d56481faa4d241fae882eae4f8303c70f85bc3759f6a0c2074979e +size 1426558 diff --git a/tests/Images/Input/Tiff/Issues/Issue2454_A.tif b/tests/Images/Input/Tiff/Issues/Issue2454_A.tif new file mode 100644 index 0000000000..99e13be550 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2454_A.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:868fbf7fc7a61bc6b1226160c8dc3bb1faebd8d4a2a6fe9494962f3fbe3a7fdc +size 5024256 diff --git a/tests/Images/Input/Tiff/Issues/Issue2454_B.tif b/tests/Images/Input/Tiff/Issues/Issue2454_B.tif new file mode 100644 index 0000000000..9c322b7653 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2454_B.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:867851192f540742ba1481f503834f8aa77caa03ac59f8204d098bf940b0bb3a +size 4387646 diff --git a/tests/Images/Input/Tiff/Issues/Issue2679.tiff b/tests/Images/Input/Tiff/Issues/Issue2679.tiff new file mode 100644 index 0000000000..1bc49f079c --- /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 diff --git a/tests/Images/Input/Tiff/Issues/Issue2909.tiff b/tests/Images/Input/Tiff/Issues/Issue2909.tiff new file mode 100644 index 0000000000..99f1f088ee --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2909.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c69a7e7c7920766e98fccd273424a34e9094550e2176a7b4757ab2c0756d084 +size 1272 diff --git a/tests/Images/Input/Tiff/Issues/Issue2983.tiff b/tests/Images/Input/Tiff/Issues/Issue2983.tiff new file mode 100644 index 0000000000..7332ca42a9 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2983.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7bfe1d8660d11111cdf2674aedc43c1362dc8c2ecfab9b74b43a06c7c195863e +size 13311100 diff --git a/tests/Images/Input/Tiff/Issues/Issue3031.tiff b/tests/Images/Input/Tiff/Issues/Issue3031.tiff new file mode 100644 index 0000000000..bc3ef7d7cf --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue3031.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e4d2db56a1b7fdea09ed65eab1d10a02952821f6662ca77caa44b8c51b30310 +size 416 diff --git a/tests/Images/Input/Tiff/Issues/Issue3182ColorMap16Bit.tiff b/tests/Images/Input/Tiff/Issues/Issue3182ColorMap16Bit.tiff new file mode 100644 index 0000000000..d32b399459 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue3182ColorMap16Bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a0553c34175e647a027d9878b63d3229b686159ac5ee863046e35ab94aee8b4 +size 4704 diff --git a/tests/Images/Input/Tiff/Issues/Issue3182ColorMap8Bit.tiff b/tests/Images/Input/Tiff/Issues/Issue3182ColorMap8Bit.tiff new file mode 100644 index 0000000000..312f385931 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue3182ColorMap8Bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b664b087ad2ecab109d100a907180bc4aa2ace573c347424c47009c37360eb59 +size 4704 diff --git a/tests/Images/Input/Tiff/icc-profiles/Perceptual_CIELAB.tiff b/tests/Images/Input/Tiff/icc-profiles/Perceptual_CIELAB.tiff new file mode 100644 index 0000000000..ab2aff8207 --- /dev/null +++ b/tests/Images/Input/Tiff/icc-profiles/Perceptual_CIELAB.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9494eea65b2c5b6ef033fb89b4fee2ef97d07773ae4b3988da972e1ba152b890 +size 64418 diff --git a/tests/Images/Input/Tiff/icc-profiles/Perceptual_CMYK.tiff b/tests/Images/Input/Tiff/icc-profiles/Perceptual_CMYK.tiff new file mode 100644 index 0000000000..7dd685fe37 --- /dev/null +++ b/tests/Images/Input/Tiff/icc-profiles/Perceptual_CMYK.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfb7fe9e93362fa121d97fa05f61242733d79fa9fa06bb7153e1773e827567dd +size 601124 diff --git a/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB16.tiff b/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB16.tiff new file mode 100644 index 0000000000..3692848191 --- /dev/null +++ b/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB16.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b0e89ac971c23c12c9edff6f9e9dd066fe99f36e28e89972343d50562dd7dbd +size 65260 diff --git a/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB8.tiff b/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB8.tiff new file mode 100644 index 0000000000..e2c1ff54c0 --- /dev/null +++ b/tests/Images/Input/Tiff/icc-profiles/Perceptual_RGB8.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18b0e5ee58c9f53bf5414fc8244e98d65aa0bc6c3397e201399baff0cd378b23 +size 35236 diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..f99c06f3fa --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af45780d252025f690e039738f37a6656fea72ecdc8b816eea354259af46ebed +size 87491 diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..fbeb462fbc --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:190a4968abd4bbd39273020f0f8bee0e0e48573931653eaea5e49b49d3961206 +size 87301 diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..2fbe32e6fa --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19b4a2ee8761e0016a598f66f12eb62edf4c4d30f33694d30986ce84516ac454 +size 80177 diff --git a/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..d4508e32ad --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_16bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:879bacee73f5fea767439071aa6057d66d2d61bc554b109abb7b79765873730b +size 78795 diff --git a/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..e7c527c9cb --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac571a8aa1274bd11f79d7d428d72e46d25ffcb331630f5eb114b94283055f7e +size 90547 diff --git a/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..76f83f3973 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_32bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4bebd7e62dbe54456923981b27dcc1415362c30a7c3c0ca665acf8dbdfe1cc8 +size 115129 diff --git a/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..8ca8fba8f5 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bec8072a0062101c08e0c1c1f4265150a53276d2568ada482b0ad554b13d658d +size 90585 diff --git a/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..53aac39e78 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_32bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e618556125236e2b0034b8654ab7b3e8956cf6db3c7c35ae365445ce85b2ea3d +size 114137 diff --git a/tests/Images/Input/Tiff/tiled_gray_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..563af83ac2 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce5c12864099fa02c5702a5da3f5af8cadcf7822ff95ee117a3b8d846518d9d8 +size 59361 diff --git a/tests/Images/Input/Tiff/tiled_gray_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_gray_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..ed05c4b526 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_gray_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2942e3a2e83ee1e10cc238299c21dcc9ccad7058b0b389f69dcf3186cbd215a1 +size 67849 diff --git a/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..c9a2e6fd22 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5ffcad0698d5485dfaffc8fa8368e62e45ec54b715f82c93b0854a41875ea11 +size 220425 diff --git a/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..d3dc3f332a --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_48bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f69c65b66041cc34bfe894628bbe39a9e09cc9a8e7eccaefad42b672b2d2e3b +size 234705 diff --git a/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..cf11248642 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3ea3976405c5512d676a0830c5d2d7a4d2c12128e56b0cf1c722b7152f4e255 +size 220415 diff --git a/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..b8d98e5d77 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_48bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6715b725ac4ac2ba57f7c9f6b8fd89182b6d2c5a6b8d9141f204783bc44a8c5 +size 234697 diff --git a/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..827584750d --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4b95d268817cfd2e261d76a6698b51f2ccb7fbda39dc26737c9572d445f3e0e +size 240737 diff --git a/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..2cb3db6007 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_64bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74dbdcc550fd0c9a425a02bb48a458e2c285030fd3fdaa8e6359bae1f4e06096 +size 270201 diff --git a/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..61f2d10271 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8601908cea23f1cb15d46472c53424a7222213a8f1a3aa8df984f64068c6654c +size 240651 diff --git a/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..93d046f1c5 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_64bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7364b83b36201d4f40144a619c7bf551f0ff27509aba3db3c7a1dbc01a881d8 +size 270305 diff --git a/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..9863b2271b --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ad32fab0240fdd87752857d349768802305a4eed73cc42146ad92a25525963e +size 232587 diff --git a/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..107a311535 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_96bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c0b50066a4480b02c36ee97885e30498b0c9a702de6d317c648a7908a2d119d +size 313111 diff --git a/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..28ee6d3f61 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6022edc75314270ca5afb198113d74d5598b50663eda27a9a40563b7384d5976 +size 232667 diff --git a/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..c636b5872c --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_96bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02e12574235e0861f4cad850869f53893b35334142c36d7c27d317382167ed68 +size 311165 diff --git a/tests/Images/Input/Tiff/tiled_rgb_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..71b4b29ce5 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba8740738c2d86a22cc1bf43b1036e296e7caca8dad1ae70fd090edd9579f2cd +size 163803 diff --git a/tests/Images/Input/Tiff/tiled_rgb_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgb_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..a1b1bbfdd5 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgb_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c47d681bfefa0cb12a25d685f13cc9fd01b3e0cb99828b97ccd948b43c138cfa +size 180641 diff --git a/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..c980ae69fe --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8b7aad5756525ba52e43e309a982bc318197b68a168d2cf08bae6ee4422f59c +size 264637 diff --git a/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..09edf13030 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b8e0812543f47ce79701ddc57a6473f8f3c6e016520cdc76cf4c1c11a5f54a0 +size 370941 diff --git a/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..5f589070a8 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0405c620eb35ce375a2a3e198ab4208e450d420a1eea17846d69fd810b7bf0aa +size 264749 diff --git a/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..5eb324e931 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_128bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50620e1aeeb1d99fc108d08215f51eb7ef648cba506a1b657a74ee138b9e3a5f +size 369133 diff --git a/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..0def244601 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f456272c6948159ea17d6fa7d2a89e30521c1d4e29f0ad0b1a79894f122d2153 +size 220119 diff --git a/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..9c442bf340 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_64bit_big_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0ddd2ec8f73c784b060ff790e70f586bba70905b75f3cf8ae18f2b054e1eb06 +size 244677 diff --git a/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..bcd96a88bb --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee63d8b0d63fd091390678a4a2600df5c14a122002a4d9c93e7d01082a2ee347 +size 220045 diff --git a/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..456f57cbd6 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_64bit_little_endian_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8e05d9ca953045d732c4304575588d2db2ead50177b2ed9416922568760ee1f +size 244625 diff --git a/tests/Images/Input/Tiff/tiled_rgba_deflate_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_deflate_compressed_predictor.tiff new file mode 100644 index 0000000000..79bf1f6b76 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_deflate_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0a3ecf077c701f450ce363633583134a79fd9d4d91fff0bd79f4bebe5f18649 +size 185503 diff --git a/tests/Images/Input/Tiff/tiled_rgba_lzw_compressed_predictor.tiff b/tests/Images/Input/Tiff/tiled_rgba_lzw_compressed_predictor.tiff new file mode 100644 index 0000000000..6cc0f28dc7 --- /dev/null +++ b/tests/Images/Input/Tiff/tiled_rgba_lzw_compressed_predictor.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:183e011bc22048d65cd1945d60dc25dc9cb688d9141afefa1c66ae0edfca8309 +size 202825 diff --git a/tests/Images/Input/Webp/alpha-blend-2.webp b/tests/Images/Input/Webp/alpha-blend-2.webp new file mode 100644 index 0000000000..3ca4c77cff --- /dev/null +++ b/tests/Images/Input/Webp/alpha-blend-2.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b07a49ab8b3af82fa123faf897ec537cd26d57175b1d6301b617372c06432899 +size 1580484 diff --git a/tests/Images/Input/Webp/alpha-blend-3.webp b/tests/Images/Input/Webp/alpha-blend-3.webp new file mode 100644 index 0000000000..1922f561d5 --- /dev/null +++ b/tests/Images/Input/Webp/alpha-blend-3.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7244a2cfb42285a196fc7846c49da65fac47e5b85f735bc07b131707c8a2d46 +size 948 diff --git a/tests/Images/Input/Webp/alpha-blend-4.webp b/tests/Images/Input/Webp/alpha-blend-4.webp new file mode 100644 index 0000000000..6f4db231f6 --- /dev/null +++ b/tests/Images/Input/Webp/alpha-blend-4.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c8b98f188d006715bd5bc60593ff2a379078f28a7fc14a51d28ae1cfb279aac +size 2185502 diff --git a/tests/Images/Input/Webp/alpha-blend.webp b/tests/Images/Input/Webp/alpha-blend.webp new file mode 100644 index 0000000000..110d2a594c --- /dev/null +++ b/tests/Images/Input/Webp/alpha-blend.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5dc2762d1c1030fb951e1af57eaa7e66035a7b5e63bd9dc9f9bd50f0ff5c4c3a +size 1297692 diff --git a/tests/Images/Input/Webp/icc-profiles/Perceptual-cLUT-only.webp b/tests/Images/Input/Webp/icc-profiles/Perceptual-cLUT-only.webp new file mode 100644 index 0000000000..4787b792ab --- /dev/null +++ b/tests/Images/Input/Webp/icc-profiles/Perceptual-cLUT-only.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b7ef9eedd1b2a4b93f11ac52807c071f7d0a24513008c3e69b0d4a0fd9b70db +size 186596 diff --git a/tests/Images/Input/Webp/icc-profiles/Perceptual.webp b/tests/Images/Input/Webp/icc-profiles/Perceptual.webp new file mode 100644 index 0000000000..b78504f438 --- /dev/null +++ b/tests/Images/Input/Webp/icc-profiles/Perceptual.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aad13221b103b60c21a19e7ecfbab047404f25269485d26fc5dee4be11188865 +size 189800 diff --git a/tests/Images/Input/Webp/issues/Issue2866.webp b/tests/Images/Input/Webp/issues/Issue2866.webp new file mode 100644 index 0000000000..845569624d --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2866.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15e8a52a6d528fe071e73b037543b682bf62da7bab6d98ab690f25dd97f7298e +size 248688 diff --git a/tests/Images/Input/Webp/issues/Issue2906.webp b/tests/Images/Input/Webp/issues/Issue2906.webp new file mode 100644 index 0000000000..0911da0472 --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2906.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56fe6a91feb9545c0a15966e0f6bc560890b193073c96ae9e39bf387c7e0cbca +size 157092 diff --git a/tests/Images/Input/Webp/issues/Issue2925.webp b/tests/Images/Input/Webp/issues/Issue2925.webp new file mode 100644 index 0000000000..414a06caad --- /dev/null +++ b/tests/Images/Input/Webp/issues/Issue2925.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e12207babf122af7a62246938c2e78faa0d3f730edb3182f4f9d6adae6bfc602 +size 262144 diff --git a/tests/coverlet.runsettings b/tests/coverlet.runsettings index cffce3540b..f327b47a2e 100644 --- a/tests/coverlet.runsettings +++ b/tests/coverlet.runsettings @@ -9,7 +9,7 @@ lcov - [SixLabors.*]* + [SixLabors.ImageSharp*]* true