Browse Source

Merge branch 'main' into png-cgbi-improvements

pull/3137/head
James Jackson-South 3 weeks ago
committed by GitHub
parent
commit
0bccbfe2f5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/build-and-test.yml
  2. 2
      .github/workflows/code-coverage.yml
  3. 7
      ImageSharp.sln
  4. 28
      src/ImageSharp/Advanced/AotCompilerTools.cs
  5. 186
      src/ImageSharp/Color/Color.cs
  6. 2
      src/ImageSharp/Common/Helpers/ColorNumerics.cs
  7. 210
      src/ImageSharp/Common/Helpers/Numerics.cs
  8. 31
      src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs
  9. 113
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  10. 82
      src/ImageSharp/Common/Helpers/Vector128Utilities.cs
  11. 68
      src/ImageSharp/Common/Helpers/Vector256Utilities.cs
  12. 74
      src/ImageSharp/Common/Helpers/Vector512Utilities.cs
  13. 10
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  14. 4
      src/ImageSharp/Formats/EncodingUtilities.cs
  15. 13
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  16. 7
      src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
  17. 3
      src/ImageSharp/Formats/Exr/ExrMetadata.cs
  18. 2
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  19. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs
  20. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs
  21. 2
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs
  22. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector128.cs
  23. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector256.cs
  24. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector512.cs
  25. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs
  26. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs
  27. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs
  28. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs
  29. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector256.cs
  30. 12
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector512.cs
  31. 8
      src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Vector256.cs
  32. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
  33. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
  34. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
  35. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16PlanarTiffColor{TPixel}.cs
  36. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16TiffColor{TPixel}.cs
  37. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8PlanarTiffColor{TPixel}.cs
  38. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8TiffColor{TPixel}.cs
  39. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs
  40. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
  41. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs
  42. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
  43. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
  44. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
  45. 22
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
  46. 21
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
  47. 20
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
  48. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
  49. 6
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
  50. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
  51. 4
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
  52. 2
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
  53. 45
      src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs
  54. 18
      src/ImageSharp/ImageSharp.csproj
  55. 387
      src/ImageSharp/PixelFormats/AssociatedAlphaPixelOperations{TPixel}.cs
  56. 323
      src/ImageSharp/PixelFormats/HalfTypeHelper.cs
  57. 118
      src/ImageSharp/PixelFormats/IPixel.cs
  58. 84152
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs
  59. 847
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt
  60. 168
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs
  61. 38
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs
  62. 5035
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs
  63. 138
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt
  64. 550
      src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs
  65. 216
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  66. 2
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  67. 216
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  68. 2
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  69. 28
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  70. 82
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  71. 20
      src/ImageSharp/PixelFormats/PixelConversionModifiers.cs
  72. 39
      src/ImageSharp/PixelFormats/PixelImplementations/A8.cs
  73. 43
      src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs
  74. 273
      src/ImageSharp/PixelFormats/PixelImplementations/Abgr32P.cs
  75. 43
      src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs
  76. 273
      src/ImageSharp/PixelFormats/PixelImplementations/Argb32P.cs
  77. 42
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
  78. 50
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs
  79. 43
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs
  80. 273
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra32P.cs
  81. 50
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs
  82. 52
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs
  83. 61
      src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs
  84. 64
      src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs
  85. 63
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs
  86. 75
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs
  87. 270
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4P.cs
  88. 42
      src/ImageSharp/PixelFormats/PixelImplementations/L16.cs
  89. 42
      src/ImageSharp/PixelFormats/PixelImplementations/L8.cs
  90. 41
      src/ImageSharp/PixelFormats/PixelImplementations/La16.cs
  91. 41
      src/ImageSharp/PixelFormats/PixelImplementations/La32.cs
  92. 79
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs
  93. 97
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs
  94. 318
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4P.cs
  95. 78
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs
  96. 96
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs
  97. 19
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs
  98. 64
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32P.PixelOperations.cs
  99. 64
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Argb32P.PixelOperations.cs
  100. 13
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs

4
.github/workflows/build-and-test.yml

@ -166,14 +166,14 @@ jobs:
- name: DotNet Setup
if: ${{ matrix.options.sdk-preview != true }}
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
8.0.x
- name: DotNet Setup Preview
if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
10.0.x

2
.github/workflows/code-coverage.yml

@ -67,7 +67,7 @@ jobs:
restore-keys: ${{ runner.os }}-nuget-
- name: DotNet Setup
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: |
8.0.x

7
ImageSharp.sln

@ -556,6 +556,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Webp", "Webp", "{983A31E2-5
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\ImageSharp.Tests\ImageSharp.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.PublicApi.Tests", "tests\ImageSharp.PublicApi.Tests\ImageSharp.PublicApi.Tests.csproj", "{7D89D21A-0F54-4B36-BEAA-2B90994E840E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}"
@ -682,6 +684,10 @@ Global
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.Build.0 = Release|Any CPU
{7D89D21A-0F54-4B36-BEAA-2B90994E840E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D89D21A-0F54-4B36-BEAA-2B90994E840E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D89D21A-0F54-4B36-BEAA-2B90994E840E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D89D21A-0F54-4B36-BEAA-2B90994E840E}.Release|Any CPU.Build.0 = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -714,6 +720,7 @@ Global
{E1C42A6F-913B-4A7B-B1A8-2BB62843B254} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{983A31E2-5E26-4058-BD6E-03B4922D4BBF} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{7D89D21A-0F54-4B36-BEAA-2B90994E840E} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{C0D7754B-5277-438E-ABEB-2BA34401B5A7} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D}
{68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}

28
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -7,6 +7,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
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.Jpeg.Components;
@ -81,10 +82,13 @@ internal static class AotCompilerTools
Seed<A8>();
Seed<Argb32>();
Seed<Argb32P>();
Seed<Abgr32>();
Seed<Abgr32P>();
Seed<Bgr24>();
Seed<Bgr565>();
Seed<Bgra32>();
Seed<Bgra32P>();
Seed<Bgra4444>();
Seed<Bgra5551>();
Seed<Byte4>();
@ -95,16 +99,23 @@ internal static class AotCompilerTools
Seed<HalfSingle>();
Seed<HalfVector2>();
Seed<HalfVector4>();
Seed<HalfVector4P>();
Seed<NormalizedByte2>();
Seed<NormalizedByte4>();
Seed<NormalizedByte4P>();
Seed<NormalizedShort2>();
Seed<NormalizedShort4>();
Seed<Rg32>();
Seed<Rgb24>();
Seed<Rgb48>();
Seed<Rgb96>();
Seed<Rgba1010102>();
Seed<Rgba128>();
Seed<Rgba32>();
Seed<Rgba32P>();
Seed<Rgba64>();
Seed<RgbaHalf>();
Seed<RgbaHalfP>();
Seed<RgbaVector>();
Seed<Short2>();
Seed<Short4>();
@ -158,10 +169,13 @@ internal static class AotCompilerTools
Image<TPixel> img = default;
img.CloneAs<A8>(default);
img.CloneAs<Argb32>(default);
img.CloneAs<Argb32P>(default);
img.CloneAs<Abgr32>(default);
img.CloneAs<Abgr32P>(default);
img.CloneAs<Bgr24>(default);
img.CloneAs<Bgr565>(default);
img.CloneAs<Bgra32>(default);
img.CloneAs<Bgra32P>(default);
img.CloneAs<Bgra4444>(default);
img.CloneAs<Bgra5551>(default);
img.CloneAs<Byte4>(default);
@ -172,16 +186,23 @@ internal static class AotCompilerTools
img.CloneAs<HalfSingle>(default);
img.CloneAs<HalfVector2>(default);
img.CloneAs<HalfVector4>(default);
img.CloneAs<HalfVector4P>(default);
img.CloneAs<NormalizedByte2>(default);
img.CloneAs<NormalizedByte4>(default);
img.CloneAs<NormalizedByte4P>(default);
img.CloneAs<NormalizedShort2>(default);
img.CloneAs<NormalizedShort4>(default);
img.CloneAs<Rg32>(default);
img.CloneAs<Rgb24>(default);
img.CloneAs<Rgb48>(default);
img.CloneAs<Rgb96>(default);
img.CloneAs<Rgba1010102>(default);
img.CloneAs<Rgba128>(default);
img.CloneAs<Rgba32>(default);
img.CloneAs<Rgba32P>(default);
img.CloneAs<Rgba64>(default);
img.CloneAs<RgbaHalf>(default);
img.CloneAs<RgbaHalfP>(default);
img.CloneAs<RgbaVector>(default);
img.CloneAs<Short2>(default);
img.CloneAs<Short4>(default);
@ -208,6 +229,7 @@ internal static class AotCompilerTools
where TPixel : unmanaged, IPixel<TPixel>
{
default(BmpEncoderCore).Encode<TPixel>(default, default, default);
default(ExrEncoderCore).Encode<TPixel>(default, default, default);
default(GifEncoderCore).Encode<TPixel>(default, default, default);
default(JpegEncoderCore).Encode<TPixel>(default, default, default);
default(PbmEncoderCore).Encode<TPixel>(default, default, default);
@ -227,6 +249,7 @@ internal static class AotCompilerTools
where TPixel : unmanaged, IPixel<TPixel>
{
default(BmpDecoderCore).Decode<TPixel>(default, default, default);
default(ExrDecoderCore).Decode<TPixel>(default, default, default);
default(GifDecoderCore).Decode<TPixel>(default, default, default);
default(JpegDecoderCore).Decode<TPixel>(default, default, default);
default(PbmDecoderCore).Decode<TPixel>(default, default, default);
@ -247,6 +270,7 @@ internal static class AotCompilerTools
{
AotCompileImageEncoder<TPixel, WebpEncoder>();
AotCompileImageEncoder<TPixel, BmpEncoder>();
AotCompileImageEncoder<TPixel, ExrEncoder>();
AotCompileImageEncoder<TPixel, GifEncoder>();
AotCompileImageEncoder<TPixel, JpegEncoder>();
AotCompileImageEncoder<TPixel, PbmEncoder>();
@ -265,6 +289,7 @@ internal static class AotCompilerTools
{
AotCompileImageDecoder<TPixel, WebpDecoder>();
AotCompileImageDecoder<TPixel, BmpDecoder>();
AotCompileImageDecoder<TPixel, ExrDecoder>();
AotCompileImageDecoder<TPixel, GifDecoder>();
AotCompileImageDecoder<TPixel, JpegDecoder>();
AotCompileImageDecoder<TPixel, PbmDecoder>();
@ -336,6 +361,7 @@ internal static class AotCompilerTools
AotCompileImageProcessor<TPixel, VignetteProcessor>();
AotCompileImageProcessor<TPixel, AdaptiveHistogramEqualizationProcessor>();
AotCompileImageProcessor<TPixel, AdaptiveHistogramEqualizationSlidingWindowProcessor>();
AotCompileImageProcessor<TPixel, AutoLevelProcessor>();
AotCompileImageProcessor<TPixel, GlobalHistogramEqualizationProcessor>();
AotCompileImageProcessor<TPixel, AchromatomalyProcessor>();
AotCompileImageProcessor<TPixel, AchromatopsiaProcessor>();
@ -368,11 +394,13 @@ internal static class AotCompilerTools
AotCompileImageProcessor<TPixel, PaletteDitherProcessor>();
AotCompileImageProcessor<TPixel, BokehBlurProcessor>();
AotCompileImageProcessor<TPixel, BoxBlurProcessor>();
AotCompileImageProcessor<TPixel, ConvolutionProcessor>();
AotCompileImageProcessor<TPixel, EdgeDetector2DProcessor>();
AotCompileImageProcessor<TPixel, EdgeDetectorCompassProcessor>();
AotCompileImageProcessor<TPixel, EdgeDetectorProcessor>();
AotCompileImageProcessor<TPixel, GaussianBlurProcessor>();
AotCompileImageProcessor<TPixel, GaussianSharpenProcessor>();
AotCompileImageProcessor<TPixel, MedianBlurProcessor>();
AotCompileImageProcessor<TPixel, AdaptiveThresholdProcessor>();
AotCompileImageProcessor<TPixel, BinaryThresholdProcessor>();

186
src/ImageSharp/Color/Color.cs

@ -21,29 +21,44 @@ public readonly partial struct Color : IEquatable<Color>
{
private readonly Vector4 data;
private readonly IPixel? boxedHighPrecisionPixel;
private readonly bool isAssociated;
private readonly bool dataIsAssociated;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">The <see cref="Vector4"/> containing the color information.</param>
/// <param name="alphaRepresentation">The alpha representation exposed by the color.</param>
/// <param name="dataAlphaRepresentation">The alpha representation of <paramref name="vector"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Color(Vector4 vector)
private Color(Vector4 vector, PixelAlphaRepresentation alphaRepresentation, PixelAlphaRepresentation dataAlphaRepresentation)
{
this.data = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
this.boxedHighPrecisionPixel = null;
this.isAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated;
this.dataIsAssociated = dataAlphaRepresentation == PixelAlphaRepresentation.Associated;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The pixel containing color information.</param>
/// <param name="alphaRepresentation">The alpha representation of <paramref name="pixel"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Color(IPixel pixel)
private Color(IPixel pixel, PixelAlphaRepresentation alphaRepresentation)
{
this.boxedHighPrecisionPixel = pixel;
this.data = default;
this.isAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated;
this.dataIsAssociated = this.isAssociated;
}
/// <summary>
/// Gets the alpha representation used by this color's scaled vector.
/// </summary>
public PixelAlphaRepresentation AlphaRepresentation
=> this.isAssociated ? PixelAlphaRepresentation.Associated : PixelAlphaRepresentation.Unassociated;
/// <summary>
/// Checks whether two <see cref="Color"/> structures are equal.
/// </summary>
@ -80,21 +95,46 @@ public readonly partial struct Color : IEquatable<Color>
{
// Avoid boxing in case we can convert to Vector4 safely and efficiently
PixelTypeInfo info = TPixel.GetPixelTypeInfo();
if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32)
if (info.ComponentInfo.HasValue)
{
return new Color(source.ToScaledVector4());
int maximumComponentPrecision = info.ComponentInfo.Value.GetMaximumComponentPrecision();
if (maximumComponentPrecision <= (int)PixelComponentBitDepth.Bit32)
{
if (info.AlphaRepresentation == PixelAlphaRepresentation.Associated && maximumComponentPrecision <= (int)PixelComponentBitDepth.Bit8)
{
// Associated formats with at most eight bits per component can be canonicalized without loss by their pixel-specific conversion.
// Higher-precision formats retain their associated values because unassociation can lose representable data.
Vector4 vector = source.ToUnassociatedScaledVector4();
return new Color(vector, info.AlphaRepresentation, PixelAlphaRepresentation.Unassociated);
}
return new Color(source.ToScaledVector4(), info.AlphaRepresentation, info.AlphaRepresentation);
}
}
return new Color(source);
return new Color(source, info.AlphaRepresentation);
}
/// <summary>
/// Creates a <see cref="Color"/> from a generic scaled <see cref="Vector4"/>.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <param name="source">The unassociated vector to load the color from.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color FromScaledVector(Vector4 source)
=> new(source, PixelAlphaRepresentation.Unassociated, PixelAlphaRepresentation.Unassociated);
/// <summary>
/// Creates a <see cref="Color"/> from a generic scaled <see cref="Vector4"/> with the specified alpha representation.
/// </summary>
/// <param name="source">The vector to load the color from.</param>
/// <param name="alphaRepresentation">The alpha representation of <paramref name="source"/>.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color FromScaledVector(Vector4 source) => new(source);
public static Color FromScaledVector(Vector4 source, PixelAlphaRepresentation alphaRepresentation)
=> new(source, alphaRepresentation, alphaRepresentation);
/// <summary>
/// Bulk converts a span of generic scaled <see cref="Vector4"/> to a span of <see cref="Color"/>.
@ -103,11 +143,23 @@ public readonly partial struct Color : IEquatable<Color>
/// <param name="destination">The destination color span.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void FromScaledVector(ReadOnlySpan<Vector4> source, Span<Color> destination)
=> FromScaledVector(source, destination, PixelAlphaRepresentation.Unassociated);
/// <summary>
/// Bulk converts a span of generic scaled <see cref="Vector4"/> values with the specified alpha representation
/// to a span of <see cref="Color"/> values.
/// </summary>
/// <param name="source">The source vector span.</param>
/// <param name="destination">The destination color span.</param>
/// <param name="alphaRepresentation">The alpha representation of the source vectors.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void FromScaledVector(ReadOnlySpan<Vector4> source, Span<Color> destination, PixelAlphaRepresentation alphaRepresentation)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector(source[i]);
destination[i] = FromScaledVector(source[i], alphaRepresentation);
}
}
@ -125,19 +177,37 @@ public readonly partial struct Color : IEquatable<Color>
// Avoid boxing in case we can convert to Vector4 safely and efficiently
PixelTypeInfo info = TPixel.GetPixelTypeInfo();
if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32)
if (info.ComponentInfo.HasValue)
{
for (int i = 0; i < source.Length; i++)
int maximumComponentPrecision = info.ComponentInfo.Value.GetMaximumComponentPrecision();
if (maximumComponentPrecision <= (int)PixelComponentBitDepth.Bit32)
{
destination[i] = FromScaledVector(source[i].ToScaledVector4());
if (info.AlphaRepresentation == PixelAlphaRepresentation.Associated && maximumComponentPrecision <= (int)PixelComponentBitDepth.Bit8)
{
// Match the scalar conversion by retaining exact unassociated values from the format-specific operation.
for (int i = 0; i < source.Length; i++)
{
Vector4 vector = source[i].ToUnassociatedScaledVector4();
destination[i] = new Color(vector, info.AlphaRepresentation, PixelAlphaRepresentation.Unassociated);
}
return;
}
for (int i = 0; i < source.Length; i++)
{
destination[i] = new Color(source[i].ToScaledVector4(), info.AlphaRepresentation, info.AlphaRepresentation);
}
return;
}
}
else
for (int i = 0; i < source.Length; i++)
{
for (int i = 0; i < source.Length; i++)
{
destination[i] = new Color(source[i]);
}
destination[i] = new Color(source[i], info.AlphaRepresentation);
}
}
@ -276,13 +346,13 @@ public readonly partial struct Color : IEquatable<Color>
/// Alters the alpha channel of the color, returning a new instance.
/// </summary>
/// <param name="alpha">The new value of alpha [0..1].</param>
/// <returns>The color having it's alpha channel altered.</returns>
/// <returns>The color having its alpha channel altered.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color WithAlpha(float alpha)
{
Vector4 v = this.ToScaledVector4();
v.W = alpha;
return FromScaledVector(v);
Vector4 vector = this.ToScaledVector4(PixelAlphaRepresentation.Unassociated);
vector.W = Numerics.Clamp(alpha, 0, 1);
return new Color(vector, this.AlphaRepresentation, PixelAlphaRepresentation.Unassociated);
}
/// <summary>
@ -296,9 +366,7 @@ public readonly partial struct Color : IEquatable<Color>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ToHex(ColorHexFormat format = ColorHexFormat.Rgba)
{
Rgba32 rgba = (this.boxedHighPrecisionPixel is not null)
? this.boxedHighPrecisionPixel.ToRgba32()
: Rgba32.FromScaledVector4(this.data);
Rgba32 rgba = this.ToPixel<Rgba32>();
uint hexOrder = format switch
{
@ -327,29 +395,76 @@ public readonly partial struct Color : IEquatable<Color>
return pixel;
}
if (this.boxedHighPrecisionPixel is null)
Vector4 vector = this.boxedHighPrecisionPixel?.ToScaledVector4() ?? this.data;
if (this.dataIsAssociated)
{
return TPixel.FromScaledVector4(this.data);
// Preserve associated components directly while allowing the destination to quantize alpha to its own storage grid.
return TPixel.FromAssociatedScaledVector4(vector);
}
return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
// Unassociated input lets an associated destination quantize alpha before it multiplies the color components.
return TPixel.FromUnassociatedScaledVector4(vector);
}
/// <summary>
/// Expands the color into a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// Expands the color into a generic ("scaled") <see cref="Vector4"/> representation,
/// preserving the <see cref="AlphaRepresentation"/>, with values scaled and clamped between
/// <value>0</value> and <value>1</value>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
if (this.boxedHighPrecisionPixel is null)
Vector4 vector = this.boxedHighPrecisionPixel?.ToScaledVector4() ?? this.data;
if (this.dataIsAssociated == this.isAssociated)
{
return vector;
}
if (this.isAssociated)
{
Numerics.Premultiply(ref vector);
}
else
{
Numerics.UnPremultiply(ref vector);
}
return vector;
}
/// <summary>
/// Expands the color into a generic ("scaled") <see cref="Vector4"/> using the specified alpha representation,
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// </summary>
/// <param name="alphaRepresentation">
/// The alpha representation to apply. <see cref="PixelAlphaRepresentation.Associated"/> returns color components
/// multiplied by alpha; other representations return color components independent of alpha.
/// </param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4(PixelAlphaRepresentation alphaRepresentation)
{
bool targetIsAssociated = alphaRepresentation == PixelAlphaRepresentation.Associated;
Vector4 vector = this.boxedHighPrecisionPixel?.ToScaledVector4() ?? this.data;
if (this.dataIsAssociated == targetIsAssociated)
{
return vector;
}
if (targetIsAssociated)
{
Numerics.Premultiply(ref vector);
}
else
{
return this.data;
Numerics.UnPremultiply(ref vector);
}
return this.boxedHighPrecisionPixel.ToScaledVector4();
return vector;
}
/// <summary>
@ -377,10 +492,11 @@ public readonly partial struct Color : IEquatable<Color>
{
if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null)
{
return this.data == other.data;
return this.isAssociated == other.isAssociated && this.ToScaledVector4() == other.ToScaledVector4();
}
return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true;
return this.isAssociated == other.isAssociated
&& this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true;
}
/// <inheritdoc />
@ -392,10 +508,10 @@ public readonly partial struct Color : IEquatable<Color>
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.GetHashCode();
return HashCode.Combine(this.ToScaledVector4(), this.isAssociated);
}
return this.boxedHighPrecisionPixel.GetHashCode();
return HashCode.Combine(this.boxedHighPrecisionPixel.ToScaledVector4(), this.isAssociated);
}
/// <summary>

2
src/ImageSharp/Common/Helpers/ColorNumerics.cs

@ -26,7 +26,7 @@ internal static class ColorNumerics
/// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images).
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels)
public static int GetBT709Luminance(Vector4 vector, int luminanceLevels)
=> (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1));
/// <summary>

210
src/ImageSharp/Common/Helpers/Numerics.cs

@ -6,6 +6,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;
@ -513,7 +514,7 @@ internal static class Numerics
/// <summary>
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <param name="source">The <see cref="Vector4"/> to premultiply.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Premultiply(ref Vector4 source)
{
@ -524,50 +525,98 @@ internal static class Numerics
}
/// <summary>
/// Bulk variant of <see cref="Premultiply(ref Vector4)"/>
/// Clamps associated color components to the alpha component while preserving alpha.
/// </summary>
/// <param name="vectors">The span of vectors</param>
/// <param name="source">The associated vector to clamp.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ClampRgbToAlpha(ref Vector4 source)
{
Vector4 alpha = PermuteW(source);
source = WithW(Vector4.Min(Vector4.Max(source, Vector4.Zero), alpha), alpha);
}
/// <summary>
/// Premultiplies the X, Y, and Z components of each vector by its W component while preserving W.
/// </summary>
/// <param name="vectors">The vectors to premultiply.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Premultiply(Span<Vector4> vectors)
{
if (Avx.IsSupported && vectors.Length >= 2)
if (Vector512.IsHardwareAccelerated)
{
int vectorsPerVector = Vector512<float>.Count / Vector128<float>.Count;
ref Vector512<float> vectorsBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector512<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)(vectors.Length / vectorsPerVector));
Vector128<float> alphaMask128 = Vector128.Create(0, 0, 0, -1).AsSingle();
Vector256<float> alphaMask256 = Vector256.Create(alphaMask128, alphaMask128);
Vector512<float> alphaMask = Vector512.Create(alphaMask256, alphaMask256);
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
Vector512<float> source = vectorsBase;
Vector512<float> alpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
// Multiplication also squares W, so select the original W lanes to preserve alpha bit-for-bit.
vectorsBase = Vector512.ConditionalSelect(alphaMask, source, source * alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
vectors = vectors[(vectors.Length - (vectors.Length % vectorsPerVector))..];
}
if (Vector256.IsHardwareAccelerated)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
int vectorsPerVector = Vector256<float>.Count / Vector128<float>.Count;
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u);
ref Vector256<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)(vectors.Length / vectorsPerVector));
Vector128<float> alphaMask128 = Vector128.Create(0, 0, 0, -1).AsSingle();
Vector256<float> alphaMask = Vector256.Create(alphaMask128, alphaMask128);
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
Vector256<float> source = vectorsBase;
Vector256<float> alpha = Avx.Permute(source, ShuffleAlphaControl);
vectorsBase = Avx.Blend(Avx.Multiply(source, alpha), source, BlendAlphaControl);
Vector256<float> alpha = Vector256_.ShuffleNative(source, ShuffleAlphaControl);
// Multiplication also squares W, so select the original W lanes to preserve alpha bit-for-bit.
vectorsBase = Vector256.ConditionalSelect(alphaMask, source, source * alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
if (Modulo2(vectors.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
Premultiply(ref MemoryMarshal.GetReference(vectors[^1..]));
}
vectors = vectors[(vectors.Length - (vectors.Length % vectorsPerVector))..];
}
else
if (Vector128.IsHardwareAccelerated)
{
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, (uint)vectors.Length);
ref Vector128<float> vectorsBase = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector128<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length);
Vector128<float> alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle();
while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd))
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
Premultiply(ref vectorsStart);
Vector128<float> source = vectorsBase;
Vector128<float> alpha = Vector128_.ShuffleNative(source, ShuffleAlphaControl);
vectorsStart = ref Unsafe.Add(ref vectorsStart, 1);
// Multiplication also squares W, so select the original W lane to preserve alpha bit-for-bit.
vectorsBase = Vector128.ConditionalSelect(alphaMask, source, source * alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
return;
}
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
for (nuint i = 0; i < (uint)vectors.Length; i++)
{
Premultiply(ref Unsafe.Add(ref vectorsStart, i));
}
}
/// <summary>
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(ref Vector4)"/>.
/// When alpha is zero, the RGB components remain unchanged because no unassociated value can be recovered.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <param name="source">The <see cref="Vector4"/> to unpremultiply.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnPremultiply(ref Vector4 source)
{
@ -575,84 +624,143 @@ internal static class Numerics
UnPremultiply(ref source, alpha);
}
/// <summary>
/// Unpremultiplies the X, Y, and Z components of a vector by the supplied alpha while preserving W.
/// When alpha is zero, the RGB components remain unchanged because no unassociated value can be recovered.
/// </summary>
/// <param name="source">The vector to unpremultiply.</param>
/// <param name="alpha">The source alpha replicated to every component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnPremultiply(ref Vector4 source, Vector4 alpha)
{
// Zero alpha has no mathematical inverse, so preserve stored additive or hidden RGB data unchanged.
if (alpha == Vector4.Zero)
{
return;
}
// Divide source by alpha if alpha is nonzero, otherwise set all components to match the source value
// Blend the result with the alpha vector to ensure that the alpha component is unchanged
// Division would replace W with one, so restore the original alpha component exactly.
source = WithW(source / alpha, alpha);
}
/// <summary>
/// Bulk variant of <see cref="UnPremultiply(ref Vector4)"/>
/// Unpremultiplies the X, Y, and Z components of each vector by its W component while preserving W.
/// Vectors with zero W retain their RGB components because no unassociated value can be recovered.
/// </summary>
/// <param name="vectors">The span of vectors</param>
/// <param name="vectors">The vectors to unpremultiply.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnPremultiply(Span<Vector4> vectors)
{
if (Avx.IsSupported && vectors.Length >= 2)
if (Vector512.IsHardwareAccelerated)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u);
Vector256<float> epsilon = Vector256.Create(Constants.Epsilon);
int vectorsPerVector = Vector512<float>.Count / Vector128<float>.Count;
ref Vector512<float> vectorsBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector512<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)(vectors.Length / vectorsPerVector));
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
Vector256<float> source = vectorsBase;
Vector256<float> alpha = Avx.Permute(source, ShuffleAlphaControl);
Vector512<float> source = vectorsBase;
Vector512<float> alpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
vectorsBase = UnPremultiply(source, alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
if (Modulo2(vectors.Length) != 0)
vectors = vectors[(vectors.Length - (vectors.Length % vectorsPerVector))..];
}
if (Vector256.IsHardwareAccelerated)
{
int vectorsPerVector = Vector256<float>.Count / Vector128<float>.Count;
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector256<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)(vectors.Length / vectorsPerVector));
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
UnPremultiply(ref MemoryMarshal.GetReference(vectors[^1..]));
Vector256<float> source = vectorsBase;
Vector256<float> alpha = Vector256_.ShuffleNative(source, ShuffleAlphaControl);
vectorsBase = UnPremultiply(source, alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
vectors = vectors[(vectors.Length - (vectors.Length % vectorsPerVector))..];
}
else
if (Vector128.IsHardwareAccelerated)
{
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, (uint)vectors.Length);
ref Vector128<float> vectorsBase = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors));
ref Vector128<float> vectorsEnd = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length);
while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd))
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsEnd))
{
UnPremultiply(ref vectorsStart);
vectorsStart = ref Unsafe.Add(ref vectorsStart, 1);
Vector128<float> source = vectorsBase;
Vector128<float> alpha = Vector128_.ShuffleNative(source, ShuffleAlphaControl);
vectorsBase = UnPremultiply(source, alpha);
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
}
return;
}
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
for (nuint i = 0; i < (uint)vectors.Length; i++)
{
UnPremultiply(ref Unsafe.Add(ref vectorsStart, i));
}
}
/// <summary>
/// Unpremultiplies the RGB lanes of a vector while preserving its alpha lane.
/// When alpha is zero, the RGB lanes remain unchanged because no unassociated value can be recovered.
/// </summary>
/// <param name="source">The associated vector.</param>
/// <param name="alpha">The source alpha replicated to every lane.</param>
/// <returns>The unassociated vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> UnPremultiply(Vector128<float> source, Vector128<float> alpha)
{
// Zero alpha has no mathematical inverse, so select the source lanes to preserve stored additive or hidden RGB data.
Vector128<float> zeroMask = Vector128.Equals(alpha, Vector128<float>.Zero);
Vector128<float> result = Vector128.ConditionalSelect(zeroMask, source, source / alpha);
// Division would replace W with one, so restore the original alpha lane exactly.
Vector128<float> alphaMask = Vector128.Create(0, 0, 0, -1).AsSingle();
return Vector128.ConditionalSelect(alphaMask, alpha, result);
}
/// <summary>
/// Unpremultiplies the RGB lanes of two vectors while preserving their alpha lanes.
/// Vectors with zero alpha retain their RGB lanes because no unassociated value can be recovered.
/// </summary>
/// <param name="source">The associated vectors.</param>
/// <param name="alpha">Each source alpha replicated across its four lanes.</param>
/// <returns>The unassociated vectors.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> UnPremultiply(Vector256<float> source, Vector256<float> alpha)
{
// Check if alpha is zero to avoid division by zero
// Zero alpha has no mathematical inverse, so select the source lanes to preserve stored additive or hidden RGB data.
Vector256<float> zeroMask = Avx.CompareEqual(alpha, Vector256<float>.Zero);
// Divide source by alpha if alpha is nonzero, otherwise set all components to match the source value
Vector256<float> result = Avx.BlendVariable(Avx.Divide(source, alpha), source, zeroMask);
// Blend the result with the alpha vector to ensure that the alpha component is unchanged
// Division would replace W with one, so restore both original alpha lanes exactly.
return Avx.Blend(result, alpha, BlendAlphaControl);
}
/// <summary>
/// Unpremultiplies the RGB lanes of four vectors while preserving their alpha lanes.
/// Vectors with zero alpha retain their RGB lanes because no unassociated value can be recovered.
/// </summary>
/// <param name="source">The associated vectors.</param>
/// <param name="alpha">Each source alpha replicated across its four lanes.</param>
/// <returns>The unassociated vectors.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> UnPremultiply(Vector512<float> source, Vector512<float> alpha)
{
// Check if alpha is zero to avoid division by zero
// Zero alpha has no mathematical inverse, so select the source lanes to preserve stored additive or hidden RGB data.
Vector512<float> zeroMask = Vector512.Equals(alpha, Vector512<float>.Zero);
// Divide source by alpha if alpha is nonzero, otherwise set all components to match the source value
Vector512<float> result = Vector512.ConditionalSelect(zeroMask, source, source / alpha);
// Blend the result with the alpha vector to ensure that the alpha component is unchanged
// Division would replace W with one, so restore all four original alpha lanes exactly.
Vector512<float> 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);
}

31
src/ImageSharp/Common/Helpers/SimdUtils.Convert.cs

@ -41,11 +41,29 @@ internal static partial class SimdUtils
{
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!");
HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref destination);
HwIntrinsics.FloatToByteSaturateReduce(ref source, ref destination, byte.MaxValue);
if (source.Length > 0)
{
ConvertNormalizedFloatToByteRemainder(source, destination);
ConvertFloatToByteRemainder(source, destination, byte.MaxValue);
}
}
/// <summary>
/// Converts byte-magnitude floating-point values to bytes using saturating round-to-nearest with midpoint values away from zero.
/// </summary>
/// <param name="source">The source byte magnitudes.</param>
/// <param name="destination">The destination bytes.</param>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void FloatToByteSaturate(ReadOnlySpan<float> source, Span<byte> destination)
{
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!");
HwIntrinsics.FloatToByteSaturateReduce(ref source, ref destination, 1F);
if (source.Length > 0)
{
ConvertFloatToByteRemainder(source, destination, 1F);
}
}
@ -57,22 +75,23 @@ internal static partial class SimdUtils
for (int i = 0; i < source.Length; i++)
{
Unsafe.Add(ref dBase, (uint)i) = Unsafe.Add(ref sBase, (uint)i) / 255f;
// Match the SIMD conversion so one span cannot contain different float representations of the same byte value.
Unsafe.Add(ref dBase, (uint)i) = Unsafe.Add(ref sBase, (uint)i) / (float)byte.MaxValue;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan<float> source, Span<byte> destination)
private static void ConvertFloatToByteRemainder(ReadOnlySpan<float> source, Span<byte> destination, float scale)
{
ref float sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < source.Length; i++)
{
Unsafe.Add(ref dBase, (uint)i) = ConvertToByte(Unsafe.Add(ref sBase, (uint)i));
Unsafe.Add(ref dBase, (uint)i) = ConvertToByte(Unsafe.Add(ref sBase, (uint)i), scale);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ConvertToByte(float f) => (byte)Numerics.Clamp((f * 255f) + 0.5f, 0, 255f);
private static byte ConvertToByte(float value, float scale) => (byte)Numerics.Clamp((value * scale) + .5F, 0, byte.MaxValue);
}

113
src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

@ -711,6 +711,10 @@ internal static partial class SimdUtils
ReadOnlySpan<byte> source,
Span<float> destination)
{
const double reciprocal = 1D / byte.MaxValue;
const float reciprocalHigh = (float)reciprocal;
const float reciprocalLow = (float)(reciprocal - reciprocalHigh);
if (Vector512.IsHardwareAccelerated && Avx512F.IsSupported)
{
DebugVerifySpanInput(source, destination, Vector512<byte>.Count);
@ -719,6 +723,8 @@ internal static partial class SimdUtils
ref byte sourceBase = ref MemoryMarshal.GetReference(source);
ref Vector512<float> destinationBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
Vector512<float> high = Vector512.Create(reciprocalHigh);
Vector512<float> low = Vector512.Create(reciprocalLow);
for (nuint i = 0; i < n; i++)
{
@ -728,11 +734,16 @@ internal static partial class SimdUtils
Vector512<int> i2 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)(Vector512<int>.Count * 2)));
Vector512<int> i3 = Avx512F.ConvertToVector512Int32(Vector128.LoadUnsafe(ref sourceBase, si + (nuint)(Vector512<int>.Count * 3)));
// Declare multiplier on each line. Codegen is better.
Vector512<float> f0 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i0);
Vector512<float> f1 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i1);
Vector512<float> f2 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i2);
Vector512<float> f3 = Vector512.Create(1 / (float)byte.MaxValue) * Avx512F.ConvertToVector512Single(i3);
Vector512<float> f0 = Avx512F.ConvertToVector512Single(i0);
Vector512<float> f1 = Avx512F.ConvertToVector512Single(i1);
Vector512<float> f2 = Avx512F.ConvertToVector512Single(i2);
Vector512<float> f3 = Avx512F.ConvertToVector512Single(i3);
// The residual term restores the correctly rounded byte / 255F result without paying for vector division.
f0 = Vector512_.FusedMultiplyAdd(f0, high, f0 * low);
f1 = Vector512_.FusedMultiplyAdd(f1, high, f1 * low);
f2 = Vector512_.FusedMultiplyAdd(f2, high, f2 * low);
f3 = Vector512_.FusedMultiplyAdd(f3, high, f3 * low);
ref Vector512<float> d = ref Unsafe.Add(ref destinationBase, i * 4);
@ -750,6 +761,8 @@ internal static partial class SimdUtils
ref byte sourceBase = ref MemoryMarshal.GetReference(source);
ref Vector256<float> destinationBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
Vector256<float> high = Vector256.Create(reciprocalHigh);
Vector256<float> low = Vector256.Create(reciprocalLow);
for (nuint i = 0; i < n; i++)
{
@ -762,11 +775,15 @@ internal static partial class SimdUtils
ref ulong refULong = ref Unsafe.As<byte, ulong>(ref Unsafe.Add(ref sourceBase, si));
Vector256<int> i3 = Avx2.ConvertToVector256Int32(Vector128.CreateScalarUnsafe(Unsafe.Add(ref refULong, 3)).AsByte());
// Declare multiplier on each line. Codegen is better.
Vector256<float> f0 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i0);
Vector256<float> f1 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i1);
Vector256<float> f2 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i2);
Vector256<float> f3 = Vector256.Create(1 / (float)byte.MaxValue) * Avx.ConvertToVector256Single(i3);
Vector256<float> f0 = Avx.ConvertToVector256Single(i0);
Vector256<float> f1 = Avx.ConvertToVector256Single(i1);
Vector256<float> f2 = Avx.ConvertToVector256Single(i2);
Vector256<float> f3 = Avx.ConvertToVector256Single(i3);
f0 = Vector256_.FusedMultiplyAdd(f0, high, f0 * low);
f1 = Vector256_.FusedMultiplyAdd(f1, high, f1 * low);
f2 = Vector256_.FusedMultiplyAdd(f2, high, f2 * low);
f3 = Vector256_.FusedMultiplyAdd(f3, high, f3 * low);
ref Vector256<float> d = ref Unsafe.Add(ref destinationBase, i * 4);
@ -785,7 +802,8 @@ internal static partial class SimdUtils
ref byte sourceBase = ref MemoryMarshal.GetReference(source);
ref Vector128<float> destinationBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(destination));
Vector128<float> scale = Vector128.Create(1 / (float)byte.MaxValue);
Vector128<float> high = Vector128.Create(reciprocalHigh);
Vector128<float> low = Vector128.Create(reciprocalLow);
for (nuint i = 0; i < n; i++)
{
@ -810,10 +828,15 @@ internal static partial class SimdUtils
(i2, i3) = Vector128.Widen(s1.AsInt16());
}
Vector128<float> f0 = scale * Vector128.ConvertToSingle(i0);
Vector128<float> f1 = scale * Vector128.ConvertToSingle(i1);
Vector128<float> f2 = scale * Vector128.ConvertToSingle(i2);
Vector128<float> f3 = scale * Vector128.ConvertToSingle(i3);
Vector128<float> f0 = Vector128.ConvertToSingle(i0);
Vector128<float> f1 = Vector128.ConvertToSingle(i1);
Vector128<float> f2 = Vector128.ConvertToSingle(i2);
Vector128<float> f3 = Vector128.ConvertToSingle(i3);
f0 = Vector128_.FusedMultiplyAdd(f0, high, f0 * low);
f1 = Vector128_.FusedMultiplyAdd(f1, high, f1 * low);
f2 = Vector128_.FusedMultiplyAdd(f2, high, f2 * low);
f3 = Vector128_.FusedMultiplyAdd(f3, high, f3 * low);
ref Vector128<float> d = ref Unsafe.Add(ref destinationBase, i * 4);
@ -834,6 +857,19 @@ internal static partial class SimdUtils
internal static void NormalizedFloatToByteSaturateReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> destination)
=> FloatToByteSaturateReduce(ref source, ref destination, byte.MaxValue);
/// <summary>
/// Converts as many scaled floating-point values as possible to bytes and retains the unconverted remainder.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="destination">The destination buffer.</param>
/// <param name="scaleFactor">The factor applied before conversion.</param>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void FloatToByteSaturateReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> destination,
float scaleFactor)
{
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!");
@ -858,9 +894,10 @@ internal static partial class SimdUtils
if (adjustedCount > 0)
{
NormalizedFloatToByteSaturate(
FloatToByteSaturate(
source[..adjustedCount],
destination[..adjustedCount]);
destination[..adjustedCount],
scaleFactor);
source = source[adjustedCount..];
destination = destination[adjustedCount..];
@ -880,6 +917,18 @@ internal static partial class SimdUtils
internal static void NormalizedFloatToByteSaturate(
ReadOnlySpan<float> source,
Span<byte> destination)
=> FloatToByteSaturate(source, destination, byte.MaxValue);
/// <summary>
/// Converts scaled floating-point values to bytes using saturating round-to-nearest with midpoint values away from zero.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="destination">The destination buffer.</param>
/// <param name="scaleFactor">The factor applied before conversion.</param>
internal static void FloatToByteSaturate(
ReadOnlySpan<float> source,
Span<byte> destination,
float scaleFactor)
{
if (Vector512.IsHardwareAccelerated && Avx512BW.IsSupported)
{
@ -890,7 +939,7 @@ internal static partial class SimdUtils
ref Vector512<float> sourceBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref Vector512<byte> destinationBase = ref Unsafe.As<byte, Vector512<byte>>(ref MemoryMarshal.GetReference(destination));
Vector512<float> scale = Vector512.Create((float)byte.MaxValue);
Vector512<float> scale = Vector512.Create(scaleFactor);
Vector512<int> mask = PermuteMaskDeinterleave16x32();
for (nuint i = 0; i < n; i++)
@ -902,10 +951,10 @@ internal static partial class SimdUtils
Vector512<float> f2 = scale * Unsafe.Add(ref s, 2);
Vector512<float> f3 = scale * Unsafe.Add(ref s, 3);
Vector512<int> w0 = Vector512_.ConvertToInt32RoundToEven(f0);
Vector512<int> w1 = Vector512_.ConvertToInt32RoundToEven(f1);
Vector512<int> w2 = Vector512_.ConvertToInt32RoundToEven(f2);
Vector512<int> w3 = Vector512_.ConvertToInt32RoundToEven(f3);
Vector512<int> w0 = Vector512_.ConvertToInt32RoundAwayFromZero(f0);
Vector512<int> w1 = Vector512_.ConvertToInt32RoundAwayFromZero(f1);
Vector512<int> w2 = Vector512_.ConvertToInt32RoundAwayFromZero(f2);
Vector512<int> w3 = Vector512_.ConvertToInt32RoundAwayFromZero(f3);
Vector512<short> u0 = Avx512BW.PackSignedSaturate(w0, w1);
Vector512<short> u1 = Avx512BW.PackSignedSaturate(w2, w3);
@ -924,7 +973,7 @@ internal static partial class SimdUtils
ref Vector256<float> sourceBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref Vector256<byte> destinationBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(destination));
Vector256<float> scale = Vector256.Create((float)byte.MaxValue);
Vector256<float> scale = Vector256.Create(scaleFactor);
Vector256<int> mask = PermuteMaskDeinterleave8x32();
for (nuint i = 0; i < n; i++)
@ -936,10 +985,10 @@ internal static partial class SimdUtils
Vector256<float> f2 = scale * Unsafe.Add(ref s, 2);
Vector256<float> f3 = scale * Unsafe.Add(ref s, 3);
Vector256<int> w0 = Vector256_.ConvertToInt32RoundToEven(f0);
Vector256<int> w1 = Vector256_.ConvertToInt32RoundToEven(f1);
Vector256<int> w2 = Vector256_.ConvertToInt32RoundToEven(f2);
Vector256<int> w3 = Vector256_.ConvertToInt32RoundToEven(f3);
Vector256<int> w0 = Vector256_.ConvertToInt32RoundAwayFromZero(f0);
Vector256<int> w1 = Vector256_.ConvertToInt32RoundAwayFromZero(f1);
Vector256<int> w2 = Vector256_.ConvertToInt32RoundAwayFromZero(f2);
Vector256<int> w3 = Vector256_.ConvertToInt32RoundAwayFromZero(f3);
Vector256<short> u0 = Avx2.PackSignedSaturate(w0, w1);
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3);
@ -959,7 +1008,7 @@ internal static partial class SimdUtils
ref Vector128<float> sourceBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destinationBase = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
Vector128<float> scale = Vector128.Create((float)byte.MaxValue);
Vector128<float> scale = Vector128.Create(scaleFactor);
Vector128<int> min = Vector128<int>.Zero;
Vector128<int> max = Vector128.Create((int)byte.MaxValue);
@ -972,10 +1021,10 @@ internal static partial class SimdUtils
Vector128<float> f2 = scale * Unsafe.Add(ref s, 2);
Vector128<float> f3 = scale * Unsafe.Add(ref s, 3);
Vector128<int> w0 = Vector128_.ConvertToInt32RoundToEven(f0);
Vector128<int> w1 = Vector128_.ConvertToInt32RoundToEven(f1);
Vector128<int> w2 = Vector128_.ConvertToInt32RoundToEven(f2);
Vector128<int> w3 = Vector128_.ConvertToInt32RoundToEven(f3);
Vector128<int> w0 = Vector128_.ConvertToInt32RoundAwayFromZero(f0);
Vector128<int> w1 = Vector128_.ConvertToInt32RoundAwayFromZero(f1);
Vector128<int> w2 = Vector128_.ConvertToInt32RoundAwayFromZero(f2);
Vector128<int> w3 = Vector128_.ConvertToInt32RoundAwayFromZero(f3);
w0 = Vector128_.Clamp(w0, min, max);
w1 = Vector128_.Clamp(w1, min, max);

82
src/ImageSharp/Common/Helpers/Vector128Utilities.cs

@ -307,11 +307,37 @@ internal static class Vector128_
return Vector128.ConvertToInt32(val_2p23_f32 | sign);
}
/// <summary>
/// Converts all values in <paramref name="vector"/> to signed 32-bit integers, rounding midpoint values away from zero.
/// </summary>
/// <param name="vector">The values to convert.</param>
/// <returns>The converted integer values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<int> ConvertToInt32RoundAwayFromZero(Vector128<float> vector)
{
if (Sse2.IsSupported)
{
// The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero.
Vector128<float> x86Adjustment = Vector128.Create(.5F) | (vector & Vector128.Create(-0F));
return Sse2.ConvertToVector128Int32WithTruncation(vector + x86Adjustment);
}
if (AdvSimd.IsSupported)
{
return AdvSimd.ConvertToInt32RoundAwayFromZero(vector);
}
Vector128<float> sign = vector & Vector128.Create(-0F);
Vector128<float> fallbackAdjustment = Vector128.Create(.5F) | sign;
return Vector128.ConvertToInt32(vector + fallbackAdjustment);
}
/// <summary>
/// Rounds all values in <paramref name="vector"/> to the nearest integer
/// following <see cref="MidpointRounding.ToEven"/> semantics.
/// </summary>
/// <param name="vector">The vector</param>
/// <param name="vector">The vector.</param>
/// <returns>The vector with each value rounded to the nearest integer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> RoundToNearestInteger(Vector128<float> vector)
{
@ -338,30 +364,58 @@ internal static class Vector128_
}
/// <summary>
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
/// Computes an estimate of (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.
/// </summary>
/// <remarks>ret = (vm0 * vm1) + va</remarks>
/// <param name="va">The vector to add to the intermediate result.</param>
/// <param name="vm0">The first vector to multiply.</param>
/// <param name="vm1">The second vector to multiply.</param>
/// <returns>The <see cref="Vector256{T}"/>.</returns>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>An estimate of the multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> MultiplyAdd(
Vector128<float> va,
Vector128<float> vm0,
Vector128<float> vm1)
public static Vector128<float> MultiplyAddEstimate(Vector128<float> left, Vector128<float> right, Vector128<float> addend)
{
if (Fma.IsSupported)
{
return Fma.MultiplyAdd(vm1, vm0, va);
return Fma.MultiplyAdd(left, right, addend);
}
if (AdvSimd.IsSupported)
{
return AdvSimd.FusedMultiplyAdd(va, vm0, vm1);
return AdvSimd.FusedMultiplyAdd(addend, left, right);
}
return va + (vm0 * vm1);
return (left * right) + addend;
}
/// <summary>
/// Computes (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>, rounded as one ternary operation.
/// </summary>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>The fused multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> FusedMultiplyAdd(Vector128<float> left, Vector128<float> right, Vector128<float> addend)
{
if (Fma.IsSupported)
{
return Fma.MultiplyAdd(left, right, addend);
}
if (AdvSimd.IsSupported)
{
return AdvSimd.FusedMultiplyAdd(addend, left, right);
}
// WebAssembly SIMD has no exact fused multiply-add, so match the runtime fallback by preserving fused rounding per element.
Vector64<float> lower = Vector64.Create(
MathF.FusedMultiplyAdd(left.GetElement(0), right.GetElement(0), addend.GetElement(0)),
MathF.FusedMultiplyAdd(left.GetElement(1), right.GetElement(1), addend.GetElement(1)));
Vector64<float> upper = Vector64.Create(
MathF.FusedMultiplyAdd(left.GetElement(2), right.GetElement(2), addend.GetElement(2)),
MathF.FusedMultiplyAdd(left.GetElement(3), right.GetElement(3), addend.GetElement(3)));
return Vector128.Create(lower, upper);
}
/// <summary>

68
src/ImageSharp/Common/Helpers/Vector256Utilities.cs

@ -73,11 +73,32 @@ internal static class Vector256_
return Vector256.ConvertToInt32(val_2p23_f32 | sign);
}
/// <summary>
/// Converts all values in <paramref name="vector"/> to signed 32-bit integers, rounding midpoint values away from zero.
/// </summary>
/// <param name="vector">The values to convert.</param>
/// <returns>The converted integer values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<int> ConvertToInt32RoundAwayFromZero(Vector256<float> vector)
{
if (Avx.IsSupported)
{
// The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero.
Vector256<float> x86Adjustment = Vector256.Create(.5F) | (vector & Vector256.Create(-0F));
return Avx.ConvertToVector256Int32WithTruncation(vector + x86Adjustment);
}
Vector256<float> sign = vector & Vector256.Create(-0F);
Vector256<float> fallbackAdjustment = Vector256.Create(.5F) | sign;
return Vector256.ConvertToInt32(vector + fallbackAdjustment);
}
/// <summary>
/// Rounds all values in <paramref name="vector"/> to the nearest integer
/// following <see cref="MidpointRounding.ToEven"/> semantics.
/// </summary>
/// <param name="vector">The vector</param>
/// <param name="vector">The vector.</param>
/// <returns>The vector with each value rounded to the nearest integer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> RoundToNearestInteger(Vector256<float> vector)
{
@ -94,25 +115,46 @@ internal static class Vector256_
}
/// <summary>
/// Performs a multiplication and an addition of the <see cref="Vector256{Single}"/>.
/// Computes an estimate of (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.
/// </summary>
/// <remarks>ret = (vm0 * vm1) + va</remarks>
/// <param name="va">The vector to add to the intermediate result.</param>
/// <param name="vm0">The first vector to multiply.</param>
/// <param name="vm1">The second vector to multiply.</param>
/// <returns>The <see cref="Vector256{T}"/>.</returns>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>An estimate of the multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> MultiplyAdd(
Vector256<float> va,
Vector256<float> vm0,
Vector256<float> vm1)
public static Vector256<float> MultiplyAddEstimate(Vector256<float> left, Vector256<float> right, Vector256<float> addend)
{
if (Fma.IsSupported)
{
return Fma.MultiplyAdd(vm0, vm1, va);
return Fma.MultiplyAdd(left, right, addend);
}
return va + (vm0 * vm1);
Vector128<float> lower = Vector128_.MultiplyAddEstimate(left.GetLower(), right.GetLower(), addend.GetLower());
Vector128<float> upper = Vector128_.MultiplyAddEstimate(left.GetUpper(), right.GetUpper(), addend.GetUpper());
return Vector256.Create(lower, upper);
}
/// <summary>
/// Computes (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>, rounded as one ternary operation.
/// </summary>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>The fused multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> FusedMultiplyAdd(Vector256<float> left, Vector256<float> right, Vector256<float> addend)
{
if (Fma.IsSupported)
{
return Fma.MultiplyAdd(left, right, addend);
}
// Match the runtime fallback by recursively applying the same fused contract to both halves.
Vector128<float> lower = Vector128_.FusedMultiplyAdd(left.GetLower(), right.GetLower(), addend.GetLower());
Vector128<float> upper = Vector128_.FusedMultiplyAdd(left.GetUpper(), right.GetUpper(), addend.GetUpper());
return Vector256.Create(lower, upper);
}
/// <summary>

74
src/ImageSharp/Common/Helpers/Vector512Utilities.cs

@ -59,33 +59,75 @@ internal static class Vector512_
public static Vector512<int> ConvertToInt32RoundToEven(Vector512<float> vector)
=> Avx512F.ConvertToVector512Int32(vector);
/// <summary>
/// Converts all values in <paramref name="vector"/> to signed 32-bit integers, rounding midpoint values away from zero.
/// </summary>
/// <param name="vector">The values to convert.</param>
/// <returns>The converted integer values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<int> ConvertToInt32RoundAwayFromZero(Vector512<float> vector)
{
// The x86 conversion truncates, so adding one half with each lane's sign implements round-to-nearest with midpoint values away from zero.
Vector512<float> half = Vector512.Create(.5F) | (vector & Vector512.Create(-0F));
return Avx512F.ConvertToVector512Int32WithTruncation(vector + half);
}
/// <summary>
/// Rounds all values in <paramref name="vector"/> to the nearest integer
/// following <see cref="MidpointRounding.ToEven"/> semantics.
/// </summary>
/// <param name="vector">The vector</param>
/// <param name="vector">The vector.</param>
/// <returns>The vector with each value rounded to the nearest integer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> RoundToNearestInteger(Vector512<float> vector)
// imm8 = 0b1000:
// imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers)
// imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions)
=> Avx512F.RoundScale(vector, 0b0000_1000);
// imm8 = 0b1000:
// imm8[7:4] = 0b0000 -> preserve 0 fractional bits (round to whole numbers)
// imm8[3:0] = 0b1000 -> _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (round to nearest even, suppress exceptions)
=> Avx512F.RoundScale(vector, 0b0000_1000);
/// <summary>
/// Performs a multiplication and an addition of the <see cref="Vector512{Single}"/>.
/// Computes an estimate of (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.
/// </summary>
/// <remarks>ret = (vm0 * vm1) + va</remarks>
/// <param name="va">The vector to add to the intermediate result.</param>
/// <param name="vm0">The first vector to multiply.</param>
/// <param name="vm1">The second vector to multiply.</param>
/// <returns>The <see cref="Vector256{T}"/>.</returns>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>An estimate of the multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> MultiplyAdd(
Vector512<float> va,
Vector512<float> vm0,
Vector512<float> vm1)
=> Avx512F.FusedMultiplyAdd(vm0, vm1, va);
public static Vector512<float> MultiplyAddEstimate(Vector512<float> left, Vector512<float> right, Vector512<float> addend)
{
if (Avx512F.IsSupported)
{
return Avx512F.FusedMultiplyAdd(left, right, addend);
}
Vector256<float> lower = Vector256_.MultiplyAddEstimate(left.GetLower(), right.GetLower(), addend.GetLower());
Vector256<float> upper = Vector256_.MultiplyAddEstimate(left.GetUpper(), right.GetUpper(), addend.GetUpper());
return Vector512.Create(lower, upper);
}
/// <summary>
/// Computes (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>, rounded as one ternary operation.
/// </summary>
/// <param name="left">The first vector to multiply.</param>
/// <param name="right">The second vector to multiply.</param>
/// <param name="addend">The vector to add to the product.</param>
/// <returns>The fused multiplication and addition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> FusedMultiplyAdd(Vector512<float> left, Vector512<float> right, Vector512<float> addend)
{
if (Avx512F.IsSupported)
{
return Avx512F.FusedMultiplyAdd(left, right, addend);
}
// Match the runtime fallback by recursively applying the same fused contract to both halves.
Vector256<float> lower = Vector256_.FusedMultiplyAdd(left.GetLower(), right.GetLower(), addend.GetLower());
Vector256<float> upper = Vector256_.FusedMultiplyAdd(left.GetUpper(), right.GetUpper(), addend.GetUpper());
return Vector512.Create(lower, upper);
}
/// <summary>
/// Performs a multiplication and a negated addition of the <see cref="Vector512{Single}"/>.

10
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -350,10 +350,10 @@ internal sealed class BmpDecoderCore : ImageDecoderCore
pixelRow[x] = this.rleSkippedPixelHandling switch
{
RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4])),
RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero),
RleSkippedPixelHandling.Transparent => TPixel.FromUnassociatedScaledVector4(Vector4.Zero),
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
_ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
_ => TPixel.FromUnassociatedScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
};
}
else
@ -411,10 +411,10 @@ internal sealed class BmpDecoderCore : ImageDecoderCore
pixelRow[x] = this.rleSkippedPixelHandling switch
{
RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])),
RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero),
RleSkippedPixelHandling.Transparent => TPixel.FromUnassociatedScaledVector4(Vector4.Zero),
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
_ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
_ => TPixel.FromUnassociatedScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
};
}
else
@ -1272,7 +1272,7 @@ internal sealed class BmpDecoderCore : ImageDecoderCore
g * invMaxValueGreen,
b * invMaxValueBlue,
alpha);
pixelRow[x] = TPixel.FromScaledVector4(vector4);
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(vector4);
}
else
{

4
src/ImageSharp/Formats/EncodingUtilities.cs

@ -65,9 +65,9 @@ internal static class EncodingUtilities
for (int y = 0; y < region.Height; y++)
{
Span<TPixel> span = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, span, vectorsSpan, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.ToVector4(configuration, span, vectorsSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
ReplaceTransparentPixels(vectorsSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorsSpan, span, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorsSpan, span, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
}

13
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -4,6 +4,7 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.ImageSharp.Formats.Exr.Compression;
@ -208,8 +209,10 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
for (int x = 0; x < width; x++)
{
HalfVector4 pixelValue = new(redPixelData[x], greenPixelData[x], bluePixelData[x], hasAlpha ? alphaPixelData[x] : 1.0f);
pixelRow[x] = TPixel.FromVector4(pixelValue.ToVector4());
Vector4 pixelValue = new(redPixelData[x], greenPixelData[x], bluePixelData[x], hasAlpha ? alphaPixelData[x] : 1F);
// OpenEXR channels are associated color values, not values in the destination pixel format's native numeric range.
pixelRow[x] = TPixel.FromAssociatedScaledVector4(pixelValue);
}
decodedRows++;
@ -288,7 +291,9 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
for (int x = 0; x < width; x++)
{
Rgba128 pixelValue = new(redPixelData[x], greenPixelData[x], bluePixelData[x], hasAlpha ? alphaPixelData[x] : uint.MaxValue);
pixelRow[x] = TPixel.FromVector4(pixelValue.ToVector4());
// Rgba128 normalizes the unsigned channel values before the destination applies its own numeric representation.
pixelRow[x] = TPixel.FromAssociatedScaledVector4(pixelValue.ToVector4());
}
decodedRows++;
@ -644,7 +649,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
this.Channels = this.HeaderAttributes.Channels;
this.Compression = this.HeaderAttributes.Compression;
uint rowsPerBlock = ExrUtils.RowsPerBlock(this.Compression);
long chunkCount = (this.Height + (long)rowsPerBlock - 1) / rowsPerBlock;
long chunkCount = (this.Height + rowsPerBlock - 1) / rowsPerBlock;
long offsetTableByteCount = chunkCount * sizeof(ulong);
// The scanline offset table sits between the header and pixel chunks; proving it

7
src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

@ -206,7 +206,9 @@ internal sealed class ExrEncoderCore
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan((int)rowIndex);
for (int x = 0; x < width; x++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
// OpenEXR stores RGB associated with alpha. Use the native vector domain so floating-point and HDR component
// ranges are preserved instead of being clamped through the scaled [0, 1] representation.
Vector4 vector4 = pixelRowSpan[x].ToAssociatedVector4();
redBuffer[x] = vector4.X;
greenBuffer[x] = vector4.Y;
blueBuffer[x] = vector4.Z;
@ -303,7 +305,8 @@ internal sealed class ExrEncoderCore
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan((int)rowIndex);
for (int x = 0; x < width; x++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
// OpenEXR channels use associated alpha; the native vector conversion also preserves the integer channel range.
Vector4 vector4 = pixelRowSpan[x].ToAssociatedVector4();
rgb = Rgba128.FromVector4(vector4);
redBuffer[x] = rgb.R;

3
src/ImageSharp/Formats/Exr/ExrMetadata.cs

@ -53,7 +53,8 @@ public class ExrMetadata : IFormatMetadata<ExrMetadata>
bitsPerPixel = hasAlpha ? bitsPerComponent * 4 : bitsPerComponent * 3;
}
PixelAlphaRepresentation alpha = hasAlpha ? PixelAlphaRepresentation.Unassociated : PixelAlphaRepresentation.None;
// OpenEXR defines RGBA color channels as premultiplied by alpha, so expose the association stored by the format.
PixelAlphaRepresentation alpha = hasAlpha ? PixelAlphaRepresentation.Associated : PixelAlphaRepresentation.None;
PixelColorType color = PixelColorType.RGB;
int componentsCount = 0;

2
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -541,7 +541,7 @@ internal sealed class GifEncoderCore
int index = -1;
if (quantized != null)
{
TPixel transparentPixel = TPixel.FromScaledVector4(Vector4.Zero);
TPixel transparentPixel = TPixel.FromUnassociatedScaledVector4(Vector4.Zero);
ReadOnlySpan<TPixel> palette = quantized.Palette.Span;
// Transparent pixels are much more likely to be found at the end of a palette.

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs

@ -74,7 +74,7 @@ internal abstract partial class JpegColorConverterBase
ref Vector128<float> b = ref Unsafe.Add(ref srcBlue, i);
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
Unsafe.Add(ref destLuminance, i) = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Unsafe.Add(ref destLuminance, i) = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b));
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs

@ -74,7 +74,7 @@ internal abstract partial class JpegColorConverterBase
ref Vector256<float> b = ref Unsafe.Add(ref srcBlue, i);
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
Unsafe.Add(ref destLuminance, i) = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Unsafe.Add(ref destLuminance, i) = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b));
}
}
}

2
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs

@ -74,7 +74,7 @@ internal abstract partial class JpegColorConverterBase
ref Vector512<float> b = ref Unsafe.Add(ref srcBlue, i);
// luminosity = (0.299 * r) + (0.587 * g) + (0.114 * b)
Unsafe.Add(ref destLuminance, i) = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Unsafe.Add(ref destLuminance, i) = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b));
}
}

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector128.cs

@ -53,9 +53,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector128<float> r = Vector128_.MultiplyAdd(y, cr, rCrMult) * scaledK;
Vector128<float> g = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult) * scaledK;
Vector128<float> b = Vector128_.MultiplyAdd(y, cb, bCbMult) * scaledK;
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK;
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK;
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK;
c0 = r;
c1 = g;
@ -117,9 +117,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector128<float> y = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector128<float> y = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y * maxSampleValue;
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue);

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector256.cs

@ -53,9 +53,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector256<float> r = Vector256_.MultiplyAdd(y, cr, rCrMult) * scaledK;
Vector256<float> g = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult) * scaledK;
Vector256<float> b = Vector256_.MultiplyAdd(y, cb, bCbMult) * scaledK;
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK;
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK;
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK;
c0 = r;
c1 = g;
@ -117,9 +117,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector256<float> y = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector256<float> y = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y * maxSampleValue;
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue);

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.TiffYccKVector512.cs

@ -57,9 +57,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector512<float> r = Vector512_.MultiplyAdd(y, cr, rCrMult) * scaledK;
Vector512<float> g = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult) * scaledK;
Vector512<float> b = Vector512_.MultiplyAdd(y, cb, bCbMult) * scaledK;
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y) * scaledK;
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y)) * scaledK;
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y) * scaledK;
c0 = r;
c1 = g;
@ -128,9 +128,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector512<float> y = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y * maxSampleValue;
Unsafe.Add(ref destCb, i) = chromaOffset + (cb * maxSampleValue);

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector128.cs

@ -53,9 +53,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector128<float> r = Vector128_.MultiplyAdd(y, cr, rCrMult);
Vector128<float> g = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector128<float> b = Vector128_.MultiplyAdd(y, cb, bCbMult);
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y);
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y));
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y);
r = Vector128_.RoundToNearestInteger(r) * scale;
g = Vector128_.RoundToNearestInteger(g) * scale;
@ -108,9 +108,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector128<float> y = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector128<float> y = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector256.cs

@ -53,9 +53,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector256<float> r = Vector256_.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> b = Vector256_.MultiplyAdd(y, cb, bCbMult);
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y);
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y));
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y);
r = Vector256_.RoundToNearestInteger(r) * scale;
g = Vector256_.RoundToNearestInteger(g) * scale;
@ -108,9 +108,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector256<float> y = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector256<float> y = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector512.cs

@ -56,9 +56,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector512<float> r = Vector512_.MultiplyAdd(y, cr, rCrMult);
Vector512<float> g = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector512<float> b = Vector512_.MultiplyAdd(y, cb, bCbMult);
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y);
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y));
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y);
r = Vector512_.RoundToNearestInteger(r) * scale;
g = Vector512_.RoundToNearestInteger(g) * scale;
@ -107,9 +107,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector512<float> y = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector128.cs

@ -58,9 +58,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector128<float> r = Vector128_.MultiplyAdd(y, cr, rCrMult);
Vector128<float> g = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector128<float> b = Vector128_.MultiplyAdd(y, cb, bCbMult);
Vector128<float> r = Vector128_.MultiplyAddEstimate(cr, rCrMult, y);
Vector128<float> g = Vector128_.MultiplyAddEstimate(cr, gCrMult, Vector128_.MultiplyAddEstimate(cb, gCbMult, y));
Vector128<float> b = Vector128_.MultiplyAddEstimate(cb, bCbMult, y);
r = max - Vector128_.RoundToNearestInteger(r);
g = max - Vector128_.RoundToNearestInteger(g);
@ -122,9 +122,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector128<float> y = Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAdd(Vector128_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector128<float> y = Vector128_.MultiplyAddEstimate(f0299, r, Vector128_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector128<float> cb = chromaOffset + Vector128_.MultiplyAddEstimate(fn0168736, r, Vector128_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector128<float> cr = chromaOffset + Vector128_.MultiplyAddEstimate(f05, r, Vector128_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector256.cs

@ -58,9 +58,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector256<float> r = Vector256_.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> b = Vector256_.MultiplyAdd(y, cb, bCbMult);
Vector256<float> r = Vector256_.MultiplyAddEstimate(cr, rCrMult, y);
Vector256<float> g = Vector256_.MultiplyAddEstimate(cr, gCrMult, Vector256_.MultiplyAddEstimate(cb, gCbMult, y));
Vector256<float> b = Vector256_.MultiplyAddEstimate(cb, bCbMult, y);
r = max - Vector256_.RoundToNearestInteger(r);
g = max - Vector256_.RoundToNearestInteger(g);
@ -122,9 +122,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector256<float> y = Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAdd(Vector256_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector256<float> y = Vector256_.MultiplyAddEstimate(f0299, r, Vector256_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector256<float> cb = chromaOffset + Vector256_.MultiplyAddEstimate(fn0168736, r, Vector256_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector256<float> cr = chromaOffset + Vector256_.MultiplyAddEstimate(f05, r, Vector256_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

12
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector512.cs

@ -58,9 +58,9 @@ internal abstract partial class JpegColorConverterBase
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
Vector512<float> r = Vector512_.MultiplyAdd(y, cr, rCrMult);
Vector512<float> g = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector512<float> b = Vector512_.MultiplyAdd(y, cb, bCbMult);
Vector512<float> r = Vector512_.MultiplyAddEstimate(cr, rCrMult, y);
Vector512<float> g = Vector512_.MultiplyAddEstimate(cr, gCrMult, Vector512_.MultiplyAddEstimate(cb, gCbMult, y));
Vector512<float> b = Vector512_.MultiplyAddEstimate(cb, bCbMult, y);
r = max - Vector512_.RoundToNearestInteger(r);
g = max - Vector512_.RoundToNearestInteger(g);
@ -126,9 +126,9 @@ internal abstract partial class JpegColorConverterBase
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
Vector512<float> y = Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f0114 * b, f0587, g), f0299, r);
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(f05 * b, fn0331264, g), fn0168736, r);
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAdd(Vector512_.MultiplyAdd(fn0081312F * b, fn0418688, g), f05, r);
Vector512<float> y = Vector512_.MultiplyAddEstimate(f0299, r, Vector512_.MultiplyAddEstimate(f0587, g, f0114 * b));
Vector512<float> cb = chromaOffset + Vector512_.MultiplyAddEstimate(fn0168736, r, Vector512_.MultiplyAddEstimate(fn0331264, g, f05 * b));
Vector512<float> cr = chromaOffset + Vector512_.MultiplyAddEstimate(f05, r, Vector512_.MultiplyAddEstimate(fn0418688, g, fn0081312F * b));
Unsafe.Add(ref destY, i) = y;
Unsafe.Add(ref destCb, i) = cb;

8
src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Vector256.cs

@ -55,8 +55,8 @@ internal static partial class FloatingPointDCT
tmp12 = tmp6 + tmp7;
Vector256<float> z5 = (tmp10 - tmp12) * Vector256.Create(0.382683433f); // mm256_F_0_3826
Vector256<float> z2 = Vector256_.MultiplyAdd(z5, Vector256.Create(0.541196100f), tmp10); // mm256_F_0_5411
Vector256<float> z4 = Vector256_.MultiplyAdd(z5, Vector256.Create(1.306562965f), tmp12); // mm256_F_1_3065
Vector256<float> z2 = Vector256_.MultiplyAddEstimate(Vector256.Create(0.541196100f), tmp10, z5); // mm256_F_0_5411
Vector256<float> z4 = Vector256_.MultiplyAddEstimate(Vector256.Create(1.306562965f), tmp12, z5); // mm256_F_1_3065
Vector256<float> z3 = tmp11 * mm256_F_0_7071;
Vector256<float> z11 = tmp7 + z3;
@ -122,8 +122,8 @@ internal static partial class FloatingPointDCT
z5 = (z10 + z12) * Vector256.Create(1.847759065f); // mm256_F_1_8477
tmp10 = Vector256_.MultiplyAdd(z5, z12, Vector256.Create(-1.082392200f)); // mm256_F_n1_0823
tmp12 = Vector256_.MultiplyAdd(z5, z10, Vector256.Create(-2.613125930f)); // mm256_F_n2_6131
tmp10 = Vector256_.MultiplyAddEstimate(z12, Vector256.Create(-1.082392200f), z5); // mm256_F_n1_0823
tmp12 = Vector256_.MultiplyAddEstimate(z10, Vector256.Create(-2.613125930f), z5); // mm256_F_n2_6131
tmp6 = tmp12 - tmp7;
tmp5 = tmp11 - tmp6;

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs

@ -34,7 +34,7 @@ internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{
L16 l16 = TiffUtilities.L16Default;
TPixel color = TPixel.FromScaledVector4(Vector4.Zero);
TPixel color = TPixel.FromUnassociatedScaledVector4(Vector4.Zero);
int offset = 0;
for (int y = top; y < top + height; y++)

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs

@ -40,7 +40,7 @@ internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = BitConverter.ToSingle(buffer);
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
}
}
else
@ -50,7 +50,7 @@ internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
}
}
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs

@ -36,7 +36,7 @@ internal class BlackIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = value / this.factor;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
}
bitReader.NextRow();

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16PlanarTiffColor{TPixel}.cs

@ -109,7 +109,7 @@ internal class CieLab16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPix
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow);
Rgb.ToScaledVector4(rgbRow, vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
return;
@ -138,7 +138,7 @@ internal class CieLab16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPix
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow);
Rgb.ToScaledVector4(rgbRow, vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
}
}

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16TiffColor{TPixel}.cs

@ -105,7 +105,7 @@ internal class CieLab16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow);
Rgb.ToScaledVector4(rgbRow, vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
return;
@ -134,7 +134,7 @@ internal class CieLab16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow);
Rgb.ToScaledVector4(rgbRow, vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
}
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8PlanarTiffColor{TPixel}.cs

@ -35,7 +35,7 @@ internal class CieLab8PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixe
{
CieLab lab = new((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]);
Rgb rgb = ColorProfileConverter.Convert<CieLab, Rgb>(in lab);
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
offset++;
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8TiffColor{TPixel}.cs

@ -39,7 +39,7 @@ internal class CieLab8TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float l = (data[offset] & 0xFF) * 100f * Inv255;
CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]);
Rgb rgb = ColorProfileConverter.Convert<CieLab, Rgb>(in lab);
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1f));
offset += 3;
}

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs

@ -66,7 +66,7 @@ internal class CmykTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++)
{
pixelRow[x] = TPixel.FromVector4(new Vector4(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, 1.0f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, 1.0f));
offset += 3;
}
@ -99,7 +99,7 @@ internal class CmykTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
// Convert CMYK -> RGB -> Vector4 -> TPixel
this.colorProfileConverter.Convert<Cmyk, Rgb>(cmykRow, rgbRow);
Rgb.ToScaledVector4(rgbRow, vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
}
}

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs

@ -97,7 +97,7 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
Vector4 color = this.vectorPallete[index];
color.W = alpha;
pixelRow[x] = TPixel.FromScaledVector4(color);
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(color);
// Best-effort palette update for downstream conversions.
// This is intentionally "last writer wins" with no per-pixel branch.
@ -163,7 +163,7 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float r = colorMap[rOffset + i] * InvMax;
float g = colorMap[gOffset + i] * InvMax;
float b = colorMap[bOffset + i] * InvMax;
palette[i] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
palette[i] = TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f));
}
return palette;

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs

@ -30,7 +30,7 @@ internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
byte b = (byte)((data[offset] & 0xF0) >> 4);
Bgra4444 bgra = new() { PackedValue = ToBgraPackedValue(b, g, r) };
pixelRow[x] = TPixel.FromScaledVector4(bgra.ToScaledVector4());
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(bgra.ToUnassociatedScaledVector4());
if (x + 1 >= pixelRow.Length)
{
offset++;
@ -44,7 +44,7 @@ internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
offset++;
bgra.PackedValue = ToBgraPackedValue(b, g, r);
pixelRow[x + 1] = TPixel.FromScaledVector4(bgra.ToScaledVector4());
pixelRow[x + 1] = TPixel.FromUnassociatedScaledVector4(bgra.ToUnassociatedScaledVector4());
}
}
}

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs

@ -51,7 +51,7 @@ internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float b = BitConverter.ToSingle(buffer);
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f));
}
}
else
@ -67,7 +67,7 @@ internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float b = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f));
}
}
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs

@ -63,7 +63,7 @@ internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f));
}
rBitReader.NextRow();

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs

@ -52,7 +52,7 @@ internal class RgbTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f));
}
bitReader.NextRow();

22
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs

@ -5,6 +5,7 @@
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -96,16 +97,21 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
int byteCount = pixelRow.Length * 8;
PixelOperations<TPixel>.Instance.FromRgba64Bytes(
this.configuration,
data.Slice(offset, byteCount),
pixelRow,
pixelRow.Length);
if (hasAssociatedAlpha)
{
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale);
// This little-endian branch can view the interleaved samples as Rgba64. The values are already associated, so
// the Rgba64 operation performs only unpacking and normalization before the destination consumes that representation.
ReadOnlySpan<Rgba64> associatedSamples = MemoryMarshal.Cast<byte, Rgba64>(data.Slice(offset, byteCount));
PixelOperations<Rgba64>.Instance.ToVector4(this.configuration, associatedSamples, vectorsSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply);
}
else
{
PixelOperations<TPixel>.Instance.FromRgba64Bytes(
this.configuration,
data.Slice(offset, byteCount),
pixelRow,
pixelRow.Length);
}
offset += byteCount;

21
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs

@ -4,6 +4,7 @@
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -41,16 +42,22 @@ internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
int byteCount = pixelRow.Length * 4;
PixelOperations<TPixel>.Instance.FromRgba32Bytes(
this.configuration,
data.Slice(offset, byteCount),
pixelRow,
pixelRow.Length);
if (hasAssociatedAlpha)
{
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale);
// The TIFF samples already contain associated RGB. Reinterpret them as Rgba32 only to normalize and unpack the
// channels; asking that unassociated storage type to associate them would multiply RGB a second time.
ReadOnlySpan<Rgba32> associatedSamples = MemoryMarshal.Cast<byte, Rgba32>(data.Slice(offset, byteCount));
PixelOperations<Rgba32>.Instance.ToVector4(this.configuration, associatedSamples, vectorsSpan, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply);
}
else
{
PixelOperations<TPixel>.Instance.FromRgba32Bytes(
this.configuration,
data.Slice(offset, byteCount),
pixelRow,
pixelRow.Length);
}
offset += byteCount;

20
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs

@ -16,16 +16,24 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
private readonly bool isBigEndian;
private readonly TiffExtraSampleType? extraSamplesType;
/// <summary>
/// Initializes a new instance of the <see cref="RgbaFloat32323232TiffColor{TPixel}" /> class.
/// </summary>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public RgbaFloat32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
/// <param name="extraSamplesType">The alpha representation declared by the TIFF extra samples field.</param>
public RgbaFloat32323232TiffColor(bool isBigEndian, TiffExtraSampleType? extraSamplesType)
{
this.isBigEndian = isBigEndian;
this.extraSamplesType = extraSamplesType;
}
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{
int offset = 0;
bool hasAssociatedAlpha = this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
Span<byte> buffer = stackalloc byte[4];
for (int y = top; y < top + height; y++)
@ -56,7 +64,10 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float a = BitConverter.ToSingle(buffer);
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, a));
Vector4 vector = new(r, g, b, a);
pixelRow[x] = hasAssociatedAlpha
? TPixel.FromAssociatedScaledVector4(vector)
: TPixel.FromUnassociatedScaledVector4(vector);
}
}
else
@ -75,7 +86,10 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float a = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, a));
Vector4 vector = new(r, g, b, a);
pixelRow[x] = hasAssociatedAlpha
? TPixel.FromAssociatedScaledVector4(vector)
: TPixel.FromUnassociatedScaledVector4(vector);
}
}
}

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs

@ -80,11 +80,11 @@ internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
Vector4 vector = new(r, g, b, a);
if (hasAssociatedAlpha)
{
pixelRow[x] = TiffUtilities.UnPremultiply<TPixel>(ref vector);
pixelRow[x] = TPixel.FromAssociatedScaledVector4(vector);
}
else
{
pixelRow[x] = TPixel.FromScaledVector4(vector);
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(vector);
}
}

6
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs

@ -63,16 +63,16 @@ internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float r = bitReader.ReadBits(this.bitsPerSampleR) / this.rFactor;
float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor;
float a = bitReader.ReadBits(this.bitsPerSampleA) / this.aFactor;
Vector4 vector = new(r, g, b, a);
if (hasAssociatedAlpha)
{
pixelRow[x] = TiffUtilities.UnPremultiply<TPixel>(ref vector);
pixelRow[x] = TPixel.FromAssociatedScaledVector4(vector);
}
else
{
pixelRow[x] = TPixel.FromScaledVector4(vector);
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(vector);
}
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs

@ -383,7 +383,7 @@ internal static class TiffColorDecoderFactory<TPixel>
&& bitsPerSample.Channel0 == 32,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbaFloat32323232TiffColor<TPixel>(isBigEndian: byteOrder == ByteOrder.BigEndian);
return new RgbaFloat32323232TiffColor<TPixel>(byteOrder == ByteOrder.BigEndian, extraSampleType);
case TiffColorType.PaletteColor:
DebugGuard.NotNull(colorMap, "colorMap");

4
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs

@ -40,7 +40,7 @@ internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = 1.0f - BitConverter.ToSingle(buffer);
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
}
}
else
@ -50,7 +50,7 @@ internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = 1.0f - BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4;
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
}
}
}

2
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs

@ -36,7 +36,7 @@ internal class WhiteIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{
int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = 1f - (value / this.factor);
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
pixelRow[x] = TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
}
bitReader.NextRow();

45
src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs

@ -40,72 +40,67 @@ internal static class TiffUtilities
public static TPixel ColorFromRgba64Premultiplied<TPixel>(ushort r, ushort g, ushort b, ushort a)
where TPixel : unmanaged, IPixel<TPixel>
{
// TIFF 6.0 requires associated RGB to be interpreted as zero when alpha is zero. Enforce that codec contract here
// while leaving the general associated-alpha conversion contract free to preserve hidden RGB for formats such as EXR.
if (a == 0)
{
return TPixel.FromRgba64(default);
return TPixel.FromAssociatedScaledVector4(Vector4.Zero);
}
float scale = 65535f / a;
ushort ur = (ushort)Math.Min(r * scale, 65535);
ushort ug = (ushort)Math.Min(g * scale, 65535);
ushort ub = (ushort)Math.Min(b * scale, 65535);
return TPixel.FromRgba64(new Rgba64(ur, ug, ub, a));
// TIFF marks these samples as already associated. Passing that representation directly avoids an unassociate/reassociate
// round trip for associated destinations while unassociated formats perform the required conversion in their pixel contract.
return TPixel.FromAssociatedScaledVector4(new Vector4(r, g, b, a) / ushort.MaxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(uint r, uint g, uint b)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(r, g, b, 1f) * Scale24BitVector);
=> TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f) * Scale24BitVector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(uint r, uint g, uint b, uint a)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(r, g, b, a) * Scale24Bit);
=> TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, a) * Scale24Bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24BitPremultiplied<TPixel>(uint r, uint g, uint b, uint a)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit;
return UnPremultiply<TPixel>(ref colorVector);
// TIFF 6.0 assigns no color to a fully transparent associated sample, even when malformed input stores nonzero RGB.
return a == 0
? TPixel.FromAssociatedScaledVector4(Vector4.Zero)
: TPixel.FromAssociatedScaledVector4(new Vector4(r, g, b, a) * Scale24Bit);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(uint r, uint g, uint b)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(r, g, b, 1f) * Scale32BitVector);
=> TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, 1f) * Scale32BitVector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(uint r, uint g, uint b, uint a)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(r, g, b, a) * Scale32Bit);
=> TPixel.FromUnassociatedScaledVector4(new Vector4(r, g, b, a) * Scale32Bit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32BitPremultiplied<TPixel>(uint r, uint g, uint b, uint a)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 vector = new Vector4(r, g, b, a) * Scale32Bit;
return UnPremultiply<TPixel>(ref vector);
// TIFF 6.0 assigns no color to a fully transparent associated sample, even when malformed input stores nonzero RGB.
return a == 0
? TPixel.FromAssociatedScaledVector4(Vector4.Zero)
: TPixel.FromAssociatedScaledVector4(new Vector4(r, g, b, a) * Scale32Bit);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(uint intensity)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale24BitVector);
=> TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale24BitVector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(uint intensity)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale32BitVector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel UnPremultiply<TPixel>(ref Vector4 vector)
where TPixel : unmanaged, IPixel<TPixel>
{
Numerics.UnPremultiply(ref vector);
return TPixel.FromScaledVector4(vector);
}
=> TPixel.FromUnassociatedScaledVector4(new Vector4(intensity, intensity, intensity, 1f) * Scale32BitVector);
/// <summary>
/// Finds the padding needed to round 'valueToRoundUp' to the next integer multiple of subSampling value.

18
src/ImageSharp/ImageSharp.csproj

@ -141,6 +141,16 @@
<AutoGen>True</AutoGen>
<DependentUpon>DefaultPixelBlenders.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelBlenders\AssociatedAlphaPixelBlenders.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>AssociatedAlphaPixelBlenders.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelBlenders\AssociatedAlphaPorterDuffFunctions.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>AssociatedAlphaPorterDuffFunctions.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\PixelBlenders\PorterDuffFunctions.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@ -221,6 +231,14 @@
<LastGenOutput>DefaultPixelBlenders.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="PixelFormats\PixelBlenders\AssociatedAlphaPixelBlenders.Generated.tt">
<LastGenOutput>AssociatedAlphaPixelBlenders.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="PixelFormats\PixelBlenders\AssociatedAlphaPorterDuffFunctions.Generated.tt">
<LastGenOutput>AssociatedAlphaPorterDuffFunctions.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Formats\_Generated\ImageExtensions.Save.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ImageExtensions.Save.cs</LastGenOutput>

387
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;
/// <summary>
/// Provides bulk operations for pixel formats that store associated alpha.
/// </summary>
/// <typeparam name="TPixel">The associated-alpha pixel format.</typeparam>
public abstract class AssociatedAlphaPixelOperations<TPixel> : PixelOperations<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <inheritdoc />
public override PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
=> AssociatedAlphaPixelBlenders<TPixel>.GetPixelBlender(colorMode, alphaMode);
/// <inheritdoc />
protected abstract override void ToUnassociatedVector4(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<Vector4> destination);
/// <inheritdoc />
protected abstract override void ToAssociatedVector4(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<Vector4> destination);
/// <inheritdoc />
protected abstract override void FromUnassociatedVector4Destructive(
Configuration configuration,
Span<Vector4> source,
Span<TPixel> destination);
/// <inheritdoc />
protected abstract override void FromAssociatedVector4Destructive(
Configuration configuration,
Span<Vector4> source,
Span<TPixel> destination);
/// <inheritdoc />
protected abstract override void ToUnassociatedScaledVector4(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<Vector4> destination);
/// <inheritdoc />
protected abstract override void ToAssociatedScaledVector4(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<Vector4> destination);
/// <inheritdoc />
protected abstract override void FromUnassociatedScaledVector4Destructive(
Configuration configuration,
Span<Vector4> source,
Span<TPixel> destination);
/// <inheritdoc />
protected abstract override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<TPixel> destination);
/// <inheritdoc />
public override void From<TSourcePixel>(
Configuration configuration,
ReadOnlySpan<TSourcePixel> source,
Span<TPixel> 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<Vector4> tempVectors = configuration.MemoryAllocator.Allocate<Vector4>(sliceLength);
Span<Vector4> 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<TSourcePixel> sourceSlice = source.Slice(start, sliceLength);
Span<TPixel> destinationSlice = destination.Slice(start, sliceLength);
PixelOperations<TSourcePixel>.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<TSourcePixel> sourceSlice = source[endOfCompleteSlices..];
Span<TPixel> destinationSlice = destination.Slice(endOfCompleteSlices, remainder);
vectorSpan = vectorSpan[..remainder];
PixelOperations<TSourcePixel>.Instance.ToVector4(
configuration,
sourceSlice,
vectorSpan,
PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
this.FromUnassociatedScaledVector4Destructive(configuration, vectorSpan, destinationSlice);
}
}
/// <inheritdoc />
public override void FromVector4Destructive(
Configuration configuration,
Span<Vector4> sourceVectors,
Span<TPixel> 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);
}
}
/// <inheritdoc />
public override void ToVector4(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<Vector4> 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<Vector4> 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);
}
}
/// <inheritdoc />
public override void FromArgb32(Configuration configuration, ReadOnlySpan<Argb32> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromAbgr32(Configuration configuration, ReadOnlySpan<Abgr32> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromBgr24(Configuration configuration, ReadOnlySpan<Bgr24> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromBgra32(Configuration configuration, ReadOnlySpan<Bgra32> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromL8(Configuration configuration, ReadOnlySpan<L8> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromL16(Configuration configuration, ReadOnlySpan<L16> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromLa16(Configuration configuration, ReadOnlySpan<La16> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromLa32(Configuration configuration, ReadOnlySpan<La32> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromRgb24(Configuration configuration, ReadOnlySpan<Rgb24> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromRgba32(Configuration configuration, ReadOnlySpan<Rgba32> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromRgb48(Configuration configuration, ReadOnlySpan<Rgb48> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromRgba64(Configuration configuration, ReadOnlySpan<Rgba64> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void FromBgra5551(Configuration configuration, ReadOnlySpan<Bgra5551> source, Span<TPixel> destination)
=> this.From(configuration, source, destination);
/// <inheritdoc />
public override void ToArgb32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Argb32> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToAbgr32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Abgr32> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToBgr24(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgr24> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToBgra32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgra32> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToL8(Configuration configuration, ReadOnlySpan<TPixel> source, Span<L8> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToL16(Configuration configuration, ReadOnlySpan<TPixel> source, Span<L16> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToLa16(Configuration configuration, ReadOnlySpan<TPixel> source, Span<La16> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToLa32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<La32> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToRgb24(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgb24> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToRgba32(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgba32> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToRgb48(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgb48> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToRgba64(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Rgba64> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <inheritdoc />
public override void ToBgra5551(Configuration configuration, ReadOnlySpan<TPixel> source, Span<Bgra5551> destination)
=> this.ConvertToUnassociated(configuration, source, destination);
/// <summary>
/// Converts associated source pixels to an unassociated destination format.
/// </summary>
/// <typeparam name="TDestinationPixel">The destination pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source pixels.</param>
/// <param name="destination">The destination pixels.</param>
private void ConvertToUnassociated<TDestinationPixel>(
Configuration configuration,
ReadOnlySpan<TPixel> source,
Span<TDestinationPixel> destination)
where TDestinationPixel : unmanaged, IPixel<TDestinationPixel>
{
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<Vector4> tempVectors = configuration.MemoryAllocator.Allocate<Vector4>(sliceLength);
Span<Vector4> vectorSpan = tempVectors.GetSpan()[..sliceLength];
PixelOperations<TDestinationPixel> destinationOperations = PixelOperations<TDestinationPixel>.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<TPixel> sourceSlice = source.Slice(start, sliceLength);
Span<TDestinationPixel> 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<TPixel> sourceSlice = source[endOfCompleteSlices..];
Span<TDestinationPixel> destinationSlice = destination.Slice(endOfCompleteSlices, remainder);
vectorSpan = vectorSpan[..remainder];
this.ToUnassociatedScaledVector4(configuration, sourceSlice, vectorSpan);
destinationOperations.FromVector4Destructive(configuration, vectorSpan, destinationSlice, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
}
}
}

323
src/ImageSharp/PixelFormats/HalfTypeHelper.cs

@ -1,7 +1,9 @@
// 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;
@ -10,6 +12,27 @@ namespace SixLabors.ImageSharp.PixelFormats;
/// </summary>
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;
/// <summary>
/// Packs a <see cref="float"/> into an <see cref="ushort"/>
/// </summary>
@ -25,4 +48,304 @@ internal static class HalfTypeHelper
/// <returns>The <see cref="float"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float Unpack(ushort value) => (float)BitConverter.UInt16BitsToHalf(value);
/// <summary>
/// Normalizes a finite binary16 value to the scaled pixel range.
/// </summary>
/// <param name="value">The native binary16 value represented as a <see cref="float"/>.</param>
/// <returns>The normalized value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float ToScaled(float value) => (value * InverseFiniteRange) + ScaledMidpoint;
/// <summary>
/// Normalizes finite binary16 values to the scaled pixel range.
/// </summary>
/// <param name="value">The native binary16 values.</param>
/// <returns>The normalized values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector2 ToScaled(Vector2 value) => (value * InverseFiniteRange) + new Vector2(ScaledMidpoint);
/// <summary>
/// Normalizes finite binary16 values to the scaled pixel range.
/// </summary>
/// <param name="value">The native binary16 values.</param>
/// <returns>The normalized values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector4 ToScaled(Vector4 value) => (value * InverseFiniteRange) + new Vector4(ScaledMidpoint);
/// <summary>
/// Expands a normalized value to the finite binary16 range.
/// </summary>
/// <param name="value">The normalized value.</param>
/// <returns>The native binary16 value represented as a <see cref="float"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float FromScaled(float value) => (value * FiniteRange) + FiniteMinimum;
/// <summary>
/// Expands normalized values to the finite binary16 range.
/// </summary>
/// <param name="value">The normalized values.</param>
/// <returns>The native binary16 values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector2 FromScaled(Vector2 value) => (value * FiniteRange) + new Vector2(FiniteMinimum);
/// <summary>
/// Expands normalized values to the finite binary16 range.
/// </summary>
/// <param name="value">The normalized values.</param>
/// <returns>The native binary16 values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector4 FromScaled(Vector4 value) => (value * FiniteRange) + new Vector4(FiniteMinimum);
/// <summary>
/// Unpacks eight binary16 values into two vectors of single-precision values.
/// </summary>
/// <param name="value">The packed binary16 values.</param>
/// <returns>The unpacked lower and upper values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static (Vector128<float> Lower, Vector128<float> Upper) Unpack(Vector128<ushort> value)
{
(Vector128<uint> lower, Vector128<uint> upper) = Vector128.Widen(value);
return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper));
}
/// <summary>
/// Unpacks sixteen binary16 values into two vectors of single-precision values.
/// </summary>
/// <param name="value">The packed binary16 values.</param>
/// <returns>The unpacked lower and upper values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static (Vector256<float> Lower, Vector256<float> Upper) Unpack(Vector256<ushort> value)
{
(Vector256<uint> lower, Vector256<uint> upper) = Vector256.Widen(value);
return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper));
}
/// <summary>
/// Unpacks thirty-two binary16 values into two vectors of single-precision values.
/// </summary>
/// <param name="value">The packed binary16 values.</param>
/// <returns>The unpacked lower and upper values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static (Vector512<float> Lower, Vector512<float> Upper) Unpack(Vector512<ushort> value)
{
(Vector512<uint> lower, Vector512<uint> upper) = Vector512.Widen(value);
return (ConvertHalfBitsToSingle(lower), ConvertHalfBitsToSingle(upper));
}
/// <summary>
/// Packs eight single-precision values into binary16 storage.
/// </summary>
/// <param name="lower">The lower single-precision values.</param>
/// <param name="upper">The upper single-precision values.</param>
/// <returns>The packed binary16 values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector128<ushort> Pack(Vector128<float> lower, Vector128<float> upper)
=> Vector128.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper));
/// <summary>
/// Packs sixteen single-precision values into binary16 storage.
/// </summary>
/// <param name="lower">The lower single-precision values.</param>
/// <param name="upper">The upper single-precision values.</param>
/// <returns>The packed binary16 values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector256<ushort> Pack(Vector256<float> lower, Vector256<float> upper)
=> Vector256.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper));
/// <summary>
/// Packs thirty-two single-precision values into binary16 storage.
/// </summary>
/// <param name="lower">The lower single-precision values.</param>
/// <param name="upper">The upper single-precision values.</param>
/// <returns>The packed binary16 values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector512<ushort> Pack(Vector512<float> lower, Vector512<float> upper)
=> Vector512.Narrow(ConvertSingleToHalfBits(lower), ConvertSingleToHalfBits(upper));
/// <summary>
/// Rounds single-precision values through binary16 without changing the vector width.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The values after binary16 quantization.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector128<float> RoundToHalf(Vector128<float> value)
=> ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value));
/// <summary>
/// Rounds single-precision values through binary16 without changing the vector width.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The values after binary16 quantization.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector256<float> RoundToHalf(Vector256<float> value)
=> ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value));
/// <summary>
/// Rounds single-precision values through binary16 without changing the vector width.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The values after binary16 quantization.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector512<float> RoundToHalf(Vector512<float> value)
=> ConvertHalfBitsToSingle(ConvertSingleToHalfBits(value));
/// <summary>
/// Converts zero-extended binary16 bit patterns to single-precision values.
/// </summary>
/// <param name="value">The binary16 bit patterns.</param>
/// <returns>The converted single-precision values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<float> ConvertHalfBitsToSingle(Vector128<uint> value)
{
Vector128<uint> sign = Vector128.ShiftLeft(value & Vector128.Create(HalfSignMask), 16);
Vector128<uint> exponent = value & Vector128.Create(HalfExponentMask);
Vector128<uint> subnormalMask = Vector128.Equals(exponent, Vector128<uint>.Zero);
Vector128<uint> infinityOrNaNMask = Vector128.Equals(exponent, Vector128.Create(HalfExponentMask));
Vector128<uint> maskedExponentLowerBound = subnormalMask & Vector128.Create(SingleExponentLowerBound);
Vector128<uint> 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<uint> bits = Vector128.ShiftLeft(value, 13) & Vector128.Create(HalfToSingleBitsMask);
exponentOffset = Vector128.ConditionalSelect(infinityOrNaNMask, Vector128.ShiftLeft(exponentOffset, 1), exponentOffset);
bits += exponentOffset;
Vector128<uint> absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32();
return (absoluteValue | sign).AsSingle();
}
/// <summary>
/// Converts zero-extended binary16 bit patterns to single-precision values.
/// </summary>
/// <param name="value">The binary16 bit patterns.</param>
/// <returns>The converted single-precision values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<float> ConvertHalfBitsToSingle(Vector256<uint> value)
{
Vector256<uint> sign = Vector256.ShiftLeft(value & Vector256.Create(HalfSignMask), 16);
Vector256<uint> exponent = value & Vector256.Create(HalfExponentMask);
Vector256<uint> subnormalMask = Vector256.Equals(exponent, Vector256<uint>.Zero);
Vector256<uint> infinityOrNaNMask = Vector256.Equals(exponent, Vector256.Create(HalfExponentMask));
Vector256<uint> maskedExponentLowerBound = subnormalMask & Vector256.Create(SingleExponentLowerBound);
Vector256<uint> 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<uint> bits = Vector256.ShiftLeft(value, 13) & Vector256.Create(HalfToSingleBitsMask);
exponentOffset = Vector256.ConditionalSelect(infinityOrNaNMask, Vector256.ShiftLeft(exponentOffset, 1), exponentOffset);
bits += exponentOffset;
Vector256<uint> absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32();
return (absoluteValue | sign).AsSingle();
}
/// <summary>
/// Converts zero-extended binary16 bit patterns to single-precision values.
/// </summary>
/// <param name="value">The binary16 bit patterns.</param>
/// <returns>The converted single-precision values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<float> ConvertHalfBitsToSingle(Vector512<uint> value)
{
Vector512<uint> sign = Vector512.ShiftLeft(value & Vector512.Create(HalfSignMask), 16);
Vector512<uint> exponent = value & Vector512.Create(HalfExponentMask);
Vector512<uint> subnormalMask = Vector512.Equals(exponent, Vector512<uint>.Zero);
Vector512<uint> infinityOrNaNMask = Vector512.Equals(exponent, Vector512.Create(HalfExponentMask));
Vector512<uint> maskedExponentLowerBound = subnormalMask & Vector512.Create(SingleExponentLowerBound);
Vector512<uint> 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<uint> bits = Vector512.ShiftLeft(value, 13) & Vector512.Create(HalfToSingleBitsMask);
exponentOffset = Vector512.ConditionalSelect(infinityOrNaNMask, Vector512.ShiftLeft(exponentOffset, 1), exponentOffset);
bits += exponentOffset;
Vector512<uint> absoluteValue = (bits.AsSingle() - maskedExponentLowerBound.AsSingle()).AsUInt32();
return (absoluteValue | sign).AsSingle();
}
/// <summary>
/// Converts single-precision values to zero-extended binary16 bit patterns.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The binary16 bit patterns in 32-bit lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<uint> ConvertSingleToHalfBits(Vector128<float> value)
{
Vector128<uint> bits = value.AsUInt32();
Vector128<uint> sign = Vector128.ShiftRightLogical(bits & Vector128.Create(SingleSignMask), 16);
Vector128<uint> realMask = Vector128.Equals(value, value).AsUInt32();
value = Vector128.Abs(value);
value = Vector128.Min(Vector128.Create(MaxHalfValueBelowInfinity), value);
Vector128<uint> 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<uint> newExponent = Vector128.ShiftRightLogical(bits, 13);
Vector128<uint> maskedHalfExponentForNaN = ~realMask & Vector128.Create(HalfExponentMask);
bits &= realMask;
bits += newExponent;
bits &= ~maskedHalfExponentForNaN;
return bits | maskedHalfExponentForNaN | sign;
}
/// <summary>
/// Converts single-precision values to zero-extended binary16 bit patterns.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The binary16 bit patterns in 32-bit lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector256<uint> ConvertSingleToHalfBits(Vector256<float> value)
{
Vector256<uint> bits = value.AsUInt32();
Vector256<uint> sign = Vector256.ShiftRightLogical(bits & Vector256.Create(SingleSignMask), 16);
Vector256<uint> realMask = Vector256.Equals(value, value).AsUInt32();
value = Vector256.Abs(value);
value = Vector256.Min(Vector256.Create(MaxHalfValueBelowInfinity), value);
Vector256<uint> 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<uint> newExponent = Vector256.ShiftRightLogical(bits, 13);
Vector256<uint> maskedHalfExponentForNaN = ~realMask & Vector256.Create(HalfExponentMask);
bits &= realMask;
bits += newExponent;
bits &= ~maskedHalfExponentForNaN;
return bits | maskedHalfExponentForNaN | sign;
}
/// <summary>
/// Converts single-precision values to zero-extended binary16 bit patterns.
/// </summary>
/// <param name="value">The single-precision values.</param>
/// <returns>The binary16 bit patterns in 32-bit lanes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<uint> ConvertSingleToHalfBits(Vector512<float> value)
{
Vector512<uint> bits = value.AsUInt32();
Vector512<uint> sign = Vector512.ShiftRightLogical(bits & Vector512.Create(SingleSignMask), 16);
Vector512<uint> realMask = Vector512.Equals(value, value).AsUInt32();
value = Vector512.Abs(value);
value = Vector512.Min(Vector512.Create(MaxHalfValueBelowInfinity), value);
Vector512<uint> 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<uint> newExponent = Vector512.ShiftRightLogical(bits, 13);
Vector512<uint> maskedHalfExponentForNaN = ~realMask & Vector512.Create(HalfExponentMask);
bits &= realMask;
bits += newExponent;
bits &= ~maskedHalfExponentForNaN;
return bits | maskedHalfExponentForNaN | sign;
}
}

118
src/ImageSharp/PixelFormats/IPixel.cs

@ -14,120 +14,150 @@ namespace SixLabors.ImageSharp.PixelFormats;
public interface IPixel<TSelf> : IPixel, IEquatable<TSelf>
where TSelf : unmanaged, IPixel<TSelf>
{
#pragma warning disable CA1000 // Do not declare static members on generic types
/// <summary>
/// Creates a <see cref="PixelOperations{TPixel}"/> instance for this pixel type.
/// This method is not intended to be consumed directly. Use <see cref="PixelOperations{TPixel}.Instance"/> instead.
/// </summary>
/// <returns>The <see cref="PixelOperations{TPixel}"/> instance.</returns>
static abstract PixelOperations<TSelf> CreatePixelOperations();
public static abstract PixelOperations<TSelf> CreatePixelOperations();
/// <summary>
/// Initializes the pixel instance from a generic a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>
/// Initializes the pixel instance from a generic ("scaled") <see cref="Vector4"/> representation using the pixel type's native alpha representation.
/// The scaled representation uses <value>0</value> and <value>1</value> as its nominal component bounds.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromScaledVector4(Vector4 source);
public static abstract TSelf FromScaledVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from a <see cref="Vector4"/> which is specific to the current pixel type.
/// Initializes the pixel instance from a generic ("scaled") <see cref="Vector4"/> whose color components use unassociated alpha.
/// The scaled representation uses <value>0</value> and <value>1</value> as its nominal component bounds.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromVector4(Vector4 source);
public static abstract TSelf FromUnassociatedScaledVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from a generic ("scaled") <see cref="Vector4"/> whose color components use associated alpha,
/// representing color multiplied by the logical opacity represented by alpha.
/// The scaled representation uses <value>0</value> and <value>1</value> as its nominal component bounds.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
public static abstract TSelf FromAssociatedScaledVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from a <see cref="Vector4"/> which is specific to the current pixel type and uses its native alpha representation.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
public static abstract TSelf FromVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from a <see cref="Vector4"/> which is specific to the current pixel type and whose color components use unassociated alpha.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
public static abstract TSelf FromUnassociatedVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from a <see cref="Vector4"/> 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.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
public static abstract TSelf FromAssociatedVector4(Vector4 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Abgr32"/> value.
/// </summary>
/// <param name="source">The <see cref="Abgr32"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromAbgr32(Abgr32 source);
public static abstract TSelf FromAbgr32(Abgr32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Argb32"/> value.
/// </summary>
/// <param name="source">The <see cref="Argb32"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromArgb32(Argb32 source);
public static abstract TSelf FromArgb32(Argb32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Bgra5551"/> value.
/// </summary>
/// <param name="source">The <see cref="Bgra5551"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgra5551(Bgra5551 source);
public static abstract TSelf FromBgra5551(Bgra5551 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Bgr24"/> value.
/// </summary>
/// <param name="source">The <see cref="Bgr24"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgr24(Bgr24 source);
public static abstract TSelf FromBgr24(Bgr24 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Bgra32"/> value.
/// </summary>
/// <param name="source">The <see cref="Bgra32"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgra32(Bgra32 source);
public static abstract TSelf FromBgra32(Bgra32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="L8"/> value.
/// </summary>
/// <param name="source">The <see cref="L8"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromL8(L8 source);
public static abstract TSelf FromL8(L8 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="L16"/> value.
/// </summary>
/// <param name="source">The <see cref="L16"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromL16(L16 source);
public static abstract TSelf FromL16(L16 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="La16"/> value.
/// </summary>
/// <param name="source">The <see cref="La16"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromLa16(La16 source);
public static abstract TSelf FromLa16(La16 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="La32"/> value.
/// </summary>
/// <param name="source">The <see cref="La32"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromLa32(La32 source);
public static abstract TSelf FromLa32(La32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Rgb24"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgb24"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgb24(Rgb24 source);
public static abstract TSelf FromRgb24(Rgb24 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Rgba32"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgba32"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgba32(Rgba32 source);
public static abstract TSelf FromRgba32(Rgba32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Rgb48"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgb48"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgb48(Rgb48 source);
public static abstract TSelf FromRgb48(Rgb48 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Rgba64"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgba64"/> value.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgba64(Rgba64 source);
#pragma warning restore CA1000 // Do not declare static members on generic types
public static abstract TSelf FromRgba64(Rgba64 source);
}
/// <summary>
@ -139,26 +169,58 @@ public interface IPixel
/// Gets the pixel type information.
/// </summary>
/// <returns>The <see cref="PixelTypeInfo"/>.</returns>
static abstract PixelTypeInfo GetPixelTypeInfo();
public static abstract PixelTypeInfo GetPixelTypeInfo();
/// <summary>
/// Convert the pixel instance into <see cref="Rgba32"/> representation.
/// </summary>
/// <returns>The <see cref="Rgba32"/></returns>
Rgba32 ToRgba32();
public Rgba32 ToRgba32();
/// <summary>
/// Expands the pixel into a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// Expands the pixel into a generic ("scaled") <see cref="Vector4"/> representation using the pixel type's native alpha representation.
/// The scaled representation uses <value>0</value> and <value>1</value> as its nominal component bounds.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToScaledVector4();
public Vector4 ToScaledVector4();
/// <summary>
/// Expands the pixel into a generic ("scaled") <see cref="Vector4"/> whose color components use unassociated alpha.
/// The scaled representation uses <value>0</value> and <value>1</value> 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.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
public Vector4 ToUnassociatedScaledVector4();
/// <summary>
/// Expands the pixel into a generic ("scaled") <see cref="Vector4"/> whose color components use associated alpha,
/// representing color multiplied by the logical opacity represented by alpha.
/// The scaled representation uses <value>0</value> and <value>1</value> as its nominal component bounds.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
public Vector4 ToAssociatedScaledVector4();
/// <summary>
/// Expands the pixel into a <see cref="Vector4"/> which is specific to the current pixel type.
/// Expands the pixel into a <see cref="Vector4"/> 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.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
public Vector4 ToVector4();
/// <summary>
/// Expands the pixel into a <see cref="Vector4"/> 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.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
public Vector4 ToUnassociatedVector4();
/// <summary>
/// Expands the pixel into a <see cref="Vector4"/> 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.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
public Vector4 ToAssociatedVector4();
}

84152
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.cs

File diff suppressed because it is too large

847
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.Generated.tt

@ -0,0 +1,847 @@
<#
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#>
<#@ 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.
// <auto-generated />
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;
/// <summary>
/// Provides generated Porter-Duff blenders for associated-alpha pixel formats.
/// </summary>
internal static partial class AssociatedAlphaPixelBlenders<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
<#
var composers = new []{
"Src",
"SrcAtop",
"SrcOver",
"SrcIn",
"SrcOut",
"Dest",
"DestAtop",
"DestOver",
"DestIn",
"DestOut",
"Clear",
"Xor",
};
var blenders = new []{
"Normal",
"Multiply",
"Add",
"Subtract",
"Screen",
"Darken",
"Lighten",
"Overlay",
"HardLight"
};
foreach(var composer in composers) {
foreach(var blender in blenders) {
var blender_composer= $"{blender}{composer}";
#>
/// <summary>
/// A pixel blender that implements the "<#= blender_composer#>" composition equation.
/// </summary>
public sealed class <#= blender_composer#> : AssociatedAlphaPixelBlender<TPixel>
{
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>();
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromAssociatedScaledVector4(AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background.ToAssociatedScaledVector4(), source.ToAssociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount)
{
amount = Numerics.Clamp(amount, 0, 1);
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source));
Vector512<float> opacity = Vector512.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=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);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source));
Vector256<float> opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=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] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
}
}
}
/// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, float amount)
{
amount = Numerics.Clamp(amount, 0, 1);
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
Vector512<float> sourceBase = Vector512.Create(
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W);
Vector512<float> opacity = Vector512.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256<float> opacity = Vector256.Create(amount);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 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] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
}
}
}
/// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount)
{
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float amount0 = amountBase;
float amount1 = Unsafe.Add(ref amountBase, 1);
float amount2 = Unsafe.Add(ref amountBase, 2);
float amount3 = Unsafe.Add(ref amountBase, 3);
// We need to create a Vector512<float> containing the current four amount values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> opacity = Vector512.Create(
amount0, amount0, amount0, amount0,
amount1, amount1, amount1, amount1,
amount2, amount2, amount2, amount2,
amount3, amount3, amount3, amount3);
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne);
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=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, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next amount values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> opacity = Vector256.Create(
Vector128.Create(amountBase),
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne);
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=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] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
}
}
}
/// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, ReadOnlySpan<float> amount)
{
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
Vector512<float> sourceBase = Vector512.Create(
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float amount0 = amountBase;
float amount1 = Unsafe.Add(ref amountBase, 1);
float amount2 = Unsafe.Add(ref amountBase, 2);
float amount3 = Unsafe.Add(ref amountBase, 3);
// We need to create a Vector512<float> containing the current four amount values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> opacity = Vector512.Create(
amount0, amount0, amount0, amount0,
amount1, amount1, amount1, amount1,
amount2, amount2, amount2, amount2,
amount3, amount3, amount3, amount3);
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne);
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next amount values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> opacity = Vector256.Create(
Vector128.Create(amountBase),
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne);
destinationBase = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 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] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
destination[i] = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
}
}
}
/// <inheritdoc />
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount, ReadOnlySpan<float> coverage)
{
amount = Numerics.Clamp(amount, 0, 1);
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector512<float> opacity = Vector512.Create(amount);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float coverage0 = coverageBase;
float coverage1 = Unsafe.Add(ref coverageBase, 1);
float coverage2 = Unsafe.Add(ref coverageBase, 2);
float coverage3 = Unsafe.Add(ref coverageBase, 3);
// We need to create a Vector512<float> containing the current four coverage values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> coverageVector = Vector512.Create(
coverage0, coverage0, coverage0, coverage0,
coverage1, coverage1, coverage1, coverage1,
coverage2, coverage2, coverage2, coverage2,
coverage3, coverage3, coverage3, coverage3);
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne);
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
coverageBase = ref Unsafe.Add(ref coverageBase, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector256<float> opacity = Vector256.Create(amount);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next coverage values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> coverageVector = Vector256.Create(
Vector128.Create(coverageBase),
Vector128.Create(Unsafe.Add(ref coverageBase, 1)));
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne);
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
sourceBase = ref Unsafe.Add(ref sourceBase, 1);
coverageBase = ref Unsafe.Add(ref coverageBase, 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;
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
/// <inheritdoc />
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, float amount, ReadOnlySpan<float> coverage)
{
amount = Numerics.Clamp(amount, 0, 1);
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector512<float> sourceBase = Vector512.Create(
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W);
Vector512<float> opacity = Vector512.Create(amount);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float coverage0 = coverageBase;
float coverage1 = Unsafe.Add(ref coverageBase, 1);
float coverage2 = Unsafe.Add(ref coverageBase, 2);
float coverage3 = Unsafe.Add(ref coverageBase, 3);
// We need to create a Vector512<float> containing the current four coverage values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> coverageVector = Vector512.Create(
coverage0, coverage0, coverage0, coverage0,
coverage1, coverage1, coverage1, coverage1,
coverage2, coverage2, coverage2, coverage2,
coverage3, coverage3, coverage3, coverage3);
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne);
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
coverageBase = ref Unsafe.Add(ref coverageBase, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256<float> opacity = Vector256.Create(amount);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next coverage values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> coverageVector = Vector256.Create(
Vector128.Create(coverageBase),
Vector128.Create(Unsafe.Add(ref coverageBase, 1)));
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne);
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
coverageBase = ref Unsafe.Add(ref coverageBase, 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;
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, amount);
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
/// <inheritdoc />
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount, ReadOnlySpan<float> coverage)
{
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref Vector512<float> sourceBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float amount0 = amountBase;
float amount1 = Unsafe.Add(ref amountBase, 1);
float amount2 = Unsafe.Add(ref amountBase, 2);
float amount3 = Unsafe.Add(ref amountBase, 3);
// We need to create a Vector512<float> containing the current four amount values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> opacity = Vector512.Create(
amount0, amount0, amount0, amount0,
amount1, amount1, amount1, amount1,
amount2, amount2, amount2, amount2,
amount3, amount3, amount3, amount3);
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne);
float coverage0 = coverageBase;
float coverage1 = Unsafe.Add(ref coverageBase, 1);
float coverage2 = Unsafe.Add(ref coverageBase, 2);
float coverage3 = Unsafe.Add(ref coverageBase, 3);
// We need to create a Vector512<float> containing the current four coverage values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> coverageVector = Vector512.Create(
coverage0, coverage0, coverage0, coverage0,
coverage1, coverage1, coverage1, coverage1,
coverage2, coverage2, coverage2, coverage2,
coverage3, coverage3, coverage3, coverage3);
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne);
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
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, 4);
coverageBase = ref Unsafe.Add(ref coverageBase, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref Vector256<float> sourceBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next amount values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> opacity = Vector256.Create(
Vector128.Create(amountBase),
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne);
// We need to create a Vector256<float> containing the current and next coverage values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> coverageVector = Vector256.Create(
Vector128.Create(coverageBase),
Vector128.Create(Unsafe.Add(ref coverageBase, 1)));
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne);
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
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);
coverageBase = ref Unsafe.Add(ref coverageBase, 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;
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
/// <inheritdoc />
protected override void BlendWithCoverageFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, Vector4 source, ReadOnlySpan<float> amount, ReadOnlySpan<float> coverage)
{
if (Avx512F.IsSupported && destination.Length >= 4)
{
// Divide by 4 as 4 elements per Vector4 and 16 per Vector512<float>
ref Vector512<float> destinationBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector512<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 4u);
ref Vector512<float> backgroundBase = ref Unsafe.As<Vector4, Vector512<float>>(ref MemoryMarshal.GetReference(background));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector512<float> sourceBase = Vector512.Create(
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W,
source.X, source.Y, source.Z, source.W);
Vector512<float> vOne = Vector512.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
float amount0 = amountBase;
float amount1 = Unsafe.Add(ref amountBase, 1);
float amount2 = Unsafe.Add(ref amountBase, 2);
float amount3 = Unsafe.Add(ref amountBase, 3);
// We need to create a Vector512<float> containing the current four amount values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> opacity = Vector512.Create(
amount0, amount0, amount0, amount0,
amount1, amount1, amount1, amount1,
amount2, amount2, amount2, amount2,
amount3, amount3, amount3, amount3);
opacity = Vector512.Min(Vector512.Max(Vector512<float>.Zero, opacity), vOne);
float coverage0 = coverageBase;
float coverage1 = Unsafe.Add(ref coverageBase, 1);
float coverage2 = Unsafe.Add(ref coverageBase, 2);
float coverage3 = Unsafe.Add(ref coverageBase, 3);
// We need to create a Vector512<float> containing the current four coverage values
// taking up each quarter of the Vector512<float> and then clamp them.
Vector512<float> coverageVector = Vector512.Create(
coverage0, coverage0, coverage0, coverage0,
coverage1, coverage1, coverage1, coverage1,
coverage2, coverage2, coverage2, coverage2,
coverage3, coverage3, coverage3, coverage3);
coverageVector = Vector512.Min(Vector512.Max(Vector512<float>.Zero, coverageVector), vOne);
Vector512<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 4);
coverageBase = ref Unsafe.Add(ref coverageBase, 4);
}
int remainder = Numerics.Modulo4(destination.Length);
if (remainder != 0)
{
for (int i = destination.Length - remainder; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
else if (Avx2.IsSupported && destination.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
ref Vector256<float> destinationBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u);
ref Vector256<float> backgroundBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(background));
ref float amountBase = ref MemoryMarshal.GetReference(amount);
ref float coverageBase = ref MemoryMarshal.GetReference(coverage);
Vector256<float> sourceBase = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W);
Vector256<float> vOne = Vector256.Create(1F);
while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast))
{
// We need to create a Vector256<float> containing the current and next amount values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> opacity = Vector256.Create(
Vector128.Create(amountBase),
Vector128.Create(Unsafe.Add(ref amountBase, 1)));
opacity = Avx.Min(Avx.Max(Vector256<float>.Zero, opacity), vOne);
// We need to create a Vector256<float> containing the current and next coverage values
// taking up each half of the Vector256<float> and then clamp them.
Vector256<float> coverageVector = Vector256.Create(
Vector128.Create(coverageBase),
Vector128.Create(Unsafe.Add(ref coverageBase, 1)));
coverageVector = Avx.Min(Avx.Max(Vector256<float>.Zero, coverageVector), vOne);
Vector256<float> blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity);
destinationBase = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(backgroundBase, blended, coverageVector);
destinationBase = ref Unsafe.Add(ref destinationBase, 1);
backgroundBase = ref Unsafe.Add(ref backgroundBase, 1);
amountBase = ref Unsafe.Add(ref amountBase, 2);
coverageBase = ref Unsafe.Add(ref coverageBase, 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;
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
else
{
for (int i = 0; i < destination.Length; i++)
{
Vector4 blended = AssociatedAlphaPorterDuffFunctions.<#=blender_composer#>(background[i], source, Numerics.Clamp(amount[i], 0, 1F));
destination[i] = AssociatedAlphaPorterDuffFunctions.BlendWithCoverage(background[i], blended, Numerics.Clamp(coverage[i], 0, 1F));
}
}
}
}
<#
}
}
#>
}

168
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlenders.cs

@ -0,0 +1,168 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders;
/// <summary>
/// Provides pixel blenders for formats that store associated alpha.
/// </summary>
internal static partial class AssociatedAlphaPixelBlenders<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Gets the blender for the requested color blending and alpha composition modes.
/// </summary>
/// <param name="colorMode">The color blending mode.</param>
/// <param name="alphaMode">The alpha composition mode.</param>
/// <returns>The pixel blender.</returns>
public static PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{
return alphaMode switch
{
PixelAlphaCompositionMode.Src => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrc.Instance,
PixelColorBlendingMode.Add => AddSrc.Instance,
PixelColorBlendingMode.Subtract => SubtractSrc.Instance,
PixelColorBlendingMode.Screen => ScreenSrc.Instance,
PixelColorBlendingMode.Darken => DarkenSrc.Instance,
PixelColorBlendingMode.Lighten => LightenSrc.Instance,
PixelColorBlendingMode.Overlay => OverlaySrc.Instance,
PixelColorBlendingMode.HardLight => HardLightSrc.Instance,
_ => NormalSrc.Instance,
},
PixelAlphaCompositionMode.SrcAtop => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrcAtop.Instance,
PixelColorBlendingMode.Add => AddSrcAtop.Instance,
PixelColorBlendingMode.Subtract => SubtractSrcAtop.Instance,
PixelColorBlendingMode.Screen => ScreenSrcAtop.Instance,
PixelColorBlendingMode.Darken => DarkenSrcAtop.Instance,
PixelColorBlendingMode.Lighten => LightenSrcAtop.Instance,
PixelColorBlendingMode.Overlay => OverlaySrcAtop.Instance,
PixelColorBlendingMode.HardLight => HardLightSrcAtop.Instance,
_ => NormalSrcAtop.Instance,
},
PixelAlphaCompositionMode.SrcIn => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrcIn.Instance,
PixelColorBlendingMode.Add => AddSrcIn.Instance,
PixelColorBlendingMode.Subtract => SubtractSrcIn.Instance,
PixelColorBlendingMode.Screen => ScreenSrcIn.Instance,
PixelColorBlendingMode.Darken => DarkenSrcIn.Instance,
PixelColorBlendingMode.Lighten => LightenSrcIn.Instance,
PixelColorBlendingMode.Overlay => OverlaySrcIn.Instance,
PixelColorBlendingMode.HardLight => HardLightSrcIn.Instance,
_ => NormalSrcIn.Instance,
},
PixelAlphaCompositionMode.SrcOut => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrcOut.Instance,
PixelColorBlendingMode.Add => AddSrcOut.Instance,
PixelColorBlendingMode.Subtract => SubtractSrcOut.Instance,
PixelColorBlendingMode.Screen => ScreenSrcOut.Instance,
PixelColorBlendingMode.Darken => DarkenSrcOut.Instance,
PixelColorBlendingMode.Lighten => LightenSrcOut.Instance,
PixelColorBlendingMode.Overlay => OverlaySrcOut.Instance,
PixelColorBlendingMode.HardLight => HardLightSrcOut.Instance,
_ => NormalSrcOut.Instance,
},
PixelAlphaCompositionMode.Dest => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyDest.Instance,
PixelColorBlendingMode.Add => AddDest.Instance,
PixelColorBlendingMode.Subtract => SubtractDest.Instance,
PixelColorBlendingMode.Screen => ScreenDest.Instance,
PixelColorBlendingMode.Darken => DarkenDest.Instance,
PixelColorBlendingMode.Lighten => LightenDest.Instance,
PixelColorBlendingMode.Overlay => OverlayDest.Instance,
PixelColorBlendingMode.HardLight => HardLightDest.Instance,
_ => NormalDest.Instance,
},
PixelAlphaCompositionMode.DestAtop => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyDestAtop.Instance,
PixelColorBlendingMode.Add => AddDestAtop.Instance,
PixelColorBlendingMode.Subtract => SubtractDestAtop.Instance,
PixelColorBlendingMode.Screen => ScreenDestAtop.Instance,
PixelColorBlendingMode.Darken => DarkenDestAtop.Instance,
PixelColorBlendingMode.Lighten => LightenDestAtop.Instance,
PixelColorBlendingMode.Overlay => OverlayDestAtop.Instance,
PixelColorBlendingMode.HardLight => HardLightDestAtop.Instance,
_ => NormalDestAtop.Instance,
},
PixelAlphaCompositionMode.DestOver => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyDestOver.Instance,
PixelColorBlendingMode.Add => AddDestOver.Instance,
PixelColorBlendingMode.Subtract => SubtractDestOver.Instance,
PixelColorBlendingMode.Screen => ScreenDestOver.Instance,
PixelColorBlendingMode.Darken => DarkenDestOver.Instance,
PixelColorBlendingMode.Lighten => LightenDestOver.Instance,
PixelColorBlendingMode.Overlay => OverlayDestOver.Instance,
PixelColorBlendingMode.HardLight => HardLightDestOver.Instance,
_ => NormalDestOver.Instance,
},
PixelAlphaCompositionMode.DestIn => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyDestIn.Instance,
PixelColorBlendingMode.Add => AddDestIn.Instance,
PixelColorBlendingMode.Subtract => SubtractDestIn.Instance,
PixelColorBlendingMode.Screen => ScreenDestIn.Instance,
PixelColorBlendingMode.Darken => DarkenDestIn.Instance,
PixelColorBlendingMode.Lighten => LightenDestIn.Instance,
PixelColorBlendingMode.Overlay => OverlayDestIn.Instance,
PixelColorBlendingMode.HardLight => HardLightDestIn.Instance,
_ => NormalDestIn.Instance,
},
PixelAlphaCompositionMode.DestOut => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyDestOut.Instance,
PixelColorBlendingMode.Add => AddDestOut.Instance,
PixelColorBlendingMode.Subtract => SubtractDestOut.Instance,
PixelColorBlendingMode.Screen => ScreenDestOut.Instance,
PixelColorBlendingMode.Darken => DarkenDestOut.Instance,
PixelColorBlendingMode.Lighten => LightenDestOut.Instance,
PixelColorBlendingMode.Overlay => OverlayDestOut.Instance,
PixelColorBlendingMode.HardLight => HardLightDestOut.Instance,
_ => NormalDestOut.Instance,
},
PixelAlphaCompositionMode.Clear => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyClear.Instance,
PixelColorBlendingMode.Add => AddClear.Instance,
PixelColorBlendingMode.Subtract => SubtractClear.Instance,
PixelColorBlendingMode.Screen => ScreenClear.Instance,
PixelColorBlendingMode.Darken => DarkenClear.Instance,
PixelColorBlendingMode.Lighten => LightenClear.Instance,
PixelColorBlendingMode.Overlay => OverlayClear.Instance,
PixelColorBlendingMode.HardLight => HardLightClear.Instance,
_ => NormalClear.Instance,
},
PixelAlphaCompositionMode.Xor => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplyXor.Instance,
PixelColorBlendingMode.Add => AddXor.Instance,
PixelColorBlendingMode.Subtract => SubtractXor.Instance,
PixelColorBlendingMode.Screen => ScreenXor.Instance,
PixelColorBlendingMode.Darken => DarkenXor.Instance,
PixelColorBlendingMode.Lighten => LightenXor.Instance,
PixelColorBlendingMode.Overlay => OverlayXor.Instance,
PixelColorBlendingMode.HardLight => HardLightXor.Instance,
_ => NormalXor.Instance,
},
_ => colorMode switch
{
PixelColorBlendingMode.Multiply => MultiplySrcOver.Instance,
PixelColorBlendingMode.Add => AddSrcOver.Instance,
PixelColorBlendingMode.Subtract => SubtractSrcOver.Instance,
PixelColorBlendingMode.Screen => ScreenSrcOver.Instance,
PixelColorBlendingMode.Darken => DarkenSrcOver.Instance,
PixelColorBlendingMode.Lighten => LightenSrcOver.Instance,
PixelColorBlendingMode.Overlay => OverlaySrcOver.Instance,
PixelColorBlendingMode.HardLight => HardLightSrcOver.Instance,
_ => NormalSrcOver.Instance,
},
};
}
}

38
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPixelBlender{TPixel}.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders;
/// <summary>
/// Provides the vector representation used to blend pixels that store associated alpha.
/// </summary>
/// <typeparam name="TPixel">The associated-alpha pixel format.</typeparam>
internal abstract class AssociatedAlphaPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private static readonly PixelOperations<TPixel> Operations = PixelOperations<TPixel>.Instance;
/// <inheritdoc />
protected override void ToBlendVector4<TPixelSource>(
Configuration configuration,
ReadOnlySpan<TPixelSource> source,
Span<Vector4> destination)
{
// Selecting the source representation once per row avoids a format check for every blended pixel.
PixelOperations<TPixelSource>.Instance.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply);
}
/// <inheritdoc />
protected override Vector4 ToBlendVector4(TPixel source) => source.ToAssociatedScaledVector4();
/// <inheritdoc />
protected override void FromBlendVector4(
Configuration configuration,
Span<Vector4> source,
Span<TPixel> destination)
{
Operations.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply);
}
}

5035
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.cs

File diff suppressed because it is too large

138
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.Generated.tt

@ -0,0 +1,138 @@
<#
// 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.
// <auto-generated />
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("<float>", "<float>.Zero");
#>
/// <summary>
/// Returns the associated-alpha result of the "<#= function #>" compositing equation.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <param name="opacity">The source opacity in the range 0 through 1.</param>
/// <returns>The associated composition result.</returns>
[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",
};
private static readonly string[] Blenders =
{
"Normal",
"Multiply",
"Add",
"Subtract",
"Screen",
"Darken",
"Lighten",
"Overlay",
"HardLight",
};
private static readonly string[] VectorTypes =
{
"Vector4",
"Vector256<float>",
"Vector512<float>",
};
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;
default:
return "Xor(backdrop, source)";
}
}
#>

550
src/ImageSharp/PixelFormats/PixelBlenders/AssociatedAlphaPorterDuffFunctions.cs

@ -0,0 +1,550 @@
// 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;
/// <summary>
/// Provides Porter-Duff composition functions for associated-alpha vectors.
/// </summary>
internal static partial class AssociatedAlphaPorterDuffFunctions
{
private const int BlendAlphaControl = 0b_10_00_10_00;
private const int ShuffleAlphaControl = 0b_11_11_11_11;
/// <summary>
/// Calculates the associated overlap term for Multiply blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Multiply(Vector4 backdrop, Vector4 source) => backdrop * source;
/// <inheritdoc cref="Multiply(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Multiply(Vector256<float> backdrop, Vector256<float> source) => backdrop * source;
/// <inheritdoc cref="Multiply(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Multiply(Vector512<float> backdrop, Vector512<float> source) => backdrop * source;
/// <summary>
/// Calculates the associated overlap term for Add blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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));
}
/// <inheritdoc cref="Add(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Add(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return Vector256.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha));
}
/// <inheritdoc cref="Add(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Add(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return Vector512.Min(backdropAlpha * sourceAlpha, (backdrop * sourceAlpha) + (source * backdropAlpha));
}
/// <summary>
/// Calculates the associated overlap term for Subtract blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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));
}
/// <inheritdoc cref="Subtract(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Subtract(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return Vector256.Max(Vector256<float>.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha));
}
/// <inheritdoc cref="Subtract(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Subtract(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return Vector512.Max(Vector512<float>.Zero, (backdrop * sourceAlpha) - (source * backdropAlpha));
}
/// <summary>
/// Calculates the associated overlap term for Screen blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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);
}
/// <inheritdoc cref="Screen(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Screen(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source);
}
/// <inheritdoc cref="Screen(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Screen(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return (backdrop * sourceAlpha) + (source * backdropAlpha) - (backdrop * source);
}
/// <summary>
/// Calculates the associated overlap term for Darken blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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);
}
/// <inheritdoc cref="Darken(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Darken(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return Vector256.Min(backdrop * sourceAlpha, source * backdropAlpha);
}
/// <inheritdoc cref="Darken(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Darken(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return Vector512.Min(backdrop * sourceAlpha, source * backdropAlpha);
}
/// <summary>
/// Calculates the associated overlap term for Lighten blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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);
}
/// <inheritdoc cref="Lighten(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Lighten(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return Vector256.Max(backdrop * sourceAlpha, source * backdropAlpha);
}
/// <inheritdoc cref="Lighten(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Lighten(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return Vector512.Max(backdrop * sourceAlpha, source * backdropAlpha);
}
/// <summary>
/// Calculates the associated overlap term for Overlay blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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);
}
/// <inheritdoc cref="Overlay(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Overlay(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> left = (backdrop + backdrop) * source;
Vector256<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F));
Vector256<float> useRight = Avx.CompareGreaterThan(backdrop + backdrop, backdropAlpha);
return Avx.BlendVariable(left, right, useRight);
}
/// <inheritdoc cref="Overlay(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Overlay(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> left = (backdrop + backdrop) * source;
Vector512<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F));
Vector512<float> useRight = Avx512F.CompareGreaterThan(backdrop + backdrop, backdropAlpha);
return Vector512.ConditionalSelect(useRight, right, left);
}
/// <summary>
/// Calculates the associated overlap term for HardLight blending.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated overlap term.</returns>
[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);
}
/// <inheritdoc cref="HardLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> HardLight(Vector256<float> backdrop, Vector256<float> source)
{
Vector256<float> backdropAlpha = Avx.Permute(backdrop, ShuffleAlphaControl);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> left = (backdrop + backdrop) * source;
Vector256<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector256.Create(2F));
Vector256<float> useRight = Avx.CompareGreaterThan(source + source, sourceAlpha);
return Avx.BlendVariable(left, right, useRight);
}
/// <inheritdoc cref="HardLight(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> HardLight(Vector512<float> backdrop, Vector512<float> source)
{
Vector512<float> backdropAlpha = Vector512_.ShuffleNative(backdrop, ShuffleAlphaControl);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> left = (backdrop + backdrop) * source;
Vector512<float> right = (backdropAlpha * sourceAlpha) - (((backdropAlpha - backdrop) * (sourceAlpha - source)) * Vector512.Create(2F));
Vector512<float> useRight = Avx512F.CompareGreaterThan(source + source, sourceAlpha);
return Vector512.ConditionalSelect(useRight, right, left);
}
/// <summary>
/// Composites an associated source over an associated destination without a color-blending function.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated composition result.</returns>
[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));
}
/// <inheritdoc cref="OverNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> OverNormal(Vector256<float> destination, Vector256<float> source)
{
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
return source + (destination * (Vector256.Create(1F) - sourceAlpha));
}
/// <inheritdoc cref="OverNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> OverNormal(Vector512<float> destination, Vector512<float> source)
{
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
return source + (destination * (Vector512.Create(1F) - sourceAlpha));
}
/// <summary>
/// Composites an associated source atop an associated destination without a color-blending function.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated composition result.</returns>
[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));
}
/// <inheritdoc cref="AtopNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> AtopNormal(Vector256<float> destination, Vector256<float> source)
{
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
return (source * destinationAlpha) + (destination * (Vector256.Create(1F) - sourceAlpha));
}
/// <inheritdoc cref="AtopNormal(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> AtopNormal(Vector512<float> destination, Vector512<float> source)
{
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
return (source * destinationAlpha) + (destination * (Vector512.Create(1F) - sourceAlpha));
}
/// <summary>
/// Composites an associated source over an associated destination using an unassociated blended color.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <param name="overlap">The associated overlap term produced by the color-blending function.</param>
/// <returns>The associated composition result.</returns>
[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);
}
/// <inheritdoc cref="Over(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Over(Vector256<float> destination, Vector256<float> source, Vector256<float> overlap)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
Vector256<float> result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap;
Vector256<float> alpha = source + (destination * (one - sourceAlpha));
return Avx.Blend(result, alpha, BlendAlphaControl);
}
/// <inheritdoc cref="Over(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Over(Vector512<float> destination, Vector512<float> source, Vector512<float> overlap)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
Vector512<float> result = (destination * (one - sourceAlpha)) + (source * (one - destinationAlpha)) + overlap;
Vector512<float> alpha = source + (destination * (one - sourceAlpha));
return Vector512.ConditionalSelect(AlphaMask512(), alpha, result);
}
/// <summary>
/// Composites an associated source atop an associated destination using an unassociated blended color.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <param name="overlap">The associated overlap term produced by the color-blending function.</param>
/// <returns>The associated composition result.</returns>
[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);
}
/// <inheritdoc cref="Atop(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Atop(Vector256<float> destination, Vector256<float> source, Vector256<float> overlap)
{
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
Vector256<float> result = Vector256_.FusedMultiplyAdd(destination, Vector256.Create(1F) - sourceAlpha, overlap);
return Avx.Blend(result, destinationAlpha, BlendAlphaControl);
}
/// <inheritdoc cref="Atop(Vector4, Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Atop(Vector512<float> destination, Vector512<float> source, Vector512<float> overlap)
{
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
Vector512<float> result = Vector512_.FusedMultiplyAdd(destination, Vector512.Create(1F) - sourceAlpha, overlap);
return Vector512.ConditionalSelect(AlphaMask512(), destinationAlpha, result);
}
/// <summary>
/// Retains the associated source within the destination coverage.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated composition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 In(Vector4 destination, Vector4 source) => source * Numerics.PermuteW(destination);
/// <inheritdoc cref="In(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> In(Vector256<float> destination, Vector256<float> source)
=> source * Avx.Permute(destination, ShuffleAlphaControl);
/// <inheritdoc cref="In(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> In(Vector512<float> destination, Vector512<float> source)
=> source * Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
/// <summary>
/// Retains the associated source outside the destination coverage.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated composition result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Out(Vector4 destination, Vector4 source)
=> source * (Vector4.One - Numerics.PermuteW(destination));
/// <inheritdoc cref="Out(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Out(Vector256<float> destination, Vector256<float> source)
=> source * (Vector256.Create(1F) - Avx.Permute(destination, ShuffleAlphaControl));
/// <inheritdoc cref="Out(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Out(Vector512<float> destination, Vector512<float> source)
=> source * (Vector512.Create(1F) - Vector512_.ShuffleNative(destination, ShuffleAlphaControl));
/// <summary>
/// Retains only the non-overlapping parts of two associated vectors.
/// </summary>
/// <param name="destination">The associated destination vector.</param>
/// <param name="source">The associated source vector.</param>
/// <returns>The associated composition result.</returns>
[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));
}
/// <inheritdoc cref="Xor(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> Xor(Vector256<float> destination, Vector256<float> source)
{
Vector256<float> one = Vector256.Create(1F);
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> destinationAlpha = Avx.Permute(destination, ShuffleAlphaControl);
return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha));
}
/// <inheritdoc cref="Xor(Vector4, Vector4)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Xor(Vector512<float> destination, Vector512<float> source)
{
Vector512<float> one = Vector512.Create(1F);
Vector512<float> sourceAlpha = Vector512_.ShuffleNative(source, ShuffleAlphaControl);
Vector512<float> destinationAlpha = Vector512_.ShuffleNative(destination, ShuffleAlphaControl);
return (source * (one - destinationAlpha)) + (destination * (one - sourceAlpha));
}
/// <summary>
/// Applies raster coverage to an associated composition result.
/// </summary>
/// <param name="backdrop">The associated backdrop vector.</param>
/// <param name="source">The associated composition result.</param>
/// <param name="coverage">The raster coverage in the range 0 through 1.</param>
/// <returns>The covered associated result.</returns>
[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();
}
/// <inheritdoc cref="BlendWithCoverage(Vector4, Vector4, float)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector256<float> BlendWithCoverage(Vector256<float> backdrop, Vector256<float> source, Vector256<float> coverage)
=> Vector256_.FusedMultiplyAdd(source - backdrop, coverage, backdrop);
/// <inheritdoc cref="BlendWithCoverage(Vector4, Vector4, float)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> BlendWithCoverage(Vector512<float> backdrop, Vector512<float> source, Vector512<float> coverage)
=> Vector512_.FusedMultiplyAdd(source - backdrop, coverage, backdrop);
/// <summary>
/// Calculates one associated Overlay overlap component without recovering either straight component.
/// </summary>
/// <param name="backdrop">The associated backdrop component.</param>
/// <param name="backdropAlpha">The backdrop alpha.</param>
/// <param name="source">The associated source component.</param>
/// <param name="sourceAlpha">The source alpha.</param>
/// <returns>The associated overlap component.</returns>
[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));
}
/// <summary>
/// Creates a SIMD lane mask selecting the alpha component of each packed vector.
/// </summary>
/// <returns>The alpha-component mask.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<float> AlphaMask512()
=> Vector512.Create(0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1).AsSingle();
}

216
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs

@ -38,7 +38,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -817,7 +817,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -1596,7 +1596,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -2375,7 +2375,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -3154,7 +3154,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -3933,7 +3933,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -4712,7 +4712,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -5491,7 +5491,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -6270,7 +6270,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -7049,7 +7049,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -7828,7 +7828,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -8607,7 +8607,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -9386,7 +9386,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -10165,7 +10165,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -10944,7 +10944,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -11723,7 +11723,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -12502,7 +12502,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -13281,7 +13281,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -14060,7 +14060,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -14839,7 +14839,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -15618,7 +15618,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -16397,7 +16397,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -17176,7 +17176,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -17955,7 +17955,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -18734,7 +18734,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -19513,7 +19513,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -20292,7 +20292,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -21071,7 +21071,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -21850,7 +21850,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -22629,7 +22629,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -23408,7 +23408,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -24187,7 +24187,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -24966,7 +24966,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -25745,7 +25745,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -26524,7 +26524,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -27303,7 +27303,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -28082,7 +28082,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -28861,7 +28861,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -29640,7 +29640,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -30419,7 +30419,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -31198,7 +31198,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -31977,7 +31977,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -32756,7 +32756,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -33535,7 +33535,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -34314,7 +34314,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -35093,7 +35093,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -35872,7 +35872,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -36651,7 +36651,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -37430,7 +37430,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -38209,7 +38209,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -38988,7 +38988,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -39767,7 +39767,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -40546,7 +40546,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -41325,7 +41325,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDest(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -42104,7 +42104,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -42883,7 +42883,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -43662,7 +43662,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -44441,7 +44441,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -45220,7 +45220,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -45999,7 +45999,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -46778,7 +46778,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -47557,7 +47557,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -48336,7 +48336,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -49115,7 +49115,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -49894,7 +49894,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -50673,7 +50673,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -51452,7 +51452,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -52231,7 +52231,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -53010,7 +53010,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -53789,7 +53789,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -54568,7 +54568,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -55347,7 +55347,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -56126,7 +56126,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -56905,7 +56905,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -57684,7 +57684,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -58463,7 +58463,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -59242,7 +59242,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -60021,7 +60021,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -60800,7 +60800,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -61579,7 +61579,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -62358,7 +62358,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -63137,7 +63137,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -63916,7 +63916,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -64695,7 +64695,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -65474,7 +65474,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -66253,7 +66253,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -67032,7 +67032,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -67811,7 +67811,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -68590,7 +68590,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -69369,7 +69369,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -70148,7 +70148,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -70927,7 +70927,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -71706,7 +71706,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -72485,7 +72485,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -73264,7 +73264,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -74043,7 +74043,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -74822,7 +74822,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -75601,7 +75601,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -76380,7 +76380,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightClear(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -77159,7 +77159,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.NormalXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -77938,7 +77938,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -78717,7 +78717,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.AddXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -79496,7 +79496,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.SubtractXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -80275,7 +80275,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.ScreenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -81054,7 +81054,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.DarkenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -81833,7 +81833,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.LightenXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -82612,7 +82612,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.OverlayXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />
@ -83391,7 +83391,7 @@ internal static class DefaultPixelBlenders<TPixel>
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.HardLightXor(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />

2
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt

@ -81,7 +81,7 @@ var blenders = new []{
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return TPixel.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0, 1)));
return TPixel.FromUnassociatedScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), Numerics.Clamp(amount, 0, 1)));
}
/// <inheritdoc />

216
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

@ -505,7 +505,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -521,7 +521,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -537,7 +537,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -553,7 +553,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -569,7 +569,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -585,7 +585,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -601,7 +601,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -617,7 +617,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -633,7 +633,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -649,7 +649,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -665,7 +665,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -681,7 +681,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(NormalXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1176,7 +1176,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplySrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1192,7 +1192,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplySrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1208,7 +1208,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplySrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1224,7 +1224,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplySrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1240,7 +1240,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplySrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1256,7 +1256,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1272,7 +1272,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1288,7 +1288,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1304,7 +1304,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1320,7 +1320,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1336,7 +1336,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1352,7 +1352,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(MultiplyXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1847,7 +1847,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1863,7 +1863,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1879,7 +1879,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1895,7 +1895,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1911,7 +1911,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1927,7 +1927,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1943,7 +1943,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1959,7 +1959,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1975,7 +1975,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -1991,7 +1991,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2007,7 +2007,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2023,7 +2023,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(AddXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2518,7 +2518,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2534,7 +2534,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2550,7 +2550,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2566,7 +2566,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2582,7 +2582,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2598,7 +2598,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2614,7 +2614,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2630,7 +2630,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2646,7 +2646,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2662,7 +2662,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2678,7 +2678,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -2694,7 +2694,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(SubtractXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3189,7 +3189,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3205,7 +3205,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3221,7 +3221,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3237,7 +3237,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3253,7 +3253,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3269,7 +3269,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3285,7 +3285,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3301,7 +3301,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3317,7 +3317,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3333,7 +3333,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3349,7 +3349,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3365,7 +3365,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(ScreenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3860,7 +3860,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3876,7 +3876,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3892,7 +3892,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3908,7 +3908,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3924,7 +3924,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3940,7 +3940,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3956,7 +3956,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3972,7 +3972,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -3988,7 +3988,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4004,7 +4004,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4020,7 +4020,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4036,7 +4036,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(DarkenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4531,7 +4531,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4547,7 +4547,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4563,7 +4563,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4579,7 +4579,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4595,7 +4595,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4611,7 +4611,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4627,7 +4627,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4643,7 +4643,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4659,7 +4659,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4675,7 +4675,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4691,7 +4691,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -4707,7 +4707,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(LightenXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5202,7 +5202,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlaySrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5218,7 +5218,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlaySrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5234,7 +5234,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlaySrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5250,7 +5250,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlaySrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5266,7 +5266,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlaySrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5282,7 +5282,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5298,7 +5298,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5314,7 +5314,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5330,7 +5330,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5346,7 +5346,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5362,7 +5362,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5378,7 +5378,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(OverlayXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5873,7 +5873,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightSrc(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5889,7 +5889,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightSrcAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5905,7 +5905,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightSrcOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5921,7 +5921,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightSrcIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5937,7 +5937,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightSrcOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5953,7 +5953,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightDest(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5969,7 +5969,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightDestAtop(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -5985,7 +5985,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightDestOver(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -6001,7 +6001,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightDestIn(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -6017,7 +6017,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightDestOut(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -6033,7 +6033,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightClear(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
/// <summary>
@ -6049,6 +6049,6 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(HardLightXor(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
}

2
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt

@ -518,7 +518,7 @@ internal static partial class PorterDuffFunctions
where TPixel : unmanaged, IPixel<TPixel>
{
opacity = Numerics.Clamp(opacity, 0, 1);
return TPixel.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return TPixel.FromUnassociatedScaledVector4(<#=blender#><#=composer#>(backdrop.ToUnassociatedScaledVector4(), source.ToUnassociatedScaledVector4(), opacity));
}
<# } #>
<#

28
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs

@ -338,7 +338,9 @@ internal static partial class PorterDuffFunctions
Vector4 sourceAlpha = Numerics.PermuteW(source);
Vector4 backdropPremultiplied = Numerics.WithW(backdrop * backdropAlpha, backdropAlpha);
Vector4 sourcePremultiplied = Numerics.WithW(source * sourceAlpha, sourceAlpha);
Vector4 result = backdropPremultiplied + ((sourcePremultiplied - backdropPremultiplied) * coverage);
// 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;
@ -358,7 +360,7 @@ internal static partial class PorterDuffFunctions
Vector256<float> sourceAlpha = Avx.Permute(source, ShuffleAlphaControl);
Vector256<float> backdropPremultiplied = Avx.Blend(backdrop * backdropAlpha, backdropAlpha, BlendAlphaControl);
Vector256<float> sourcePremultiplied = Avx.Blend(source * sourceAlpha, sourceAlpha, BlendAlphaControl);
Vector256<float> result = Vector256_.MultiplyAdd(backdropPremultiplied, sourcePremultiplied - backdropPremultiplied, coverage);
Vector256<float> result = Vector256_.MultiplyAddEstimate(sourcePremultiplied - backdropPremultiplied, coverage, backdropPremultiplied);
return Numerics.UnPremultiply(result, Avx.Permute(result, ShuffleAlphaControl));
}
@ -378,7 +380,7 @@ internal static partial class PorterDuffFunctions
Vector512<float> alphaMask = AlphaMask512();
Vector512<float> backdropPremultiplied = Vector512.ConditionalSelect(alphaMask, backdropAlpha, backdrop * backdropAlpha);
Vector512<float> sourcePremultiplied = Vector512.ConditionalSelect(alphaMask, sourceAlpha, source * sourceAlpha);
Vector512<float> result = Vector512_.MultiplyAdd(backdropPremultiplied, sourcePremultiplied - backdropPremultiplied, coverage);
Vector512<float> result = Vector512_.MultiplyAddEstimate(sourcePremultiplied - backdropPremultiplied, coverage, backdropPremultiplied);
return Numerics.UnPremultiply(result, Vector512_.ShuffleNative(result, ShuffleAlphaControl));
}
@ -481,8 +483,8 @@ internal static partial class PorterDuffFunctions
// calculate final color
Vector256<float> color = destination * dstW;
color = Vector256_.MultiplyAdd(color, source, srcW);
color = Vector256_.MultiplyAdd(color, blend, blendW);
color = Vector256_.MultiplyAddEstimate(source, srcW, color);
color = Vector256_.MultiplyAddEstimate(blend, blendW, color);
// unpremultiply
return Numerics.UnPremultiply(color, alpha);
@ -511,8 +513,8 @@ internal static partial class PorterDuffFunctions
// calculate final color
Vector512<float> color = destination * dstW;
color = Vector512_.MultiplyAdd(color, source, srcW);
color = Vector512_.MultiplyAdd(color, blend, blendW);
color = Vector512_.MultiplyAddEstimate(source, srcW, color);
color = Vector512_.MultiplyAddEstimate(blend, blendW, color);
// unpremultiply
return Numerics.UnPremultiply(color, alpha);
@ -565,7 +567,7 @@ internal static partial class PorterDuffFunctions
Vector256<float> dstW = alpha - blendW;
// calculate final color
Vector256<float> color = Vector256_.MultiplyAdd(Avx.Multiply(blend, blendW), destination, dstW);
Vector256<float> color = Vector256_.MultiplyAddEstimate(destination, dstW, Avx.Multiply(blend, blendW));
// unpremultiply
return Numerics.UnPremultiply(color, alpha);
@ -590,7 +592,7 @@ internal static partial class PorterDuffFunctions
Vector512<float> dstW = alpha - blendW;
// calculate final color
Vector512<float> color = Vector512_.MultiplyAdd(blend * blendW, destination, dstW);
Vector512<float> color = Vector512_.MultiplyAddEstimate(destination, dstW, blend * blendW);
// unpremultiply
return Numerics.UnPremultiply(color, alpha);
@ -749,8 +751,8 @@ internal static partial class PorterDuffFunctions
Vector256<float> dstW = vOne - sW;
// calculate alpha
Vector256<float> alpha = Vector256_.MultiplyAdd(Avx.Multiply(dW, dstW), sW, srcW);
Vector256<float> color = Vector256_.MultiplyAdd(Avx.Multiply(Avx.Multiply(dW, destination), dstW), Avx.Multiply(sW, source), srcW);
Vector256<float> alpha = Vector256_.MultiplyAddEstimate(sW, srcW, Avx.Multiply(dW, dstW));
Vector256<float> color = Vector256_.MultiplyAddEstimate(Avx.Multiply(sW, source), srcW, Avx.Multiply(Avx.Multiply(dW, destination), dstW));
// unpremultiply
return Numerics.UnPremultiply(color, alpha);
@ -774,8 +776,8 @@ internal static partial class PorterDuffFunctions
Vector512<float> dstW = vOne - sW;
// calculate alpha
Vector512<float> alpha = Vector512_.MultiplyAdd(dW * dstW, sW, srcW);
Vector512<float> color = Vector512_.MultiplyAdd((dW * destination) * dstW, sW * source, srcW);
Vector512<float> alpha = Vector512_.MultiplyAddEstimate(sW, srcW, dW * dstW);
Vector512<float> color = Vector512_.MultiplyAddEstimate(sW * source, srcW, (dW * destination) * dstW);
// unpremultiply
return Numerics.UnPremultiply(color, alpha);

82
src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs

@ -93,12 +93,12 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.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<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -161,11 +161,11 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> destinationVectors = workingBuffer[..maxLength];
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount);
this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -242,12 +242,12 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.ToBlendVector4(configuration, source[..maxLength], sourceVectors);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -317,11 +317,11 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> destinationVectors = workingBuffer[..maxLength];
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -463,12 +463,12 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.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<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -499,11 +499,11 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> destinationVectors = workingBuffer[..maxLength];
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.BlendFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount);
this.BlendFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -660,12 +660,12 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
Span<Vector4> sourceVectors = workingBuffer.Slice(maxLength * 2, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, source[..maxLength], sourceVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.ToBlendVector4(configuration, source[..maxLength], sourceVectors);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, sourceVectors, amount, coverage);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
@ -699,13 +699,47 @@ public abstract class PixelBlender<TPixel>
Span<Vector4> destinationVectors = workingBuffer[..maxLength];
Span<Vector4> backgroundVectors = workingBuffer.Slice(maxLength, maxLength);
PixelOperations<TPixel>.Instance.ToVector4(configuration, background[..maxLength], backgroundVectors, PixelConversionModifiers.Scale);
this.ToBlendVector4(configuration, background[..maxLength], backgroundVectors);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, source.ToScaledVector4(), amount, coverage);
this.BlendWithCoverageFunction(destinationVectors, backgroundVectors, this.ToBlendVector4(source), amount, coverage);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, destinationVectors, destination, PixelConversionModifiers.Scale);
this.FromBlendVector4(configuration, destinationVectors, destination);
}
/// <summary>
/// Converts source pixels to the scaled-vector representation consumed by this blender.
/// </summary>
/// <typeparam name="TPixelSource">The source pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source pixels.</param>
/// <param name="destination">The destination vectors.</param>
protected virtual void ToBlendVector4<TPixelSource>(
Configuration configuration,
ReadOnlySpan<TPixelSource> source,
Span<Vector4> destination)
where TPixelSource : unmanaged, IPixel<TPixelSource>
=> PixelOperations<TPixelSource>.Instance.ToVector4(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
/// <summary>
/// Converts a source pixel to the vector representation consumed by the blend functions.
/// </summary>
/// <param name="source">The source pixel.</param>
/// <returns>The source vector.</returns>
protected virtual Vector4 ToBlendVector4(TPixel source)
=> source.ToUnassociatedScaledVector4();
/// <summary>
/// Converts blend results from this blender's scaled-vector representation to destination pixels.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source vectors.</param>
/// <param name="destination">The destination pixels.</param>
protected virtual void FromBlendVector4(
Configuration configuration,
Span<Vector4> source,
Span<TPixel> destination)
=> PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, source, destination, PixelConversionModifiers.Scale | PixelConversionModifiers.UnPremultiply);
/// <summary>
/// Blend 2 rows together.
/// </summary>

20
src/ImageSharp/PixelFormats/PixelConversionModifiers.cs

@ -6,32 +6,38 @@ using SixLabors.ImageSharp.ColorProfiles.Companding;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Flags responsible to select additional operations which could be efficiently applied in
/// Specifies vector representation transformations applied by
/// <see cref="PixelOperations{TPixel}.ToVector4(Configuration,ReadOnlySpan{TPixel},Span{System.Numerics.Vector4},PixelConversionModifiers)"/>
/// or
/// and
/// <see cref="PixelOperations{TPixel}.FromVector4Destructive(Configuration,Span{System.Numerics.Vector4},Span{TPixel},PixelConversionModifiers)"/>
/// knowing the pixel type.
/// during bulk pixel conversion.
/// </summary>
[Flags]
public enum PixelConversionModifiers
{
/// <summary>
/// No special operation is selected
/// Preserves the pixel format's native numeric range and alpha representation.
/// </summary>
None = 0,
/// <summary>
/// Select <see cref="IPixel.ToScaledVector4"/> and <see cref="IPixel{T}.FromScaledVector4"/> instead the standard (non scaled) variants.
/// Requests the scaled numeric range represented by <see cref="IPixel.ToScaledVector4"/> and <see cref="IPixel{T}.FromScaledVector4"/>.
/// </summary>
Scale = 1 << 0,
/// <summary>
/// Enable alpha premultiplication / unpremultiplication
/// Requests associated alpha for the vector representation.
/// When combined with <see cref="UnPremultiply"/>, associated alpha takes precedence.
/// </summary>
Premultiply = 1 << 1,
/// <summary>
/// Enable SRGB companding (defined in <see cref="SRgbCompanding"/>).
/// Expands sRGB color components when converting pixels to vectors and compresses linear RGB color components when converting vectors to pixels.
/// </summary>
SRgbCompand = 1 << 2,
/// <summary>
/// Requests unassociated alpha for the vector representation unless <see cref="Premultiply"/> is also specified.
/// </summary>
UnPremultiply = 1 << 3,
}

39
src/ImageSharp/PixelFormats/PixelImplementations/A8.cs

@ -8,10 +8,11 @@ namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing a single 8-bit normalized alpha value.
/// <para>
/// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form.
/// </para>
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> and scaled vector conversions return alpha in <c>[0, 1]</c> with zero color components.
/// The storage layout matches <c>DXGI_FORMAT_A8_UNORM</c>.
/// </remarks>
public partial struct A8 : IPixel<A8>, IPackedVector<byte>
{
/// <summary>
@ -77,6 +78,38 @@ public partial struct A8 : IPixel<A8>, IPackedVector<byte>
/// <inheritdoc />
public static PixelOperations<A8> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static A8 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static A8 FromAssociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static A8 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static A8 FromAssociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static A8 FromScaledVector4(Vector4 source) => FromVector4(source);

43
src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs

@ -182,7 +182,7 @@ public partial struct Abgr32 : IPixel<Abgr32>, IPackedVector<uint>
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
@ -194,6 +194,47 @@ public partial struct Abgr32 : IPixel<Abgr32>, IPackedVector<uint>
/// <inheritdoc />
public static PixelOperations<Abgr32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32 FromScaledVector4(Vector4 source) => FromVector4(source);

273
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;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// The native component, packed, and vector representations use associated alpha.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public partial struct Abgr32P : IPixel<Abgr32P>, IPackedVector<uint>
{
private static readonly Vector4 Half = new(0.5F);
private static readonly Vector4 MaxBytes = new(byte.MaxValue);
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public byte A;
/// <summary>
/// Gets or sets the associated blue component.
/// </summary>
public byte B;
/// <summary>
/// Gets or sets the associated green component.
/// </summary>
public byte G;
/// <summary>
/// Gets or sets the associated red component.
/// </summary>
public byte R;
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Abgr32P(byte r, byte g, byte b)
: this(r, g, b, byte.MaxValue)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Abgr32P(byte r, byte g, byte b, byte a)
{
this.A = a;
this.B = b;
this.G = g;
this.R = r;
}
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Abgr32P(float r, float g, float b)
: this(new Vector4(r, g, b, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Abgr32P(float r, float g, float b, float a)
: this(new Vector4(r, g, b, a))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Abgr32P(Vector3 vector)
: this(new Vector4(vector, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Abgr32P(Vector4 vector)
: this() => this = FromScaledVector4(vector);
/// <summary>
/// Initializes a new instance of the <see cref="Abgr32P"/> struct from a packed associated value.
/// </summary>
/// <param name="packed">The packed associated value.</param>
public Abgr32P(uint packed)
: this() => this.PackedValue = packed;
/// <inheritdoc />
public uint PackedValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get => Unsafe.As<Abgr32P, uint>(ref Unsafe.AsRef(in this));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Unsafe.As<Abgr32P, uint>(ref this) = value;
}
/// <summary>
/// Compares two <see cref="Abgr32P"/> values for equality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are equal.</returns>
public static bool operator ==(Abgr32P left, Abgr32P right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Abgr32P"/> values for inequality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are not equal.</returns>
public static bool operator !=(Abgr32P left, Abgr32P right) => !left.Equals(right);
/// <inheritdoc />
public readonly Rgba32 ToRgba32()
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A));
/// <inheritdoc />
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
public readonly Vector4 ToVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
=> PixelTypeInfo.Create<Abgr32P>(
PixelComponentInfo.Create<Abgr32P>(4, 8, 8, 8, 8),
PixelColorType.Alpha | PixelColorType.BGR,
PixelAlphaRepresentation.Associated);
/// <inheritdoc />
public static PixelOperations<Abgr32P> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
public static Abgr32P FromVector4(Vector4 source) => FromAssociatedVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Abgr32P FromUnassociatedScaledVector4(Vector4 source)
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToAbgr32P(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
public static Abgr32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Abgr32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is Abgr32P other && this.Equals(other);
/// <inheritdoc />
public readonly bool Equals(Abgr32P other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly int GetHashCode() => this.PackedValue.GetHashCode();
/// <inheritdoc />
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<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte();
return new Abgr32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12));
}
}

43
src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs

@ -175,7 +175,7 @@ public partial struct Argb32 : IPixel<Argb32>, IPackedVector<uint>
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
@ -187,6 +187,47 @@ public partial struct Argb32 : IPixel<Argb32>, IPackedVector<uint>
/// <inheritdoc />
public static PixelOperations<Argb32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32 FromScaledVector4(Vector4 source) => FromVector4(source);

273
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;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// The native component, packed, and vector representations use associated alpha.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public partial struct Argb32P : IPixel<Argb32P>, IPackedVector<uint>
{
private static readonly Vector4 Half = new(0.5F);
private static readonly Vector4 MaxBytes = new(byte.MaxValue);
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public byte A;
/// <summary>
/// Gets or sets the associated red component.
/// </summary>
public byte R;
/// <summary>
/// Gets or sets the associated green component.
/// </summary>
public byte G;
/// <summary>
/// Gets or sets the associated blue component.
/// </summary>
public byte B;
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Argb32P(byte r, byte g, byte b)
: this(r, g, b, byte.MaxValue)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Argb32P(byte r, byte g, byte b, byte a)
{
this.A = a;
this.R = r;
this.G = g;
this.B = b;
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Argb32P(float r, float g, float b)
: this(new Vector4(r, g, b, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Argb32P(float r, float g, float b, float a)
: this(new Vector4(r, g, b, a))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Argb32P(Vector3 vector)
: this(new Vector4(vector, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Argb32P(Vector4 vector)
: this() => this = FromScaledVector4(vector);
/// <summary>
/// Initializes a new instance of the <see cref="Argb32P"/> struct from a packed associated value.
/// </summary>
/// <param name="packed">The packed associated value.</param>
public Argb32P(uint packed)
: this() => this.PackedValue = packed;
/// <inheritdoc />
public uint PackedValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get => Unsafe.As<Argb32P, uint>(ref Unsafe.AsRef(in this));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Unsafe.As<Argb32P, uint>(ref this) = value;
}
/// <summary>
/// Compares two <see cref="Argb32P"/> values for equality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are equal.</returns>
public static bool operator ==(Argb32P left, Argb32P right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Argb32P"/> values for inequality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are not equal.</returns>
public static bool operator !=(Argb32P left, Argb32P right) => !left.Equals(right);
/// <inheritdoc />
public readonly Rgba32 ToRgba32()
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A));
/// <inheritdoc />
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
public readonly Vector4 ToVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
=> PixelTypeInfo.Create<Argb32P>(
PixelComponentInfo.Create<Argb32P>(4, 8, 8, 8, 8),
PixelColorType.Alpha | PixelColorType.RGB,
PixelAlphaRepresentation.Associated);
/// <inheritdoc />
public static PixelOperations<Argb32P> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
public static Argb32P FromVector4(Vector4 source) => FromAssociatedVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Argb32P FromUnassociatedScaledVector4(Vector4 source)
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToArgb32P(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
public static Argb32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Argb32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is Argb32P other && this.Equals(other);
/// <inheritdoc />
public readonly bool Equals(Argb32P other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly int GetHashCode() => this.PackedValue.GetHashCode();
/// <inheritdoc />
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<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte();
return new Argb32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12));
}
}

42
src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs

@ -97,6 +97,48 @@ public partial struct Bgr24 : IPixel<Bgr24>
/// <inheritdoc/>
public static PixelOperations<Bgr24> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr24 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr24 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr24 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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr24 FromScaledVector4(Vector4 source) => FromVector4(source);

50
src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs

@ -7,14 +7,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// 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.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="Bgr565"/> struct.
/// <see cref="ToVector4"/> and scaled vector conversions return color components in <c>[0, 1]</c> with an implicit
/// alpha of <c>1</c>. The storage layout matches <c>DXGI_FORMAT_B5G6R5_UNORM</c>.
/// </remarks>
/// <param name="vector">
/// The vector containing the components for the packed value.
@ -75,6 +73,48 @@ public partial struct Bgr565(Vector3 vector) : IPixel<Bgr565>, IPackedVector<ush
/// <inheritdoc />
public static PixelOperations<Bgr565> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr565 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr565 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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgr565 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.ToScaledVector4());

43
src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs

@ -124,7 +124,7 @@ public partial struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
@ -136,6 +136,47 @@ public partial struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
/// <inheritdoc/>
public static PixelOperations<Bgra32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32 FromScaledVector4(Vector4 source) => FromVector4(source);

273
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;
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// The native component, packed, and vector representations use associated alpha.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public partial struct Bgra32P : IPixel<Bgra32P>, IPackedVector<uint>
{
private static readonly Vector4 Half = new(0.5F);
private static readonly Vector4 MaxBytes = new(byte.MaxValue);
/// <summary>
/// Gets or sets the associated blue component.
/// </summary>
public byte B;
/// <summary>
/// Gets or sets the associated green component.
/// </summary>
public byte G;
/// <summary>
/// Gets or sets the associated red component.
/// </summary>
public byte R;
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public byte A;
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Bgra32P(byte r, byte g, byte b)
: this(r, g, b, byte.MaxValue)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Bgra32P(byte r, byte g, byte b, byte a)
{
this.B = b;
this.G = g;
this.R = r;
this.A = a;
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
public Bgra32P(float r, float g, float b)
: this(new Vector4(r, g, b, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from associated components.
/// </summary>
/// <param name="r">The associated red component.</param>
/// <param name="g">The associated green component.</param>
/// <param name="b">The associated blue component.</param>
/// <param name="a">The alpha component.</param>
public Bgra32P(float r, float g, float b, float a)
: this(new Vector4(r, g, b, a))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Bgra32P(Vector3 vector)
: this(new Vector4(vector, 1F))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from an associated vector.
/// </summary>
/// <param name="vector">The associated vector.</param>
public Bgra32P(Vector4 vector)
: this() => this = FromScaledVector4(vector);
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32P"/> struct from a packed associated value.
/// </summary>
/// <param name="packed">The packed associated value.</param>
public Bgra32P(uint packed)
: this() => this.PackedValue = packed;
/// <inheritdoc />
public uint PackedValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get => Unsafe.As<Bgra32P, uint>(ref Unsafe.AsRef(in this));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Unsafe.As<Bgra32P, uint>(ref this) = value;
}
/// <summary>
/// Compares two <see cref="Bgra32P"/> values for equality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are equal.</returns>
public static bool operator ==(Bgra32P left, Bgra32P right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Bgra32P"/> values for inequality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are not equal.</returns>
public static bool operator !=(Bgra32P left, Bgra32P right) => !left.Equals(right);
/// <inheritdoc />
public readonly Rgba32 ToRgba32()
=> Rgba32.FromScaledVector4(Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(this.R, this.G, this.B, this.A));
/// <inheritdoc />
public readonly Vector4 ToScaledVector4() => new Vector4(this.R, this.G, this.B, this.A) / byte.MaxValue;
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
public readonly Vector4 ToVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToUnassociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
=> PixelTypeInfo.Create<Bgra32P>(
PixelComponentInfo.Create<Bgra32P>(4, 8, 8, 8, 8),
PixelColorType.BGR | PixelColorType.Alpha,
PixelAlphaRepresentation.Associated);
/// <inheritdoc />
public static PixelOperations<Bgra32P> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
public static Bgra32P FromVector4(Vector4 source) => FromAssociatedVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32P FromUnassociatedVector4(Vector4 source) => FromUnassociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32P FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra32P FromUnassociatedScaledVector4(Vector4 source)
=> Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4ToBgra32P(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
public static Bgra32P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static Bgra32P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is Bgra32P other && this.Equals(other);
/// <inheritdoc />
public readonly bool Equals(Bgra32P other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly int GetHashCode() => this.PackedValue.GetHashCode();
/// <inheritdoc />
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<byte> result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte();
return new Bgra32P(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12));
}
}

50
src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs

@ -7,11 +7,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w.
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// Packed pixel type containing four unsigned normalized values using 4 bits per component.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> and scaled vector conversions return all components in <c>[0, 1]</c>. The storage layout matches
/// <c>DXGI_FORMAT_B4G4R4A4_UNORM</c>.
/// </remarks>
public partial struct Bgra4444 : IPixel<Bgra4444>, IPackedVector<ushort>
{
/// <summary>
@ -88,6 +89,47 @@ public partial struct Bgra4444 : IPixel<Bgra4444>, IPackedVector<ushort>
/// <inheritdoc />
public static PixelOperations<Bgra4444> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra4444 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra4444 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra4444 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra4444 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra4444 FromScaledVector4(Vector4 source) => FromVector4(source);

52
src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs

@ -7,12 +7,13 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// 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.
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// Packed pixel type containing four unsigned normalized values.
/// The x, y, and z components use 5 bits, and the w component uses 1 bit.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> and scaled vector conversions return all components in <c>[0, 1]</c>. The storage layout matches
/// <c>DXGI_FORMAT_B5G5R5A1_UNORM</c>.
/// </remarks>
public partial struct Bgra5551 : IPixel<Bgra5551>, IPackedVector<ushort>
{
/// <summary>
@ -86,6 +87,47 @@ public partial struct Bgra5551 : IPixel<Bgra5551>, IPackedVector<ushort>
/// <inheritdoc />
public static PixelOperations<Bgra5551> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra5551 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra5551 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra5551 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra5551 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bgra5551 FromScaledVector4(Vector4 source) => FromVector4(source);

61
src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs

@ -8,11 +8,12 @@ using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255.
/// <para>
/// Ranges from [0, 0, 0, 0] to [255, 255, 255, 255] in vector form.
/// </para>
/// Packed pixel type containing four 8-bit unsigned integer values.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns components in <c>[0, 255]</c>. Scaled vector conversions map that range to
/// <c>[0, 1]</c>. The storage layout matches <c>DXGI_FORMAT_R8G8B8A8_UINT</c>.
/// </remarks>
public partial struct Byte4 : IPixel<Byte4>, IPackedVector<uint>
{
private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4();
@ -88,6 +89,58 @@ public partial struct Byte4 : IPixel<Byte4>, IPackedVector<uint>
/// <inheritdoc />
public static PixelOperations<Byte4> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4()
{
Vector4 vector = this.ToAssociatedScaledVector4();
// Native components use [0, 255], so association occurs in scaled [0, 1] space before mapping the result back.
return vector * MaxBytes;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Byte4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Byte4 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Byte4 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Byte4 FromScaledVector4(Vector4 source) => FromVector4(source * 255f);

64
src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs

@ -7,11 +7,13 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing a single 16 bit floating point value.
/// <para>
/// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form.
/// </para>
/// Packed pixel type containing a single IEEE 754 binary16 floating-point value.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns the stored IEEE 754 binary16 value directly. Scaled vector conversions normalize
/// the finite range <c>[-65504, 65504]</c> to <c>[0, 1]</c>. The packed representation is binary-compatible with
/// <c>DXGI_FORMAT_R16_FLOAT</c>.
/// </remarks>
public partial struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
{
/// <summary>
@ -53,9 +55,8 @@ public partial struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
float single = this.ToSingle() + 1F;
single /= 2F;
return new Vector4(single, 0, 0, 1F);
float scaled = HalfTypeHelper.ToScaled(this.ToSingle());
return new Vector4(scaled, 0, 0, 1F);
}
/// <inheritdoc />
@ -72,16 +73,53 @@ public partial struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
/// <inheritdoc />
public static PixelOperations<HalfSingle> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromScaledVector4(Vector4 source)
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromAssociatedScaledVector4(Vector4 source)
{
float scaled = source.X;
scaled *= 2F;
scaled--;
return new HalfSingle { PackedValue = HalfTypeHelper.Pack(scaled) };
// The destination has implicit alpha one, but associated input must be restored before its alpha is discarded.
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromScaledVector4(Vector4 source)
=> new() { PackedValue = HalfTypeHelper.Pack(HalfTypeHelper.FromScaled(source.X)) };
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfSingle FromVector4(Vector4 source) => new() { PackedValue = HalfTypeHelper.Pack(source.X) };

63
src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs

@ -7,11 +7,13 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing two 16-bit floating-point values.
/// <para>
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// Packed pixel type containing two IEEE 754 binary16 floating-point values.
/// </summary>
/// <remarks>
/// <see cref="ToVector2"/> and <see cref="ToVector4"/> return the stored IEEE 754 binary16 values directly. Scaled
/// vector conversions normalize the finite range <c>[-65504, 65504]</c> to <c>[0, 1]</c>. The packed representation is
/// binary-compatible with <c>DXGI_FORMAT_R16G16_FLOAT</c>.
/// </remarks>
public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
{
/// <summary>
@ -60,9 +62,7 @@ public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector2 scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2F;
Vector2 scaled = HalfTypeHelper.ToScaled(this.ToVector2());
return new Vector4(scaled, 0F, 1F);
}
@ -84,13 +84,56 @@ public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
/// <inheritdoc />
public static PixelOperations<HalfVector2> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector2 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector2 FromScaledVector4(Vector4 source)
{
Vector2 scaled = new Vector2(source.X, source.Y) * 2F;
scaled -= Vector2.One;
return new HalfVector2 { PackedValue = Pack(scaled.X, scaled.Y) };
Vector2 native = HalfTypeHelper.FromScaled(new Vector2(source.X, source.Y));
return new HalfVector2 { PackedValue = Pack(native.X, native.Y) };
}
/// <inheritdoc />

75
src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs

@ -7,11 +7,13 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing four 16-bit floating-point values.
/// <para>
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// Packed pixel type containing four IEEE 754 binary16 floating-point values.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns the stored IEEE 754 binary16 values directly. Scaled vector conversions normalize
/// the finite range <c>[-65504, 65504]</c> to <c>[0, 1]</c>. The packed representation is binary-compatible with
/// <c>DXGI_FORMAT_R16G16B16A16_FLOAT</c>.
/// </remarks>
public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
{
/// <summary>
@ -63,13 +65,7 @@ public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector4 scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2f;
return scaled;
}
public readonly Vector4 ToScaledVector4() => HalfTypeHelper.ToScaled(this.ToVector4());
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -89,15 +85,62 @@ public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
/// <inheritdoc />
public static PixelOperations<HalfVector4> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromScaledVector4(Vector4 source)
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4()
{
source *= 2f;
source -= Vector4.One;
return FromVector4(source);
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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromAssociatedVector4(Vector4 source)
{
// Restore scaled opacity before unassociating the color channels.
source = HalfTypeHelper.ToScaled(source);
return FromAssociatedScaledVector4(source);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromScaledVector4(Vector4 source) => FromVector4(HalfTypeHelper.FromScaled(source));
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4 FromVector4(Vector4 source) => new() { PackedValue = Pack(source) };

270
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;
/// <summary>
/// Packed pixel type containing four associated 16-bit floating-point values.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns the stored associated IEEE 754 binary16 values directly. Scaled vector conversions
/// normalize the finite range <c>[-65504, 65504]</c> to <c>[0, 1]</c> while preserving associated alpha. The packed
/// representation is binary-compatible with <c>DXGI_FORMAT_R16G16B16A16_FLOAT</c>.
/// </remarks>
public partial struct HalfVector4P : IPixel<HalfVector4P>, IPackedVector<ulong>
{
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4P"/> struct.
/// </summary>
/// <param name="x">The associated x-component.</param>
/// <param name="y">The associated y-component.</param>
/// <param name="z">The associated z-component.</param>
/// <param name="w">The alpha component.</param>
public HalfVector4P(float x, float y, float z, float w)
: this(new Vector4(x, y, z, w))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4P"/> struct.
/// </summary>
/// <param name="vector">The vector containing the associated component values.</param>
public HalfVector4P(Vector4 vector) => this.PackedValue = Pack(vector);
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfVector4P"/> values for equality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are equal.</returns>
public static bool operator ==(HalfVector4P left, HalfVector4P right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="HalfVector4P"/> values for inequality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are not equal.</returns>
public static bool operator !=(HalfVector4P left, HalfVector4P right) => !left.Equals(right);
/// <inheritdoc />
public readonly Rgba32 ToRgba32()
{
return Rgba32.FromScaledVector4(this.ToUnassociatedScaledVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4() => HalfTypeHelper.ToScaled(this.ToVector4());
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.UnPremultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
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)));
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4()
{
Vector4 vector = this.ToUnassociatedScaledVector4();
return HalfTypeHelper.FromScaled(vector);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
=> PixelTypeInfo.Create<HalfVector4P>(
PixelComponentInfo.Create<HalfVector4P>(4, 16, 16, 16, 16),
PixelColorType.RGB | PixelColorType.Alpha,
PixelAlphaRepresentation.Associated);
/// <inheritdoc />
public static PixelOperations<HalfVector4P> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
public static HalfVector4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
public static HalfVector4P FromVector4(Vector4 source) => FromAssociatedVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4P FromUnassociatedVector4(Vector4 source)
{
source = HalfTypeHelper.ToScaled(source);
return FromUnassociatedScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4P FromAssociatedVector4(Vector4 source)
{
source = HalfTypeHelper.ToScaled(source);
return FromAssociatedScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source));
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static HalfVector4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source));
/// <inheritdoc />
public static HalfVector4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static HalfVector4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is HalfVector4P other && this.Equals(other);
/// <inheritdoc />
public readonly bool Equals(HalfVector4P other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly int GetHashCode() => this.PackedValue.GetHashCode();
/// <inheritdoc />
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.##})");
}
/// <summary>
/// Converts an unassociated scaled vector to the associated representation of a half-precision destination.
/// </summary>
/// <param name="source">The unassociated scaled vector.</param>
/// <returns>The associated scaled vector.</returns>
[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;
}
/// <summary>
/// Reassociates a scaled vector with the alpha value the destination stores.
/// </summary>
/// <param name="source">The associated scaled vector.</param>
/// <returns>The reassociated scaled vector.</returns>
[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;
}
/// <summary>
/// Packs an associated scaled vector into the native binary16 representation.
/// </summary>
/// <param name="source">The associated scaled vector.</param>
/// <returns>The packed pixel.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static HalfVector4P PackAssociatedScaledVector4(Vector4 source)
{
source = HalfTypeHelper.FromScaled(source);
return new HalfVector4P { PackedValue = Pack(source) };
}
/// <summary>
/// Quantizes scaled alpha through the native binary16 representation.
/// </summary>
/// <param name="alpha">The scaled alpha value.</param>
/// <returns>The scaled value represented by the stored binary16 component.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float QuantizeScaledAlpha(float alpha)
{
float nativeAlpha = HalfTypeHelper.FromScaled(Numerics.Clamp(alpha, 0F, 1F));
return HalfTypeHelper.ToScaled(HalfTypeHelper.Unpack(HalfTypeHelper.Pack(nativeAlpha)));
}
/// <summary>
/// Packs native half-precision components into a 64-bit value.
/// </summary>
/// <param name="vector">The native component values.</param>
/// <returns>The packed value.</returns>
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;
}
}

42
src/ImageSharp/PixelFormats/PixelImplementations/L16.cs

@ -77,6 +77,48 @@ public partial struct L16 : IPixel<L16>, IPackedVector<ushort>
/// <inheritdoc />
public static PixelOperations<L16> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L16 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L16 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L16 FromScaledVector4(Vector4 source) => FromVector4(source);

42
src/ImageSharp/PixelFormats/PixelImplementations/L8.cs

@ -79,6 +79,48 @@ public partial struct L8 : IPixel<L8>, IPackedVector<byte>
/// <inheritdoc />
public static PixelOperations<L8> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L8 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L8 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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L8 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static L8 FromScaledVector4(Vector4 source) => FromVector4(source);

41
src/ImageSharp/PixelFormats/PixelImplementations/La16.cs

@ -106,6 +106,47 @@ public partial struct La16 : IPixel<La16>, IPackedVector<ushort>
/// <inheritdoc/>
public static PixelOperations<La16> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La16 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La16 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La16 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La16 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La16 FromScaledVector4(Vector4 source) => Pack(source);

41
src/ImageSharp/PixelFormats/PixelImplementations/La32.cs

@ -103,6 +103,47 @@ public partial struct La32 : IPixel<La32>, IPackedVector<uint>
/// <inheritdoc/>
public static PixelOperations<La32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToAssociatedScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La32 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La32 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La32 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La32 FromAssociatedVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static La32 FromScaledVector4(Vector4 source) => Pack(source);

79
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs

@ -7,15 +7,20 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// Packed pixel type containing two 8-bit signed normalized values.
/// </summary>
/// <remarks>
/// <see cref="ToVector2"/> and <see cref="ToVector4"/> return components in the native signed-normalized range
/// <c>[-1, 1]</c>. Scaled vector conversions return components in <c>[0, 1]</c>.
/// The packed two's-complement codes <c>-128</c> and <c>-127</c> both represent <c>-1</c>,
/// matching <c>DXGI_FORMAT_R8G8_SNORM</c>.
/// </remarks>
public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<ushort>
{
private const float MaxPos = 127f;
private const float ScaledMagnitude = MaxPos * 2F;
private static readonly Vector2 Half = new(MaxPos);
private static readonly Vector2 Minimum = new(-MaxPos);
private static readonly Vector2 MinusOne = new(-1f);
/// <summary>
@ -67,9 +72,14 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector2 scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2f;
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);
}
@ -87,6 +97,49 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
/// <inheritdoc />
public static PixelOperations<NormalizedByte2> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte2 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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte2 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte2 FromAssociatedVector4(Vector4 source)
{
// 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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte2 FromScaledVector4(Vector4 source)
@ -158,9 +211,15 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector2 ToVector2() => new(
(sbyte)((this.PackedValue >> 0) & 0xFF) / MaxPos,
(sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos);
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;
}
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is NormalizedByte2 other && this.Equals(other);

97
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs

@ -8,15 +8,20 @@ using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// Packed pixel type containing four 8-bit signed normalized values.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns components in the native signed-normalized range <c>[-1, 1]</c>.
/// Scaled vector conversions return components in <c>[0, 1]</c>.
/// The packed two's-complement codes <c>-128</c> and <c>-127</c> both represent <c>-1</c>,
/// matching <c>DXGI_FORMAT_R8G8B8A8_SNORM</c>.
/// </remarks>
public partial struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<uint>
{
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();
/// <summary>
@ -70,19 +75,30 @@ public partial struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<u
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector4 scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2f;
return scaled;
// 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;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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);
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;
}
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
@ -94,6 +110,61 @@ public partial struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<u
/// <inheritdoc />
public static PixelOperations<NormalizedByte4> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[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;
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4 FromScaledVector4(Vector4 source)

318
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;
/// <summary>
/// Packed pixel type containing four associated 8-bit signed normalized values ranging from -1 to 1.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns associated components in the native signed-normalized range <c>[-1, 1]</c>.
/// Scaled vector conversions return associated components in <c>[0, 1]</c>.
/// The packed two's-complement codes <c>-128</c> and <c>-127</c> both represent <c>-1</c>,
/// matching <c>DXGI_FORMAT_R8G8B8A8_SNORM</c>.
/// </remarks>
public partial struct NormalizedByte4P : IPixel<NormalizedByte4P>, IPackedVector<uint>
{
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();
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4P"/> struct.
/// </summary>
/// <param name="x">The associated x-component.</param>
/// <param name="y">The associated y-component.</param>
/// <param name="z">The associated z-component.</param>
/// <param name="w">The alpha component.</param>
public NormalizedByte4P(float x, float y, float z, float w)
: this(new Vector4(x, y, z, w))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4P"/> struct.
/// </summary>
/// <param name="vector">The vector containing the associated component values.</param>
public NormalizedByte4P(Vector4 vector) => this.PackedValue = Pack(vector);
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedByte4P"/> values for equality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are equal.</returns>
public static bool operator ==(NormalizedByte4P left, NormalizedByte4P right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="NormalizedByte4P"/> values for inequality.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns><see langword="true"/> when the values are not equal.</returns>
public static bool operator !=(NormalizedByte4P left, NormalizedByte4P right) => !left.Equals(right);
/// <inheritdoc />
public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(ToUnassociatedScaledVector4(this));
/// <inheritdoc />
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;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => ToUnassociatedScaledVector4(this);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
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;
}
/// <inheritdoc />
[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;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
=> PixelTypeInfo.Create<NormalizedByte4P>(
PixelComponentInfo.Create<NormalizedByte4P>(4, 8, 8, 8, 8),
PixelColorType.RGB | PixelColorType.Alpha,
PixelAlphaRepresentation.Associated);
/// <inheritdoc />
public static PixelOperations<NormalizedByte4P> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
public static NormalizedByte4P FromScaledVector4(Vector4 source) => FromAssociatedScaledVector4(source);
/// <inheritdoc />
public static NormalizedByte4P FromVector4(Vector4 source) => FromAssociatedVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4P FromUnassociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Associate(source));
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedByte4P FromAssociatedScaledVector4(Vector4 source) => PackAssociatedScaledVector4(Reassociate(source));
/// <summary>
/// Packs an associated scaled vector into signed-normalized storage.
/// </summary>
/// <param name="source">The associated scaled vector.</param>
/// <returns>The packed pixel.</returns>
[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) };
}
/// <inheritdoc />
public static NormalizedByte4P FromAbgr32(Abgr32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromArgb32(Argb32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromBgra5551(Bgra5551 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromBgr24(Bgr24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromBgra32(Bgra32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromL8(L8 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromL16(L16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromLa16(La16 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromLa32(La32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromRgb24(Rgb24 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromRgba32(Rgba32 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromRgb48(Rgb48 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public static NormalizedByte4P FromRgba64(Rgba64 source) => FromUnassociatedScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is NormalizedByte4P other && this.Equals(other);
/// <inheritdoc />
public readonly bool Equals(NormalizedByte4P other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly int GetHashCode() => this.PackedValue.GetHashCode();
/// <inheritdoc />
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.##})");
}
/// <summary>
/// Converts an unassociated scaled vector to the associated representation of a signed-normalized-byte destination.
/// </summary>
/// <param name="source">The unassociated scaled vector.</param>
/// <returns>The associated scaled vector.</returns>
[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;
}
/// <summary>
/// Converts the stored associated components to an unassociated scaled vector.
/// </summary>
/// <param name="source">The associated pixel.</param>
/// <returns>The unassociated scaled vector.</returns>
[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;
}
/// <summary>
/// Reassociates a scaled vector with the alpha value the destination stores.
/// </summary>
/// <param name="source">The associated scaled vector.</param>
/// <returns>The reassociated scaled vector.</returns>
[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;
}
/// <summary>
/// Packs native signed normalized components into a 32-bit value.
/// </summary>
/// <param name="vector">The native component values.</param>
/// <returns>The packed value.</returns>
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;
}
}

78
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs

@ -7,15 +7,19 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// Packed pixel type containing two 16-bit signed normalized values.
/// </summary>
/// <remarks>
/// <see cref="ToVector2"/> and <see cref="ToVector4"/> return components in the native signed-normalized range
/// <c>[-1, 1]</c>. Scaled vector conversions return components in <c>[0, 1]</c>.
/// The packed two's-complement codes <c>-32768</c> and <c>-32767</c> both represent <c>-1</c>,
/// matching <c>DXGI_FORMAT_R16G16_SNORM</c>.
/// </remarks>
public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector<uint>
{
// 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);
@ -69,9 +73,14 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector2 scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2f;
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);
}
@ -89,6 +98,49 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
/// <inheritdoc />
public static PixelOperations<NormalizedShort2> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort2 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort2 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);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort2 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort2 FromAssociatedVector4(Vector4 source)
{
// 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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort2 FromScaledVector4(Vector4 source)
@ -160,9 +212,15 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector2 ToVector2() => new(
(short)(this.PackedValue & 0xFFFF) / MaxPos,
(short)(this.PackedValue >> 0x10) / MaxPos);
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;
}
/// <inheritdoc />
public override readonly bool Equals(object? obj) => obj is NormalizedShort2 other && this.Equals(other);

96
src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs

@ -8,15 +8,19 @@ using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// Packed pixel type containing four 16-bit signed normalized values.
/// </summary>
/// <remarks>
/// <see cref="ToVector4"/> returns components in the native signed-normalized range <c>[-1, 1]</c>.
/// Scaled vector conversions return components in <c>[0, 1]</c>.
/// The packed two's-complement codes <c>-32768</c> and <c>-32767</c> both represent <c>-1</c>,
/// matching <c>DXGI_FORMAT_R16G16B16A16_SNORM</c>.
/// </remarks>
public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector<ulong>
{
// Largest two byte positive number 0xFFFF >> 1;
private const float MaxPos = 0x7FFF;
private const float ScaledMagnitude = MaxPos * 2F;
private static readonly Vector4 Max = Vector128.Create(MaxPos).AsVector4();
private static readonly Vector4 Min = Vector4.Negate(Max);
@ -71,19 +75,30 @@ public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToScaledVector4()
{
Vector4 scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2f;
return scaled;
// 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;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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);
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;
}
/// <inheritdoc />
public static PixelTypeInfo GetPixelTypeInfo()
@ -95,6 +110,61 @@ public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector
/// <inheritdoc />
public static PixelOperations<NormalizedShort4> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedScaledVector4() => this.ToScaledVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToAssociatedScaledVector4()
{
Vector4 vector = this.ToScaledVector4();
Numerics.Premultiply(ref vector);
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 ToUnassociatedVector4() => this.ToVector4();
/// <inheritdoc />
[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;
return vector;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort4 FromUnassociatedScaledVector4(Vector4 source) => FromScaledVector4(source);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort4 FromAssociatedScaledVector4(Vector4 source)
{
Numerics.UnPremultiply(ref source);
return FromScaledVector4(source);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort4 FromUnassociatedVector4(Vector4 source) => FromVector4(source);
/// <inheritdoc />
[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);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NormalizedShort4 FromScaledVector4(Vector4 source)

19
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/A8.PixelOperations.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.ImageSharp.PixelFormats;
/// <content>
@ -11,5 +13,20 @@ public partial struct A8
/// <summary>
/// Provides optimized overrides for bulk operations.
/// </summary>
internal class PixelOperations : PixelOperations<A8>;
internal class PixelOperations : PixelOperations<A8>
{
// A8 stores alpha without color components, so association cannot alter any emitted or consumed vector component.
/// <inheritdoc />
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<A8> source, Span<Vector4> destination) => this.ToUnassociatedVector4(configuration, source, destination);
/// <inheritdoc />
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<A8> source, Span<Vector4> destination) => this.ToUnassociatedScaledVector4(configuration, source, destination);
/// <inheritdoc />
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<A8> destination) => this.FromUnassociatedVector4Destructive(configuration, source, destination);
/// <inheritdoc />
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<A8> destination) => this.FromUnassociatedScaledVector4Destructive(configuration, source, destination);
}
}

64
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;
/// <content>
/// Provides optimized overrides for bulk operations.
/// </content>
public partial struct Abgr32P
{
/// <summary>
/// Provides optimized bulk operations for <see cref="Abgr32P"/>.
/// </summary>
internal class PixelOperations : AssociatedAlphaPixelOperations<Abgr32P>
{
/// <inheritdoc />
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination);
/// <inheritdoc />
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination);
/// <inheritdoc />
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> 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);
}
/// <inheritdoc />
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination)
{
// The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored.
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination);
/// <inheritdoc />
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<Abgr32P> source, Span<Vector4> destination)
{
// This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts.
Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination)
{
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Abgr32P> destination)
{
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination);
}
}
}

64
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;
/// <content>
/// Provides optimized overrides for bulk operations.
/// </content>
public partial struct Argb32P
{
/// <summary>
/// Provides optimized bulk operations for <see cref="Argb32P"/>.
/// </summary>
internal class PixelOperations : AssociatedAlphaPixelOperations<Argb32P>
{
/// <inheritdoc />
protected override void ToUnassociatedVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination);
/// <inheritdoc />
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination);
/// <inheritdoc />
protected override void FromUnassociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> 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);
}
/// <inheritdoc />
protected override void FromAssociatedVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination)
{
// The converter rescales RGB when alpha rounds so the channels remain associated with the byte alpha actually stored.
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void ToUnassociatedScaledVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination)
=> Vector4Converters.AssociatedRgbaCompatible.ToUnassociatedVector4(source, destination);
/// <inheritdoc />
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<Argb32P> source, Span<Vector4> destination)
{
// This byte format has the same normalized native and scaled ranges, so the unmodified converter satisfies both contracts.
Vector4Converters.AssociatedRgbaCompatible.ToAssociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void FromUnassociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination)
{
Vector4Converters.AssociatedRgbaCompatible.FromUnassociatedVector4(source, destination);
}
/// <inheritdoc />
protected override void FromAssociatedScaledVector4Destructive(Configuration configuration, Span<Vector4> source, Span<Argb32P> destination)
{
Vector4Converters.AssociatedRgbaCompatible.FromAssociatedVector4(source, destination);
}
}
}

13
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Bgr565.PixelOperations.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.ImageSharp.PixelFormats;
/// <content>
@ -11,5 +13,14 @@ public partial struct Bgr565
/// <summary>
/// Provides optimized overrides for bulk operations.
/// </summary>
internal class PixelOperations : PixelOperations<Bgr565>;
internal class PixelOperations : PixelOperations<Bgr565>
{
// Alpha is implicitly one, so both outward representations already contain associated color components.
/// <inheritdoc />
protected override void ToAssociatedVector4(Configuration configuration, ReadOnlySpan<Bgr565> source, Span<Vector4> destination) => this.ToUnassociatedVector4(configuration, source, destination);
/// <inheritdoc />
protected override void ToAssociatedScaledVector4(Configuration configuration, ReadOnlySpan<Bgr565> source, Span<Vector4> destination) => this.ToUnassociatedScaledVector4(configuration, source, destination);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save