Browse Source

Merge branch 'main' into heic-support

pull/2633/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
03eda75eab
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .editorconfig
  2. 1
      ImageSharp.sln
  3. 2
      shared-infrastructure
  4. 3
      src/ImageSharp.ruleset
  5. 240
      src/ImageSharp/Color/Color.Conversions.cs
  6. 288
      src/ImageSharp/Color/Color.NamedColors.cs
  7. 155
      src/ImageSharp/Color/Color.cs
  8. 37
      src/ImageSharp/Common/Helpers/ColorNumerics.cs
  9. 7
      src/ImageSharp/Common/Helpers/Numerics.cs
  10. 194
      src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
  11. 28
      src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
  12. 20
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
  13. 178
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs
  14. 30
      src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
  15. 466
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  16. 125
      src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
  17. 176
      src/ImageSharp/Common/Helpers/Vector128Utilities.cs
  18. 78
      src/ImageSharp/Common/Helpers/Vector256Utilities.cs
  19. 80
      src/ImageSharp/Common/Helpers/Vector512Utilities.cs
  20. 4
      src/ImageSharp/Formats/AnimationUtilities.cs
  21. 210
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  22. 20
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  23. 7
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  24. 53
      src/ImageSharp/Formats/PixelTypeInfo.cs
  25. 2
      src/ImageSharp/Formats/Png/PngBitDepth.cs
  26. 9
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  27. 117
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  28. 119
      src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
  29. 14
      src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs
  30. 108
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  31. 10
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  32. 5
      src/ImageSharp/Formats/Tga/TgaFileHeader.cs
  33. 140
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  34. 10
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
  35. 6
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs
  36. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs
  37. 11
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
  38. 13
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
  39. 25
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs
  40. 13
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
  41. 15
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs
  42. 10
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs
  43. 8
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs
  44. 13
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
  45. 15
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
  46. 22
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
  47. 20
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
  48. 20
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
  49. 20
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs
  50. 21
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs
  51. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs
  52. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
  53. 13
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
  54. 8
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
  55. 23
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
  56. 34
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
  57. 33
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
  58. 32
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
  59. 33
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
  60. 34
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
  61. 5
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
  62. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
  63. 18
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
  64. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
  65. 14
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
  66. 7
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs
  67. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
  68. 11
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
  69. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
  70. 25
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs
  71. 8
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs
  72. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
  73. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs
  74. 11
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs
  75. 6
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  76. 120
      src/ImageSharp/Formats/Tiff/Utils/TiffUtilities.cs
  77. 176
      src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
  78. 4
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  79. 1
      src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs
  80. 2
      src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs
  81. 2
      src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs
  82. 1
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  83. 1
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  84. 4
      src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs
  85. 2
      src/ImageSharp/Formats/Webp/RiffHelper.cs
  86. 2
      src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
  87. 2
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  88. 2
      src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
  89. 2
      src/ImageSharp/ImageInfo.cs
  90. 9
      src/ImageSharp/Image{TPixel}.cs
  91. 2
      src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs
  92. 119
      src/ImageSharp/PixelFormats/IPixel.cs
  93. 432
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  94. 4
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  95. 432
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  96. 4
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  97. 46
      src/ImageSharp/PixelFormats/PixelColorType.cs
  98. 50
      src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs
  99. 111
      src/ImageSharp/PixelFormats/PixelComponentInfo.cs
  100. 8
      src/ImageSharp/PixelFormats/PixelConversionModifiers.cs

2
.editorconfig

@ -172,6 +172,8 @@ dotnet_diagnostic.IDE0063.severity = suggestion
csharp_using_directive_placement = outside_namespace:warning csharp_using_directive_placement = outside_namespace:warning
# Modifier preferences # Modifier preferences
csharp_prefer_static_local_function = true:warning csharp_prefer_static_local_function = true:warning
# Primary constructor preferences
csharp_style_prefer_primary_constructors = false:none
########################################## ##########################################
# Unnecessary Code Rules # Unnecessary Code Rules

1
ImageSharp.sln

@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3
src\Directory.Build.props = src\Directory.Build.props src\Directory.Build.props = src\Directory.Build.props
src\Directory.Build.targets = src\Directory.Build.targets src\Directory.Build.targets = src\Directory.Build.targets
src\README.md = src\README.md src\README.md = src\README.md
src\ImageSharp.ruleset = src\ImageSharp.ruleset
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 1c526a97eea8bcbc7c79de095676f7fb975a9fb1 Subproject commit 1dbfb576c83507645265c79e03369b66cdc0379f

3
src/ImageSharp.ruleset

@ -1,7 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="ImageSharp" ToolsVersion="17.0"> <RuleSet Name="ImageSharp" ToolsVersion="17.0">
<Include Path="..\shared-infrastructure\sixlabors.ruleset" Action="Default" /> <Include Path="..\shared-infrastructure\sixlabors.ruleset" Action="Default" />
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1011" Action="None" />
</Rules>
</RuleSet> </RuleSet>

240
src/ImageSharp/Color/Color.Conversions.cs

@ -1,240 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp;
/// <content>
/// Contains constructors and implicit conversion methods.
/// </content>
public readonly partial struct Color
{
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Rgba64"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Rgba64 pixel)
{
this.data = pixel;
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Rgb48"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Rgb48 pixel)
{
this.data = new Rgba64(pixel.R, pixel.G, pixel.B, ushort.MaxValue);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="La32"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(La32 pixel)
{
this.data = new Rgba64(pixel.L, pixel.L, pixel.L, pixel.A);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="L16"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(L16 pixel)
{
this.data = new Rgba64(pixel.PackedValue, pixel.PackedValue, pixel.PackedValue, ushort.MaxValue);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Rgba32"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Rgba32 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Argb32"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Argb32 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Bgra32"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Bgra32 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Abgr32"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Abgr32 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Rgb24"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Rgb24 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The <see cref="Bgr24"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Bgr24 pixel)
{
this.data = new Rgba64(pixel);
this.boxedHighPrecisionPixel = null;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">The <see cref="Vector4"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Vector4 vector)
{
vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
this.boxedHighPrecisionPixel = new RgbaVector(vector.X, vector.Y, vector.Z, vector.W);
this.data = default;
}
/// <summary>
/// Converts a <see cref="Color"/> to <see cref="Vector4"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/>.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static explicit operator Vector4(Color color) => color.ToScaledVector4();
/// <summary>
/// Converts an <see cref="Vector4"/> to <see cref="Color"/>.
/// </summary>
/// <param name="source">The <see cref="Vector4"/>.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static explicit operator Color(Vector4 source) => new(source);
[MethodImpl(InliningOptions.ShortMethod)]
internal Rgba32 ToRgba32()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToRgba32();
}
Rgba32 value = default;
this.boxedHighPrecisionPixel.ToRgba32(ref value);
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Bgra32 ToBgra32()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToBgra32();
}
Bgra32 value = default;
value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Argb32 ToArgb32()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToArgb32();
}
Argb32 value = default;
value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Abgr32 ToAbgr32()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToAbgr32();
}
Abgr32 value = default;
value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Rgb24 ToRgb24()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToRgb24();
}
Rgb24 value = default;
value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Bgr24 ToBgr24()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToBgr24();
}
Bgr24 value = default;
value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return value;
}
[MethodImpl(InliningOptions.ShortMethod)]
internal Vector4 ToScaledVector4()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data.ToScaledVector4();
}
return this.boxedHighPrecisionPixel.ToScaledVector4();
}
}

288
src/ImageSharp/Color/Color.NamedColors.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;
/// <content> /// <content>
@ -9,107 +11,107 @@ namespace SixLabors.ImageSharp;
/// </content> /// </content>
public readonly partial struct Color public readonly partial struct Color
{ {
private static readonly Lazy<Dictionary<string, Color>> NamedColorsLookupLazy = new Lazy<Dictionary<string, Color>>(CreateNamedColorsLookup, true); private static readonly Lazy<Dictionary<string, Color>> NamedColorsLookupLazy = new(CreateNamedColorsLookup, true);
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0F8FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0F8FF.
/// </summary> /// </summary>
public static readonly Color AliceBlue = FromRgba(240, 248, 255, 255); public static readonly Color AliceBlue = FromPixel(new Rgba32(240, 248, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAEBD7. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAEBD7.
/// </summary> /// </summary>
public static readonly Color AntiqueWhite = FromRgba(250, 235, 215, 255); public static readonly Color AntiqueWhite = FromPixel(new Rgba32(250, 235, 215, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary> /// </summary>
public static readonly Color Aqua = FromRgba(0, 255, 255, 255); public static readonly Color Aqua = FromPixel(new Rgba32(0, 255, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7FFFD4. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7FFFD4.
/// </summary> /// </summary>
public static readonly Color Aquamarine = FromRgba(127, 255, 212, 255); public static readonly Color Aquamarine = FromPixel(new Rgba32(127, 255, 212, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0FFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0FFFF.
/// </summary> /// </summary>
public static readonly Color Azure = FromRgba(240, 255, 255, 255); public static readonly Color Azure = FromPixel(new Rgba32(240, 255, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5F5DC. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5F5DC.
/// </summary> /// </summary>
public static readonly Color Beige = FromRgba(245, 245, 220, 255); public static readonly Color Beige = FromPixel(new Rgba32(245, 245, 220, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4C4. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4C4.
/// </summary> /// </summary>
public static readonly Color Bisque = FromRgba(255, 228, 196, 255); public static readonly Color Bisque = FromPixel(new Rgba32(255, 228, 196, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #000000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #000000.
/// </summary> /// </summary>
public static readonly Color Black = FromRgba(0, 0, 0, 255); public static readonly Color Black = FromPixel(new Rgba32(0, 0, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFEBCD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFEBCD.
/// </summary> /// </summary>
public static readonly Color BlanchedAlmond = FromRgba(255, 235, 205, 255); public static readonly Color BlanchedAlmond = FromPixel(new Rgba32(255, 235, 205, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #0000FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #0000FF.
/// </summary> /// </summary>
public static readonly Color Blue = FromRgba(0, 0, 255, 255); public static readonly Color Blue = FromPixel(new Rgba32(0, 0, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8A2BE2. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8A2BE2.
/// </summary> /// </summary>
public static readonly Color BlueViolet = FromRgba(138, 43, 226, 255); public static readonly Color BlueViolet = FromPixel(new Rgba32(138, 43, 226, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A52A2A. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A52A2A.
/// </summary> /// </summary>
public static readonly Color Brown = FromRgba(165, 42, 42, 255); public static readonly Color Brown = FromPixel(new Rgba32(165, 42, 42, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DEB887. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DEB887.
/// </summary> /// </summary>
public static readonly Color BurlyWood = FromRgba(222, 184, 135, 255); public static readonly Color BurlyWood = FromPixel(new Rgba32(222, 184, 135, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #5F9EA0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #5F9EA0.
/// </summary> /// </summary>
public static readonly Color CadetBlue = FromRgba(95, 158, 160, 255); public static readonly Color CadetBlue = FromPixel(new Rgba32(95, 158, 160, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7FFF00. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7FFF00.
/// </summary> /// </summary>
public static readonly Color Chartreuse = FromRgba(127, 255, 0, 255); public static readonly Color Chartreuse = FromPixel(new Rgba32(127, 255, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D2691E. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D2691E.
/// </summary> /// </summary>
public static readonly Color Chocolate = FromRgba(210, 105, 30, 255); public static readonly Color Chocolate = FromPixel(new Rgba32(210, 105, 30, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF7F50. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF7F50.
/// </summary> /// </summary>
public static readonly Color Coral = FromRgba(255, 127, 80, 255); public static readonly Color Coral = FromPixel(new Rgba32(255, 127, 80, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6495ED. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6495ED.
/// </summary> /// </summary>
public static readonly Color CornflowerBlue = FromRgba(100, 149, 237, 255); public static readonly Color CornflowerBlue = FromPixel(new Rgba32(100, 149, 237, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF8DC. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF8DC.
/// </summary> /// </summary>
public static readonly Color Cornsilk = FromRgba(255, 248, 220, 255); public static readonly Color Cornsilk = FromPixel(new Rgba32(255, 248, 220, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DC143C. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DC143C.
/// </summary> /// </summary>
public static readonly Color Crimson = FromRgba(220, 20, 60, 255); public static readonly Color Crimson = FromPixel(new Rgba32(220, 20, 60, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FFFF.
@ -119,27 +121,27 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00008B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00008B.
/// </summary> /// </summary>
public static readonly Color DarkBlue = FromRgba(0, 0, 139, 255); public static readonly Color DarkBlue = FromPixel(new Rgba32(0, 0, 139, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008B8B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008B8B.
/// </summary> /// </summary>
public static readonly Color DarkCyan = FromRgba(0, 139, 139, 255); public static readonly Color DarkCyan = FromPixel(new Rgba32(0, 139, 139, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B8860B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B8860B.
/// </summary> /// </summary>
public static readonly Color DarkGoldenrod = FromRgba(184, 134, 11, 255); public static readonly Color DarkGoldenrod = FromPixel(new Rgba32(184, 134, 11, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A9A9A9. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A9A9A9.
/// </summary> /// </summary>
public static readonly Color DarkGray = FromRgba(169, 169, 169, 255); public static readonly Color DarkGray = FromPixel(new Rgba32(169, 169, 169, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #006400. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #006400.
/// </summary> /// </summary>
public static readonly Color DarkGreen = FromRgba(0, 100, 0, 255); public static readonly Color DarkGreen = FromPixel(new Rgba32(0, 100, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A9A9A9. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A9A9A9.
@ -149,52 +151,52 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BDB76B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BDB76B.
/// </summary> /// </summary>
public static readonly Color DarkKhaki = FromRgba(189, 183, 107, 255); public static readonly Color DarkKhaki = FromPixel(new Rgba32(189, 183, 107, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B008B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B008B.
/// </summary> /// </summary>
public static readonly Color DarkMagenta = FromRgba(139, 0, 139, 255); public static readonly Color DarkMagenta = FromPixel(new Rgba32(139, 0, 139, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #556B2F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #556B2F.
/// </summary> /// </summary>
public static readonly Color DarkOliveGreen = FromRgba(85, 107, 47, 255); public static readonly Color DarkOliveGreen = FromPixel(new Rgba32(85, 107, 47, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF8C00. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF8C00.
/// </summary> /// </summary>
public static readonly Color DarkOrange = FromRgba(255, 140, 0, 255); public static readonly Color DarkOrange = FromPixel(new Rgba32(255, 140, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9932CC. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9932CC.
/// </summary> /// </summary>
public static readonly Color DarkOrchid = FromRgba(153, 50, 204, 255); public static readonly Color DarkOrchid = FromPixel(new Rgba32(153, 50, 204, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B0000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B0000.
/// </summary> /// </summary>
public static readonly Color DarkRed = FromRgba(139, 0, 0, 255); public static readonly Color DarkRed = FromPixel(new Rgba32(139, 0, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E9967A. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E9967A.
/// </summary> /// </summary>
public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); public static readonly Color DarkSalmon = FromPixel(new Rgba32(233, 150, 122, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8F.
/// </summary> /// </summary>
public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255); public static readonly Color DarkSeaGreen = FromPixel(new Rgba32(143, 188, 143, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #483D8B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #483D8B.
/// </summary> /// </summary>
public static readonly Color DarkSlateBlue = FromRgba(72, 61, 139, 255); public static readonly Color DarkSlateBlue = FromPixel(new Rgba32(72, 61, 139, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2F4F4F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2F4F4F.
/// </summary> /// </summary>
public static readonly Color DarkSlateGray = FromRgba(47, 79, 79, 255); public static readonly Color DarkSlateGray = FromPixel(new Rgba32(47, 79, 79, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2F4F4F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2F4F4F.
@ -204,27 +206,27 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00CED1. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00CED1.
/// </summary> /// </summary>
public static readonly Color DarkTurquoise = FromRgba(0, 206, 209, 255); public static readonly Color DarkTurquoise = FromPixel(new Rgba32(0, 206, 209, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9400D3. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9400D3.
/// </summary> /// </summary>
public static readonly Color DarkViolet = FromRgba(148, 0, 211, 255); public static readonly Color DarkViolet = FromPixel(new Rgba32(148, 0, 211, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF1493. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF1493.
/// </summary> /// </summary>
public static readonly Color DeepPink = FromRgba(255, 20, 147, 255); public static readonly Color DeepPink = FromPixel(new Rgba32(255, 20, 147, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00BFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00BFFF.
/// </summary> /// </summary>
public static readonly Color DeepSkyBlue = FromRgba(0, 191, 255, 255); public static readonly Color DeepSkyBlue = FromPixel(new Rgba32(0, 191, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #696969. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #696969.
/// </summary> /// </summary>
public static readonly Color DimGray = FromRgba(105, 105, 105, 255); public static readonly Color DimGray = FromPixel(new Rgba32(105, 105, 105, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #696969. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #696969.
@ -234,62 +236,62 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #1E90FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #1E90FF.
/// </summary> /// </summary>
public static readonly Color DodgerBlue = FromRgba(30, 144, 255, 255); public static readonly Color DodgerBlue = FromPixel(new Rgba32(30, 144, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B22222. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B22222.
/// </summary> /// </summary>
public static readonly Color Firebrick = FromRgba(178, 34, 34, 255); public static readonly Color Firebrick = FromPixel(new Rgba32(178, 34, 34, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFAF0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFAF0.
/// </summary> /// </summary>
public static readonly Color FloralWhite = FromRgba(255, 250, 240, 255); public static readonly Color FloralWhite = FromPixel(new Rgba32(255, 250, 240, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #228B22. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #228B22.
/// </summary> /// </summary>
public static readonly Color ForestGreen = FromRgba(34, 139, 34, 255); public static readonly Color ForestGreen = FromPixel(new Rgba32(34, 139, 34, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF00FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary> /// </summary>
public static readonly Color Fuchsia = FromRgba(255, 0, 255, 255); public static readonly Color Fuchsia = FromPixel(new Rgba32(255, 0, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DCDCDC. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DCDCDC.
/// </summary> /// </summary>
public static readonly Color Gainsboro = FromRgba(220, 220, 220, 255); public static readonly Color Gainsboro = FromPixel(new Rgba32(220, 220, 220, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F8F8FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F8F8FF.
/// </summary> /// </summary>
public static readonly Color GhostWhite = FromRgba(248, 248, 255, 255); public static readonly Color GhostWhite = FromPixel(new Rgba32(248, 248, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFD700. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFD700.
/// </summary> /// </summary>
public static readonly Color Gold = FromRgba(255, 215, 0, 255); public static readonly Color Gold = FromPixel(new Rgba32(255, 215, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DAA520. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DAA520.
/// </summary> /// </summary>
public static readonly Color Goldenrod = FromRgba(218, 165, 32, 255); public static readonly Color Goldenrod = FromPixel(new Rgba32(218, 165, 32, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808080.
/// </summary> /// </summary>
public static readonly Color Gray = FromRgba(128, 128, 128, 255); public static readonly Color Gray = FromPixel(new Rgba32(128, 128, 128, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008000.
/// </summary> /// </summary>
public static readonly Color Green = FromRgba(0, 128, 0, 255); public static readonly Color Green = FromPixel(new Rgba32(0, 128, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #ADFF2F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #ADFF2F.
/// </summary> /// </summary>
public static readonly Color GreenYellow = FromRgba(173, 255, 47, 255); public static readonly Color GreenYellow = FromPixel(new Rgba32(173, 255, 47, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808080.
@ -299,82 +301,82 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0FFF0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0FFF0.
/// </summary> /// </summary>
public static readonly Color Honeydew = FromRgba(240, 255, 240, 255); public static readonly Color Honeydew = FromPixel(new Rgba32(240, 255, 240, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF69B4. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF69B4.
/// </summary> /// </summary>
public static readonly Color HotPink = FromRgba(255, 105, 180, 255); public static readonly Color HotPink = FromPixel(new Rgba32(255, 105, 180, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #CD5C5C. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #CD5C5C.
/// </summary> /// </summary>
public static readonly Color IndianRed = FromRgba(205, 92, 92, 255); public static readonly Color IndianRed = FromPixel(new Rgba32(205, 92, 92, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4B0082. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4B0082.
/// </summary> /// </summary>
public static readonly Color Indigo = FromRgba(75, 0, 130, 255); public static readonly Color Indigo = FromPixel(new Rgba32(75, 0, 130, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFF0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFF0.
/// </summary> /// </summary>
public static readonly Color Ivory = FromRgba(255, 255, 240, 255); public static readonly Color Ivory = FromPixel(new Rgba32(255, 255, 240, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0E68C. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0E68C.
/// </summary> /// </summary>
public static readonly Color Khaki = FromRgba(240, 230, 140, 255); public static readonly Color Khaki = FromPixel(new Rgba32(240, 230, 140, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E6E6FA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E6E6FA.
/// </summary> /// </summary>
public static readonly Color Lavender = FromRgba(230, 230, 250, 255); public static readonly Color Lavender = FromPixel(new Rgba32(230, 230, 250, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF0F5. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF0F5.
/// </summary> /// </summary>
public static readonly Color LavenderBlush = FromRgba(255, 240, 245, 255); public static readonly Color LavenderBlush = FromPixel(new Rgba32(255, 240, 245, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7CFC00. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7CFC00.
/// </summary> /// </summary>
public static readonly Color LawnGreen = FromRgba(124, 252, 0, 255); public static readonly Color LawnGreen = FromPixel(new Rgba32(124, 252, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFACD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFACD.
/// </summary> /// </summary>
public static readonly Color LemonChiffon = FromRgba(255, 250, 205, 255); public static readonly Color LemonChiffon = FromPixel(new Rgba32(255, 250, 205, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #ADD8E6. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #ADD8E6.
/// </summary> /// </summary>
public static readonly Color LightBlue = FromRgba(173, 216, 230, 255); public static readonly Color LightBlue = FromPixel(new Rgba32(173, 216, 230, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F08080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F08080.
/// </summary> /// </summary>
public static readonly Color LightCoral = FromRgba(240, 128, 128, 255); public static readonly Color LightCoral = FromPixel(new Rgba32(240, 128, 128, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E0FFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #E0FFFF.
/// </summary> /// </summary>
public static readonly Color LightCyan = FromRgba(224, 255, 255, 255); public static readonly Color LightCyan = FromPixel(new Rgba32(224, 255, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAFAD2. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAFAD2.
/// </summary> /// </summary>
public static readonly Color LightGoldenrodYellow = FromRgba(250, 250, 210, 255); public static readonly Color LightGoldenrodYellow = FromPixel(new Rgba32(250, 250, 210, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D3D3D3. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D3D3D3.
/// </summary> /// </summary>
public static readonly Color LightGray = FromRgba(211, 211, 211, 255); public static readonly Color LightGray = FromPixel(new Rgba32(211, 211, 211, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #90EE90. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #90EE90.
/// </summary> /// </summary>
public static readonly Color LightGreen = FromRgba(144, 238, 144, 255); public static readonly Color LightGreen = FromPixel(new Rgba32(144, 238, 144, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D3D3D3. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D3D3D3.
@ -384,27 +386,27 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFB6C1. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFB6C1.
/// </summary> /// </summary>
public static readonly Color LightPink = FromRgba(255, 182, 193, 255); public static readonly Color LightPink = FromPixel(new Rgba32(255, 182, 193, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFA07A. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFA07A.
/// </summary> /// </summary>
public static readonly Color LightSalmon = FromRgba(255, 160, 122, 255); public static readonly Color LightSalmon = FromPixel(new Rgba32(255, 160, 122, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #20B2AA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #20B2AA.
/// </summary> /// </summary>
public static readonly Color LightSeaGreen = FromRgba(32, 178, 170, 255); public static readonly Color LightSeaGreen = FromPixel(new Rgba32(32, 178, 170, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #87CEFA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #87CEFA.
/// </summary> /// </summary>
public static readonly Color LightSkyBlue = FromRgba(135, 206, 250, 255); public static readonly Color LightSkyBlue = FromPixel(new Rgba32(135, 206, 250, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #778899. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #778899.
/// </summary> /// </summary>
public static readonly Color LightSlateGray = FromRgba(119, 136, 153, 255); public static readonly Color LightSlateGray = FromPixel(new Rgba32(119, 136, 153, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #778899. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #778899.
@ -414,27 +416,27 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B0C4DE. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B0C4DE.
/// </summary> /// </summary>
public static readonly Color LightSteelBlue = FromRgba(176, 196, 222, 255); public static readonly Color LightSteelBlue = FromPixel(new Rgba32(176, 196, 222, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFE0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFE0.
/// </summary> /// </summary>
public static readonly Color LightYellow = FromRgba(255, 255, 224, 255); public static readonly Color LightYellow = FromPixel(new Rgba32(255, 255, 224, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FF00. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FF00.
/// </summary> /// </summary>
public static readonly Color Lime = FromRgba(0, 255, 0, 255); public static readonly Color Lime = FromPixel(new Rgba32(0, 255, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #32CD32. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #32CD32.
/// </summary> /// </summary>
public static readonly Color LimeGreen = FromRgba(50, 205, 50, 255); public static readonly Color LimeGreen = FromPixel(new Rgba32(50, 205, 50, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAF0E6. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FAF0E6.
/// </summary> /// </summary>
public static readonly Color Linen = FromRgba(250, 240, 230, 255); public static readonly Color Linen = FromPixel(new Rgba32(250, 240, 230, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF00FF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF00FF.
@ -444,237 +446,237 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #800000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #800000.
/// </summary> /// </summary>
public static readonly Color Maroon = FromRgba(128, 0, 0, 255); public static readonly Color Maroon = FromPixel(new Rgba32(128, 0, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #66CDAA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #66CDAA.
/// </summary> /// </summary>
public static readonly Color MediumAquamarine = FromRgba(102, 205, 170, 255); public static readonly Color MediumAquamarine = FromPixel(new Rgba32(102, 205, 170, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #0000CD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #0000CD.
/// </summary> /// </summary>
public static readonly Color MediumBlue = FromRgba(0, 0, 205, 255); public static readonly Color MediumBlue = FromPixel(new Rgba32(0, 0, 205, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BA55D3. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BA55D3.
/// </summary> /// </summary>
public static readonly Color MediumOrchid = FromRgba(186, 85, 211, 255); public static readonly Color MediumOrchid = FromPixel(new Rgba32(186, 85, 211, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9370DB. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9370DB.
/// </summary> /// </summary>
public static readonly Color MediumPurple = FromRgba(147, 112, 219, 255); public static readonly Color MediumPurple = FromPixel(new Rgba32(147, 112, 219, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #3CB371. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #3CB371.
/// </summary> /// </summary>
public static readonly Color MediumSeaGreen = FromRgba(60, 179, 113, 255); public static readonly Color MediumSeaGreen = FromPixel(new Rgba32(60, 179, 113, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7B68EE. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #7B68EE.
/// </summary> /// </summary>
public static readonly Color MediumSlateBlue = FromRgba(123, 104, 238, 255); public static readonly Color MediumSlateBlue = FromPixel(new Rgba32(123, 104, 238, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FA9A. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FA9A.
/// </summary> /// </summary>
public static readonly Color MediumSpringGreen = FromRgba(0, 250, 154, 255); public static readonly Color MediumSpringGreen = FromPixel(new Rgba32(0, 250, 154, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #48D1CC. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #48D1CC.
/// </summary> /// </summary>
public static readonly Color MediumTurquoise = FromRgba(72, 209, 204, 255); public static readonly Color MediumTurquoise = FromPixel(new Rgba32(72, 209, 204, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #C71585. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #C71585.
/// </summary> /// </summary>
public static readonly Color MediumVioletRed = FromRgba(199, 21, 133, 255); public static readonly Color MediumVioletRed = FromPixel(new Rgba32(199, 21, 133, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #191970. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #191970.
/// </summary> /// </summary>
public static readonly Color MidnightBlue = FromRgba(25, 25, 112, 255); public static readonly Color MidnightBlue = FromPixel(new Rgba32(25, 25, 112, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5FFFA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5FFFA.
/// </summary> /// </summary>
public static readonly Color MintCream = FromRgba(245, 255, 250, 255); public static readonly Color MintCream = FromPixel(new Rgba32(245, 255, 250, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4E1. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4E1.
/// </summary> /// </summary>
public static readonly Color MistyRose = FromRgba(255, 228, 225, 255); public static readonly Color MistyRose = FromPixel(new Rgba32(255, 228, 225, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4B5. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFE4B5.
/// </summary> /// </summary>
public static readonly Color Moccasin = FromRgba(255, 228, 181, 255); public static readonly Color Moccasin = FromPixel(new Rgba32(255, 228, 181, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFDEAD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFDEAD.
/// </summary> /// </summary>
public static readonly Color NavajoWhite = FromRgba(255, 222, 173, 255); public static readonly Color NavajoWhite = FromPixel(new Rgba32(255, 222, 173, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #000080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #000080.
/// </summary> /// </summary>
public static readonly Color Navy = FromRgba(0, 0, 128, 255); public static readonly Color Navy = FromPixel(new Rgba32(0, 0, 128, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FDF5E6. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FDF5E6.
/// </summary> /// </summary>
public static readonly Color OldLace = FromRgba(253, 245, 230, 255); public static readonly Color OldLace = FromPixel(new Rgba32(253, 245, 230, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808000.
/// </summary> /// </summary>
public static readonly Color Olive = FromRgba(128, 128, 0, 255); public static readonly Color Olive = FromPixel(new Rgba32(128, 128, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6B8E23. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6B8E23.
/// </summary> /// </summary>
public static readonly Color OliveDrab = FromRgba(107, 142, 35, 255); public static readonly Color OliveDrab = FromPixel(new Rgba32(107, 142, 35, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFA500. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFA500.
/// </summary> /// </summary>
public static readonly Color Orange = FromRgba(255, 165, 0, 255); public static readonly Color Orange = FromPixel(new Rgba32(255, 165, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF4500. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF4500.
/// </summary> /// </summary>
public static readonly Color OrangeRed = FromRgba(255, 69, 0, 255); public static readonly Color OrangeRed = FromPixel(new Rgba32(255, 69, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DA70D6. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DA70D6.
/// </summary> /// </summary>
public static readonly Color Orchid = FromRgba(218, 112, 214, 255); public static readonly Color Orchid = FromPixel(new Rgba32(218, 112, 214, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #EEE8AA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #EEE8AA.
/// </summary> /// </summary>
public static readonly Color PaleGoldenrod = FromRgba(238, 232, 170, 255); public static readonly Color PaleGoldenrod = FromPixel(new Rgba32(238, 232, 170, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #98FB98. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #98FB98.
/// </summary> /// </summary>
public static readonly Color PaleGreen = FromRgba(152, 251, 152, 255); public static readonly Color PaleGreen = FromPixel(new Rgba32(152, 251, 152, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #AFEEEE. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #AFEEEE.
/// </summary> /// </summary>
public static readonly Color PaleTurquoise = FromRgba(175, 238, 238, 255); public static readonly Color PaleTurquoise = FromPixel(new Rgba32(175, 238, 238, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DB7093. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DB7093.
/// </summary> /// </summary>
public static readonly Color PaleVioletRed = FromRgba(219, 112, 147, 255); public static readonly Color PaleVioletRed = FromPixel(new Rgba32(219, 112, 147, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFEFD5. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFEFD5.
/// </summary> /// </summary>
public static readonly Color PapayaWhip = FromRgba(255, 239, 213, 255); public static readonly Color PapayaWhip = FromPixel(new Rgba32(255, 239, 213, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFDAB9. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFDAB9.
/// </summary> /// </summary>
public static readonly Color PeachPuff = FromRgba(255, 218, 185, 255); public static readonly Color PeachPuff = FromPixel(new Rgba32(255, 218, 185, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #CD853F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #CD853F.
/// </summary> /// </summary>
public static readonly Color Peru = FromRgba(205, 133, 63, 255); public static readonly Color Peru = FromPixel(new Rgba32(205, 133, 63, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFC0CB. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFC0CB.
/// </summary> /// </summary>
public static readonly Color Pink = FromRgba(255, 192, 203, 255); public static readonly Color Pink = FromPixel(new Rgba32(255, 192, 203, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DDA0DD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #DDA0DD.
/// </summary> /// </summary>
public static readonly Color Plum = FromRgba(221, 160, 221, 255); public static readonly Color Plum = FromPixel(new Rgba32(221, 160, 221, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B0E0E6. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B0E0E6.
/// </summary> /// </summary>
public static readonly Color PowderBlue = FromRgba(176, 224, 230, 255); public static readonly Color PowderBlue = FromPixel(new Rgba32(176, 224, 230, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #800080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #800080.
/// </summary> /// </summary>
public static readonly Color Purple = FromRgba(128, 0, 128, 255); public static readonly Color Purple = FromPixel(new Rgba32(128, 0, 128, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #663399. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #663399.
/// </summary> /// </summary>
public static readonly Color RebeccaPurple = FromRgba(102, 51, 153, 255); public static readonly Color RebeccaPurple = FromPixel(new Rgba32(102, 51, 153, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF0000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF0000.
/// </summary> /// </summary>
public static readonly Color Red = FromRgba(255, 0, 0, 255); public static readonly Color Red = FromPixel(new Rgba32(255, 0, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BC8F8F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BC8F8F.
/// </summary> /// </summary>
public static readonly Color RosyBrown = FromRgba(188, 143, 143, 255); public static readonly Color RosyBrown = FromPixel(new Rgba32(188, 143, 143, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4169E1. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4169E1.
/// </summary> /// </summary>
public static readonly Color RoyalBlue = FromRgba(65, 105, 225, 255); public static readonly Color RoyalBlue = FromPixel(new Rgba32(65, 105, 225, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B4513. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8B4513.
/// </summary> /// </summary>
public static readonly Color SaddleBrown = FromRgba(139, 69, 19, 255); public static readonly Color SaddleBrown = FromPixel(new Rgba32(139, 69, 19, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FA8072. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FA8072.
/// </summary> /// </summary>
public static readonly Color Salmon = FromRgba(250, 128, 114, 255); public static readonly Color Salmon = FromPixel(new Rgba32(250, 128, 114, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F4A460. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F4A460.
/// </summary> /// </summary>
public static readonly Color SandyBrown = FromRgba(244, 164, 96, 255); public static readonly Color SandyBrown = FromPixel(new Rgba32(244, 164, 96, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2E8B57. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2E8B57.
/// </summary> /// </summary>
public static readonly Color SeaGreen = FromRgba(46, 139, 87, 255); public static readonly Color SeaGreen = FromPixel(new Rgba32(46, 139, 87, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF5EE. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFF5EE.
/// </summary> /// </summary>
public static readonly Color SeaShell = FromRgba(255, 245, 238, 255); public static readonly Color SeaShell = FromPixel(new Rgba32(255, 245, 238, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A0522D. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A0522D.
/// </summary> /// </summary>
public static readonly Color Sienna = FromRgba(160, 82, 45, 255); public static readonly Color Sienna = FromPixel(new Rgba32(160, 82, 45, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #C0C0C0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #C0C0C0.
/// </summary> /// </summary>
public static readonly Color Silver = FromRgba(192, 192, 192, 255); public static readonly Color Silver = FromPixel(new Rgba32(192, 192, 192, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #87CEEB. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #87CEEB.
/// </summary> /// </summary>
public static readonly Color SkyBlue = FromRgba(135, 206, 235, 255); public static readonly Color SkyBlue = FromPixel(new Rgba32(135, 206, 235, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6A5ACD. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #6A5ACD.
/// </summary> /// </summary>
public static readonly Color SlateBlue = FromRgba(106, 90, 205, 255); public static readonly Color SlateBlue = FromPixel(new Rgba32(106, 90, 205, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #708090. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #708090.
/// </summary> /// </summary>
public static readonly Color SlateGray = FromRgba(112, 128, 144, 255); public static readonly Color SlateGray = FromPixel(new Rgba32(112, 128, 144, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #708090. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #708090.
@ -684,81 +686,80 @@ public readonly partial struct Color
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFAFA. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFAFA.
/// </summary> /// </summary>
public static readonly Color Snow = FromRgba(255, 250, 250, 255); public static readonly Color Snow = FromPixel(new Rgba32(255, 250, 250, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FF7F. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FF7F.
/// </summary> /// </summary>
public static readonly Color SpringGreen = FromRgba(0, 255, 127, 255); public static readonly Color SpringGreen = FromPixel(new Rgba32(0, 255, 127, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4682B4. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #4682B4.
/// </summary> /// </summary>
public static readonly Color SteelBlue = FromRgba(70, 130, 180, 255); public static readonly Color SteelBlue = FromPixel(new Rgba32(70, 130, 180, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D2B48C. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D2B48C.
/// </summary> /// </summary>
public static readonly Color Tan = FromRgba(210, 180, 140, 255); public static readonly Color Tan = FromPixel(new Rgba32(210, 180, 140, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008080. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #008080.
/// </summary> /// </summary>
public static readonly Color Teal = FromRgba(0, 128, 128, 255); public static readonly Color Teal = FromPixel(new Rgba32(0, 128, 128, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D8BFD8. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D8BFD8.
/// </summary> /// </summary>
public static readonly Color Thistle = FromRgba(216, 191, 216, 255); public static readonly Color Thistle = FromPixel(new Rgba32(216, 191, 216, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF6347. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF6347.
/// </summary> /// </summary>
public static readonly Color Tomato = FromRgba(255, 99, 71, 255); public static readonly Color Tomato = FromPixel(new Rgba32(255, 99, 71, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00000000. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00000000.
/// </summary> /// </summary>
public static readonly Color Transparent = FromRgba(0, 0, 0, 0); public static readonly Color Transparent = FromPixel(new Rgba32(0, 0, 0, 0));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #40E0D0. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #40E0D0.
/// </summary> /// </summary>
public static readonly Color Turquoise = FromRgba(64, 224, 208, 255); public static readonly Color Turquoise = FromPixel(new Rgba32(64, 224, 208, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #EE82EE. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #EE82EE.
/// </summary> /// </summary>
public static readonly Color Violet = FromRgba(238, 130, 238, 255); public static readonly Color Violet = FromPixel(new Rgba32(238, 130, 238, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5DEB3. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5DEB3.
/// </summary> /// </summary>
public static readonly Color Wheat = FromRgba(245, 222, 179, 255); public static readonly Color Wheat = FromPixel(new Rgba32(245, 222, 179, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFFF. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFFF.
/// </summary> /// </summary>
public static readonly Color White = FromRgba(255, 255, 255, 255); public static readonly Color White = FromPixel(new Rgba32(255, 255, 255, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5F5F5. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F5F5F5.
/// </summary> /// </summary>
public static readonly Color WhiteSmoke = FromRgba(245, 245, 245, 255); public static readonly Color WhiteSmoke = FromPixel(new Rgba32(245, 245, 245, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFF00. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFF00.
/// </summary> /// </summary>
public static readonly Color Yellow = FromRgba(255, 255, 0, 255); public static readonly Color Yellow = FromPixel(new Rgba32(255, 255, 0, 255));
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9ACD32. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9ACD32.
/// </summary> /// </summary>
public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); public static readonly Color YellowGreen = FromPixel(new Rgba32(154, 205, 50, 255));
private static Dictionary<string, Color> CreateNamedColorsLookup() private static Dictionary<string, Color> CreateNamedColorsLookup()
{ => new(StringComparer.OrdinalIgnoreCase)
return new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase)
{ {
{ nameof(AliceBlue), AliceBlue }, { nameof(AliceBlue), AliceBlue },
{ nameof(AntiqueWhite), AntiqueWhite }, { nameof(AntiqueWhite), AntiqueWhite },
@ -910,5 +911,4 @@ public readonly partial struct Color
{ nameof(Yellow), Yellow }, { nameof(Yellow), Yellow },
{ nameof(YellowGreen), YellowGreen } { nameof(YellowGreen), YellowGreen }
}; };
}
} }

155
src/ImageSharp/Color/Color.cs

@ -18,33 +18,24 @@ namespace SixLabors.ImageSharp;
/// </remarks> /// </remarks>
public readonly partial struct Color : IEquatable<Color> public readonly partial struct Color : IEquatable<Color>
{ {
private readonly Rgba64 data; private readonly Vector4 data;
private readonly IPixel? boxedHighPrecisionPixel; private readonly IPixel? boxedHighPrecisionPixel;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">The <see cref="Vector4"/> containing the color information.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b, byte a) private Color(Vector4 vector)
{
this.data = new Rgba64(
ColorNumerics.UpscaleFrom8BitTo16Bit(r),
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ColorNumerics.UpscaleFrom8BitTo16Bit(a));
this.boxedHighPrecisionPixel = null;
}
[MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b)
{ {
this.data = new Rgba64( this.data = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
ColorNumerics.UpscaleFrom8BitTo16Bit(r),
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ushort.MaxValue);
this.boxedHighPrecisionPixel = null; this.boxedHighPrecisionPixel = null;
} }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="pixel">The pixel containing color information.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private Color(IPixel pixel) private Color(IPixel pixel)
{ {
@ -52,6 +43,21 @@ public readonly partial struct Color : IEquatable<Color>
this.data = default; this.data = default;
} }
/// <summary>
/// Converts a <see cref="Color"/> to <see cref="Vector4"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/>.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static explicit operator Vector4(Color color) => color.ToScaledVector4();
/// <summary>
/// Converts an <see cref="Vector4"/> to <see cref="Color"/>.
/// </summary>
/// <param name="source">The <see cref="Vector4"/>.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static explicit operator Color(Vector4 source) => new(source);
/// <summary> /// <summary>
/// Checks whether two <see cref="Color"/> structures are equal. /// Checks whether two <see cref="Color"/> structures are equal.
/// </summary> /// </summary>
@ -76,27 +82,6 @@ public readonly partial struct Color : IEquatable<Color>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Color left, Color right) => !left.Equals(right); public static bool operator !=(Color left, Color right) => !left.Equals(right);
/// <summary>
/// Creates a <see cref="Color"/> from RGBA bytes.
/// </summary>
/// <param name="r">The red component (0-255).</param>
/// <param name="g">The green component (0-255).</param>
/// <param name="b">The blue component (0-255).</param>
/// <param name="a">The alpha component (0-255).</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Color FromRgba(byte r, byte g, byte b, byte a) => new(r, g, b, a);
/// <summary>
/// Creates a <see cref="Color"/> from RGB bytes.
/// </summary>
/// <param name="r">The red component (0-255).</param>
/// <param name="g">The green component (0-255).</param>
/// <param name="b">The blue component (0-255).</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Color FromRgb(byte r, byte g, byte b) => new(r, g, b);
/// <summary> /// <summary>
/// Creates a <see cref="Color"/> from the given <typeparamref name="TPixel"/>. /// Creates a <see cref="Color"/> from the given <typeparamref name="TPixel"/>.
/// </summary> /// </summary>
@ -107,32 +92,43 @@ public readonly partial struct Color : IEquatable<Color>
public static Color FromPixel<TPixel>(TPixel pixel) public static Color FromPixel<TPixel>(TPixel pixel)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Avoid boxing in case we can convert to Rgba64 safely and efficently // Avoid boxing in case we can convert to Vector4 safely and efficiently
if (typeof(TPixel) == typeof(Rgba64)) PixelTypeInfo info = TPixel.GetPixelTypeInfo();
{ if (info.ComponentInfo.HasValue && info.ComponentInfo.Value.GetMaximumComponentPrecision() <= (int)PixelComponentBitDepth.Bit32)
return new((Rgba64)(object)pixel);
}
else if (typeof(TPixel) == typeof(Rgb48))
{
return new((Rgb48)(object)pixel);
}
else if (typeof(TPixel) == typeof(La32))
{
return new((La32)(object)pixel);
}
else if (typeof(TPixel) == typeof(L16))
{ {
return new((L16)(object)pixel); return new(pixel.ToScaledVector4());
} }
else if (Unsafe.SizeOf<TPixel>() <= Unsafe.SizeOf<Rgba32>())
return new(pixel);
}
/// <summary>
/// Bulk converts a span of a specified <typeparamref name="TPixel"/> type to a span of <see cref="Color"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="source">The source pixel span.</param>
/// <param name="destination">The destination color span.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void FromPixel<TPixel>(ReadOnlySpan<TPixel> source, Span<Color> destination)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// 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)
{ {
Rgba32 p = default; for (int i = 0; i < destination.Length; i++)
pixel.ToRgba32(ref p); {
return new(p); destination[i] = new(source[i].ToScaledVector4());
}
} }
else else
{ {
return new(pixel); for (int i = 0; i < destination.Length; i++)
{
destination[i] = new(source[i]);
}
} }
} }
@ -151,8 +147,7 @@ public readonly partial struct Color : IEquatable<Color>
public static Color ParseHex(string hex) public static Color ParseHex(string hex)
{ {
Rgba32 rgba = Rgba32.ParseHex(hex); Rgba32 rgba = Rgba32.ParseHex(hex);
return FromPixel(rgba);
return new Color(rgba);
} }
/// <summary> /// <summary>
@ -174,7 +169,7 @@ public readonly partial struct Color : IEquatable<Color>
if (Rgba32.TryParseHex(hex, out Rgba32 rgba)) if (Rgba32.TryParseHex(hex, out Rgba32 rgba))
{ {
result = new Color(rgba); result = FromPixel(rgba);
return true; return true;
} }
@ -255,12 +250,10 @@ public readonly partial struct Color : IEquatable<Color>
{ {
if (this.boxedHighPrecisionPixel is not null) if (this.boxedHighPrecisionPixel is not null)
{ {
Rgba32 rgba = default; return this.boxedHighPrecisionPixel.ToRgba32().ToHex();
this.boxedHighPrecisionPixel.ToRgba32(ref rgba);
return rgba.ToHex();
} }
return this.data.ToRgba32().ToHex(); return Rgba32.FromScaledVector4(this.data).ToHex();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -282,14 +275,10 @@ public readonly partial struct Color : IEquatable<Color>
if (this.boxedHighPrecisionPixel is null) if (this.boxedHighPrecisionPixel is null)
{ {
pixel = default; return TPixel.FromScaledVector4(this.data);
pixel.FromRgba64(this.data);
return pixel;
} }
pixel = default; return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
pixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
return pixel;
} }
/// <summary> /// <summary>
@ -302,7 +291,8 @@ public readonly partial struct Color : IEquatable<Color>
public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination) public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// TODO: Investigate bulk operations utilizing configuration parameter here. // We cannot use bulk pixel operations here as there is no guarantee that the source colors are
// created from pixel formats which fit into the unboxed vector data.
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
for (int i = 0; i < source.Length; i++) for (int i = 0; i < source.Length; i++)
{ {
@ -316,7 +306,7 @@ public readonly partial struct Color : IEquatable<Color>
{ {
if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null) if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null)
{ {
return this.data.PackedValue == other.data.PackedValue; return this.data == other.data;
} }
return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true; return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true;
@ -331,9 +321,20 @@ public readonly partial struct Color : IEquatable<Color>
{ {
if (this.boxedHighPrecisionPixel is null) if (this.boxedHighPrecisionPixel is null)
{ {
return this.data.PackedValue.GetHashCode(); return this.data.GetHashCode();
} }
return this.boxedHighPrecisionPixel.GetHashCode(); return this.boxedHighPrecisionPixel.GetHashCode();
} }
[MethodImpl(InliningOptions.ShortMethod)]
private Vector4 ToScaledVector4()
{
if (this.boxedHighPrecisionPixel is null)
{
return this.data;
}
return this.boxedHighPrecisionPixel.ToScaledVector4();
}
} }

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

@ -41,6 +41,34 @@ internal static class ColorNumerics
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) public static byte Get8BitBT709Luminance(byte r, byte g, byte b)
=> (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); => (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
/// <summary>
/// Gets the luminance from the rgb components using the formula
/// as specified by ITU-R Recommendation BT.709.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <returns>The <see cref="byte"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte Get8BitBT709Luminance(ushort r, ushort g, ushort b)
=> (byte)((From16BitTo8Bit(r) * .2126F) +
(From16BitTo8Bit(g) * .7152F) +
(From16BitTo8Bit(b) * .0722F) + 0.5F);
/// <summary>
/// Gets the luminance from the rgb components using the formula as
/// specified by ITU-R Recommendation BT.709.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <returns>The <see cref="ushort"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort Get16BitBT709Luminance(byte r, byte g, byte b)
=> (ushort)((From8BitTo16Bit(r) * .2126F) +
(From8BitTo16Bit(g) * .7152F) +
(From8BitTo16Bit(b) * .0722F) + 0.5F);
/// <summary> /// <summary>
/// Gets the luminance from the rgb components using the formula as /// Gets the luminance from the rgb components using the formula as
/// specified by ITU-R Recommendation BT.709. /// specified by ITU-R Recommendation BT.709.
@ -72,8 +100,8 @@ internal static class ColorNumerics
/// <param name="component">The 8 bit component value.</param> /// <param name="component">The 8 bit component value.</param>
/// <returns>The <see cref="byte"/></returns> /// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte DownScaleFrom16BitTo8Bit(ushort component) public static byte From16BitTo8Bit(ushort component) =>
{
// To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is: // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
// //
// (V * 255) / 65535 // (V * 255) / 65535
@ -102,8 +130,7 @@ internal static class ColorNumerics
// An alternative arithmetic calculation which also gives no errors is: // An alternative arithmetic calculation which also gives no errors is:
// //
// (V * 255 + 32895) >> 16 // (V * 255 + 32895) >> 16
return (byte)(((component * 255) + 32895) >> 16); (byte)(((component * 255) + 32895) >> 16);
}
/// <summary> /// <summary>
/// Scales a value from an 8 bit <see cref="byte"/> to /// Scales a value from an 8 bit <see cref="byte"/> to
@ -112,7 +139,7 @@ internal static class ColorNumerics
/// <param name="component">The 8 bit component value.</param> /// <param name="component">The 8 bit component value.</param>
/// <returns>The <see cref="ushort"/></returns> /// <returns>The <see cref="ushort"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) public static ushort From8BitTo16Bit(byte component)
=> (ushort)(component * 257); => (ushort)(component * 257);
/// <summary> /// <summary>

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

@ -5,7 +5,6 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;
@ -61,6 +60,12 @@ internal static class Numerics
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nint Modulo4(nint x) => x & 3; public static nint Modulo4(nint x) => x & 3;
/// <summary>
/// Calculates <paramref name="x"/> % 4
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint Modulo4(nuint x) => x & 3;
/// <summary> /// <summary>
/// Calculates <paramref name="x"/> % 8 /// Calculates <paramref name="x"/> % 8
/// </summary> /// </summary>

194
src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs

@ -1,12 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
// The JIT can detect and optimize rotation idioms ROTL (Rotate Left) // The JIT can detect and optimize rotation idioms ROTL (Rotate Left)
// and ROTR (Rotate Right) emitting efficient CPU instructions: // and ROTR (Rotate Right) emitting efficient CPU instructions:
// https://github.com/dotnet/coreclr/pull/1830 // https://github.com/dotnet/coreclr/pull/1830
@ -19,190 +13,24 @@ namespace SixLabors.ImageSharp;
internal interface IComponentShuffle internal interface IComponentShuffle
{ {
/// <summary> /// <summary>
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffles then slices 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using a byte control and store the results in <paramref name="destination"/>.
/// If successful, this method will reduce the length of <paramref name="source"/> length
/// by the shuffle amount.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest); void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination);
/// <summary> /// <summary>
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffle 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <remarks> /// <remarks>
/// Implementation can assume that source.Length is less or equal than dest.Length. /// Implementation can assume that source.Length is less or equal than destination.Length.
/// Loops should iterate using source.Length. /// Loops should iterate using source.Length.
/// </remarks> /// </remarks>
void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest); void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination);
}
/// <inheritdoc/>
internal interface IShuffle4 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle4 : IShuffle4
{
public DefaultShuffle4(byte control)
=> this.Control = control;
public byte Control { get; }
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, this.Control);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest);
Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 4)
{
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
}
}
}
internal readonly struct WXYZShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle2103);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTL(8, packed) = [Z Y X W]
Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
}
}
}
internal readonly struct WZYXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0123);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// REVERSE(packedArgb) = [X Y Z W]
Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
}
}
}
internal readonly struct YZWXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0321);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTR(8, packedArgb) = [Y Z W X]
Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8);
}
}
}
internal readonly struct ZYXWShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3012);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [W 0 Y 0]
// tmp2 = [0 Z 0 X]
// tmp3=ROTL(16, tmp2) = [0 X 0 Z]
// tmp1 + tmp3 = [W X Y Z]
uint tmp1 = packed & 0xFF00FF00;
uint tmp2 = packed & 0x00FF00FF;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}
internal readonly struct XWZYShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle1230);
[MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [0 Z 0 X]
// tmp2 = [W 0 Y 0]
// tmp3=ROTL(16, tmp2) = [Y 0 W 0]
// tmp1 + tmp3 = [Y Z W X]
uint tmp1 = packed & 0x00FF00FF;
uint tmp2 = packed & 0xFF00FF00;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
} }

28
src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils; using static SixLabors.ImageSharp.SimdUtils;
@ -12,24 +13,23 @@ internal interface IPad3Shuffle4 : IComponentShuffle
{ {
} }
internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 internal readonly struct DefaultPad3Shuffle4([ConstantExpected] byte control) : IPad3Shuffle4
{ {
public DefaultPad3Shuffle4(byte control) public byte Control { get; } = control;
=> this.Control = control;
public byte Control { get; }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest) public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control); #pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest) public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest); ref byte dBase = ref MemoryMarshal.GetReference(destination);
Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
Span<byte> temp = stackalloc byte[4]; Span<byte> temp = stackalloc byte[4];
ref byte t = ref MemoryMarshal.GetReference(temp); ref byte t = ref MemoryMarshal.GetReference(temp);
@ -51,14 +51,14 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4
internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4
{ {
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest) public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3210); => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest) public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest); ref byte dBase = ref MemoryMarshal.GetReference(destination);
ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length); ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length);
ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4); ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4);

20
src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils; using static SixLabors.ImageSharp.SimdUtils;
@ -12,24 +13,23 @@ internal interface IShuffle3 : IComponentShuffle
{ {
} }
internal readonly struct DefaultShuffle3 : IShuffle3 internal readonly struct DefaultShuffle3([ConstantExpected] byte control) : IShuffle3
{ {
public DefaultShuffle3(byte control) public byte Control { get; } = control;
=> this.Control = control;
public byte Control { get; }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest) public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control); #pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle3Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest) public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest); ref byte dBase = ref MemoryMarshal.GetReference(destination);
Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 3) for (nuint i = 0; i < (uint)source.Length; i += 3)
{ {

178
src/ImageSharp/Common/Helpers/Shuffle/IShuffle4.cs

@ -0,0 +1,178 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils;
namespace SixLabors.ImageSharp;
/// <inheritdoc/>
internal interface IShuffle4 : IComponentShuffle
{
}
internal readonly struct DefaultShuffle4([ConstantExpected] byte control) : IShuffle4
{
public byte Control { get; } = control;
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
#pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(destination);
SimdUtils.Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 4)
{
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
}
}
}
internal readonly struct WXYZShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle2103);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTL(8, packed) = [Z Y X W]
Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
}
}
}
internal readonly struct WZYXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0123);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// REVERSE(packedArgb) = [X Y Z W]
Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
}
}
}
internal readonly struct YZWXShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle0321);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// ROTR(8, packedArgb) = [Y Z W X]
Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8);
}
}
}
internal readonly struct ZYXWShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3012);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [W 0 Y 0]
// tmp2 = [0 Z 0 X]
// tmp3=ROTL(16, tmp2) = [0 X 0 Z]
// tmp1 + tmp3 = [W X Y Z]
uint tmp1 = packed & 0xFF00FF00;
uint tmp2 = packed & 0x00FF00FF;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}
internal readonly struct XWZYShuffle4 : IShuffle4
{
[MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle1230);
[MethodImpl(InliningOptions.ShortMethod)]
public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(destination));
uint n = (uint)source.Length / 4;
for (nuint i = 0; i < n; i++)
{
uint packed = Unsafe.Add(ref sBase, i);
// packed = [W Z Y X]
// tmp1 = [0 Z 0 X]
// tmp2 = [W 0 Y 0]
// tmp3=ROTL(16, tmp2) = [Y 0 W 0]
// tmp1 + tmp3 = [Y Z W X]
uint tmp1 = packed & 0x00FF00FF;
uint tmp2 = packed & 0xFF00FF00;
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
}
}
}

30
src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.SimdUtils; using static SixLabors.ImageSharp.SimdUtils;
@ -12,26 +13,25 @@ internal interface IShuffle4Slice3 : IComponentShuffle
{ {
} }
internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 internal readonly struct DefaultShuffle4Slice3([ConstantExpected] byte control) : IShuffle4Slice3
{ {
public DefaultShuffle4Slice3(byte control) public byte Control { get; } = control;
=> this.Control = control;
public byte Control { get; }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest) public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control); #pragma warning disable CA1857 // A constant is expected for the parameter
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, this.Control);
#pragma warning restore CA1857 // A constant is expected for the parameter
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest) public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte sBase = ref MemoryMarshal.GetReference(source);
ref byte dBase = ref MemoryMarshal.GetReference(dest); ref byte dBase = ref MemoryMarshal.GetReference(destination);
Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); SimdUtils.Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0);
for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4) for (nuint i = 0, j = 0; i < (uint)destination.Length; i += 3, j += 4)
{ {
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j); Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j);
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j);
@ -43,14 +43,14 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3
internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3
{ {
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest) public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination)
=> HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, Shuffle.MMShuffle3210); => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref destination, SimdUtils.Shuffle.MMShuffle3210);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest) public void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
ref Byte3 dBase = ref Unsafe.As<byte, Byte3>(ref MemoryMarshal.GetReference(dest)); ref Byte3 dBase = ref Unsafe.As<byte, Byte3>(ref MemoryMarshal.GetReference(destination));
nint n = (nint)(uint)source.Length / 4; nint n = (nint)(uint)source.Length / 4;
nint m = Numerics.Modulo4(n); nint m = Numerics.Modulo4(n);

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

@ -8,6 +8,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;
@ -51,22 +52,34 @@ internal static partial class SimdUtils
/// <summary> /// <summary>
/// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/> /// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of floats.</param> /// <param name="source">The source span of floats.</param>
/// <param name="dest">The destination span of floats.</param> /// <param name="destination">The destination span of floats.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4Reduce( public static void Shuffle4Reduce(
ref ReadOnlySpan<float> source, ref ReadOnlySpan<float> source,
ref Span<float> dest, ref Span<float> destination,
[ConstantExpected] byte control) [ConstantExpected] byte control)
{ {
if (Avx.IsSupported || Sse.IsSupported) if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat) ||
(Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat) ||
(Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat))
{ {
int remainder = Avx.IsSupported int remainder = 0;
? Numerics.ModuloP2(source.Length, Vector256<float>.Count) if (Vector512.IsHardwareAccelerated)
: Numerics.ModuloP2(source.Length, Vector128<float>.Count); {
remainder = Numerics.ModuloP2(source.Length, Vector512<float>.Count);
}
else if (Vector256.IsHardwareAccelerated)
{
remainder = Numerics.ModuloP2(source.Length, Vector256<float>.Count);
}
else if (Vector128.IsHardwareAccelerated)
{
remainder = Numerics.ModuloP2(source.Length, Vector128<float>.Count);
}
int adjustedCount = source.Length - remainder; int adjustedCount = source.Length - remainder;
@ -74,33 +87,45 @@ internal static partial class SimdUtils
{ {
Shuffle4( Shuffle4(
source[..adjustedCount], source[..adjustedCount],
dest[..adjustedCount], destination[..adjustedCount],
control); control);
source = source[adjustedCount..]; source = source[adjustedCount..];
dest = dest[adjustedCount..]; destination = destination[adjustedCount..];
} }
} }
} }
/// <summary> /// <summary>
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffle 8-bit integers <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4Reduce( public static void Shuffle4Reduce(
ref ReadOnlySpan<byte> source, ref ReadOnlySpan<byte> source,
ref Span<byte> dest, ref Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Avx2.IsSupported || Ssse3.IsSupported) if ((Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte) ||
(Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte) ||
(Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte))
{ {
int remainder = Avx2.IsSupported int remainder = 0;
? Numerics.ModuloP2(source.Length, Vector256<byte>.Count) if (Vector512.IsHardwareAccelerated)
: Numerics.ModuloP2(source.Length, Vector128<byte>.Count); {
remainder = Numerics.ModuloP2(source.Length, Vector512<byte>.Count);
}
else if (Vector256.IsHardwareAccelerated)
{
remainder = Numerics.ModuloP2(source.Length, Vector256<byte>.Count);
}
else if (Vector128.IsHardwareAccelerated)
{
remainder = Numerics.ModuloP2(source.Length, Vector128<byte>.Count);
}
int adjustedCount = source.Length - remainder; int adjustedCount = source.Length - remainder;
@ -108,29 +133,29 @@ internal static partial class SimdUtils
{ {
Shuffle4( Shuffle4(
source[..adjustedCount], source[..adjustedCount],
dest[..adjustedCount], destination[..adjustedCount],
control); control);
source = source[adjustedCount..]; source = source[adjustedCount..];
dest = dest[adjustedCount..]; destination = destination[adjustedCount..];
} }
} }
} }
/// <summary> /// <summary>
/// Shuffles 8-bit integer triplets within 128-bit lanes in <paramref name="source"/> /// Shuffles 8-bit integer triplets in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle3Reduce( public static void Shuffle3Reduce(
ref ReadOnlySpan<byte> source, ref ReadOnlySpan<byte> source,
ref Span<byte> dest, ref Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign)
{ {
int remainder = source.Length % (Vector128<byte>.Count * 3); int remainder = source.Length % (Vector128<byte>.Count * 3);
@ -140,77 +165,77 @@ internal static partial class SimdUtils
{ {
Shuffle3( Shuffle3(
source[..adjustedCount], source[..adjustedCount],
dest[..adjustedCount], destination[..adjustedCount],
control); control);
source = source[adjustedCount..]; source = source[adjustedCount..];
dest = dest[adjustedCount..]; destination = destination[adjustedCount..];
} }
} }
} }
/// <summary> /// <summary>
/// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Pads then shuffles 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Pad3Shuffle4Reduce( public static void Pad3Shuffle4Reduce(
ref ReadOnlySpan<byte> source, ref ReadOnlySpan<byte> source,
ref Span<byte> dest, ref Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{ {
int remainder = source.Length % (Vector128<byte>.Count * 3); int remainder = source.Length % (Vector128<byte>.Count * 3);
int sourceCount = source.Length - remainder; int sourceCount = source.Length - remainder;
int destCount = (int)((uint)sourceCount * 4 / 3); int destinationCount = (int)((uint)sourceCount * 4 / 3);
if (sourceCount > 0) if (sourceCount > 0)
{ {
Pad3Shuffle4( Pad3Shuffle4(
source[..sourceCount], source[..sourceCount],
dest[..destCount], destination[..destinationCount],
control); control);
source = source[sourceCount..]; source = source[sourceCount..];
dest = dest[destCount..]; destination = destination[destinationCount..];
} }
} }
} }
/// <summary> /// <summary>
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffles then slices 8-bit integers in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4Slice3Reduce( public static void Shuffle4Slice3Reduce(
ref ReadOnlySpan<byte> source, ref ReadOnlySpan<byte> source,
ref Span<byte> dest, ref Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{ {
int remainder = source.Length & ((Vector128<byte>.Count * 4) - 1); // bit-hack for modulo int remainder = source.Length & ((Vector128<byte>.Count * 4) - 1); // bit-hack for modulo
int sourceCount = source.Length - remainder; int sourceCount = source.Length - remainder;
int destCount = (int)((uint)sourceCount * 3 / 4); int destinationCount = (int)((uint)sourceCount * 3 / 4);
if (sourceCount > 0) if (sourceCount > 0)
{ {
Shuffle4Slice3( Shuffle4Slice3(
source[..sourceCount], source[..sourceCount],
dest[..destCount], destination[..destinationCount],
control); control);
source = source[sourceCount..]; source = source[sourceCount..];
dest = dest[destCount..]; destination = destination[destinationCount..];
} }
} }
} }
@ -218,76 +243,90 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Shuffle4( private static void Shuffle4(
ReadOnlySpan<float> source, ReadOnlySpan<float> source,
Span<float> dest, Span<float> destination,
[ConstantExpected] byte control) [ConstantExpected] byte control)
{ {
if (Avx.IsSupported) if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleFloat)
{ {
ref Vector256<float> sourceBase = ref Vector512<float> sourceBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(source));
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); ref Vector512<float> destinationBase = ref Unsafe.As<float, Vector512<float>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<float> destBase = nuint n = (uint)destination.Length / (uint)Vector512<float>.Count;
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(dest)); nuint m = Numerics.Modulo4(n);
nuint u = n - m;
nint n = (nint)dest.Vector256Count<float>(); for (nuint i = 0; i < u; i += 4)
nint m = Numerics.Modulo4(n);
nint u = n - m;
for (nint i = 0; i < u; i += 4)
{ {
ref Vector256<float> vd0 = ref Unsafe.Add(ref destBase, i); ref Vector512<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector256<float> vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector512<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Avx.Permute(vs0, control); vd0 = Vector512Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, 1) = Avx.Permute(Unsafe.Add(ref vs0, 1), control); Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, 2) = Avx.Permute(Unsafe.Add(ref vs0, 2), control); Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, 3) = Avx.Permute(Unsafe.Add(ref vs0, 3), control); Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
} }
if (m > 0) if (m > 0)
{ {
for (nint i = u; i < n; i++) for (nuint i = u; i < n; i++)
{ {
Unsafe.Add(ref destBase, i) = Avx.Permute(Unsafe.Add(ref sourceBase, i), control); Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
} }
} }
} }
else else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleFloat)
{ {
// Sse ref Vector256<float> sourceBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source));
ref Vector128<float> sourceBase = ref Vector256<float> destinationBase = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(destination));
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source));
ref Vector128<float> destBase = nuint n = (uint)destination.Length / (uint)Vector256<float>.Count;
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(dest)); nuint m = Numerics.Modulo4(n);
nuint u = n - m;
nint n = (nint)((uint)dest.Length / (uint)Vector128<float>.Count); for (nuint i = 0; i < u; i += 4)
nint m = Numerics.Modulo4(n);
nint u = n - m;
for (nint i = 0; i < u; i += 4)
{ {
ref Vector128<float> vd0 = ref Unsafe.Add(ref destBase, i); ref Vector256<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector128<float> vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector256Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
}
vd0 = Sse.Shuffle(vs0, vs0, control); if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
}
}
}
else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleFloat)
{
ref Vector128<float> sourceBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source));
ref Vector128<float> destinationBase = ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(destination));
Vector128<float> vs1 = Unsafe.Add(ref vs0, 1); nuint n = (uint)destination.Length / (uint)Vector128<float>.Count;
Unsafe.Add(ref vd0, 1) = Sse.Shuffle(vs1, vs1, control); nuint m = Numerics.Modulo4(n);
nuint u = n - m;
Vector128<float> vs2 = Unsafe.Add(ref vs0, 2); for (nuint i = 0; i < u; i += 4)
Unsafe.Add(ref vd0, 2) = Sse.Shuffle(vs2, vs2, control); {
ref Vector128<float> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector128<float> vd0 = ref Unsafe.Add(ref destinationBase, i);
Vector128<float> vs3 = Unsafe.Add(ref vs0, 3); vd0 = Vector128Utilities.Shuffle(vs0, control);
Unsafe.Add(ref vd0, 3) = Sse.Shuffle(vs3, vs3, control); Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), control);
Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), control);
Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), control);
} }
if (m > 0) if (m > 0)
{ {
for (nint i = u; i < n; i++) for (nuint i = u; i < n; i++)
{ {
Vector128<float> vs = Unsafe.Add(ref sourceBase, i); Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), control);
Unsafe.Add(ref destBase, i) = Sse.Shuffle(vs, vs, control);
} }
} }
} }
@ -296,80 +335,102 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Shuffle4( private static void Shuffle4(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Avx2.IsSupported) if (Vector512.IsHardwareAccelerated && Vector512Utilities.SupportsShuffleByte)
{ {
// I've chosen to do this for convenience while we determine what Span<byte> temp = stackalloc byte[Vector512<byte>.Count];
// shuffle controls to add to the library. Shuffle.MMShuffleSpan(ref temp, control);
// We can add static ROS instances if need be in the future. Vector512<byte> mask = Unsafe.As<byte, Vector512<byte>>(ref MemoryMarshal.GetReference(temp));
Span<byte> bytes = stackalloc byte[Vector256<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control);
Vector256<byte> vshuffle = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(bytes));
ref Vector256<byte> sourceBase = ref Vector512<byte> sourceBase = ref Unsafe.As<byte, Vector512<byte>>(ref MemoryMarshal.GetReference(source));
ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(source)); ref Vector512<byte> destinationBase = ref Unsafe.As<byte, Vector512<byte>>(ref MemoryMarshal.GetReference(destination));
ref Vector256<byte> destBase = nuint n = (uint)destination.Length / (uint)Vector512<byte>.Count;
ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest)); nuint m = Numerics.Modulo4(n);
nuint u = n - m;
for (nuint i = 0; i < u; i += 4)
{
ref Vector512<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector512<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Vector512Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, (nuint)1) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, (nuint)2) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, (nuint)3) = Vector512Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
}
if (m > 0)
{
for (nuint i = u; i < n; i++)
{
Unsafe.Add(ref destinationBase, i) = Vector512Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
}
}
}
else if (Vector256.IsHardwareAccelerated && Vector256Utilities.SupportsShuffleByte)
{
Span<byte> temp = stackalloc byte[Vector256<byte>.Count];
Shuffle.MMShuffleSpan(ref temp, control);
Vector256<byte> mask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(temp));
ref Vector256<byte> sourceBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector256<byte> destinationBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(destination));
nint n = (nint)((uint)dest.Length / (uint)Vector256<byte>.Count); nuint n = (uint)destination.Length / (uint)Vector256<byte>.Count;
nint m = Numerics.Modulo4(n); nuint m = Numerics.Modulo4(n);
nint u = n - m; nuint u = n - m;
for (nint i = 0; i < u; i += 4) for (nuint i = 0; i < u; i += 4)
{ {
ref Vector256<byte> vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector256<byte> vd0 = ref Unsafe.Add(ref destBase, i); ref Vector256<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Avx2.Shuffle(vs0, vshuffle); vd0 = Vector256Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, 1) = Avx2.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); Unsafe.Add(ref vd0, (nuint)1) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, 2) = Avx2.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); Unsafe.Add(ref vd0, (nuint)2) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, 3) = Avx2.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); Unsafe.Add(ref vd0, (nuint)3) = Vector256Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
} }
if (m > 0) if (m > 0)
{ {
for (nint i = u; i < n; i++) for (nuint i = u; i < n; i++)
{ {
Unsafe.Add(ref destBase, i) = Avx2.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); Unsafe.Add(ref destinationBase, i) = Vector256Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
} }
} }
} }
else else if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte)
{ {
// Ssse3 Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; Shuffle.MMShuffleSpan(ref temp, control);
Shuffle.MMShuffleSpan(ref bytes, control); Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(temp));
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes));
ref Vector128<byte> sourceBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destBase = ref Vector128<byte> sourceBase = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); ref Vector128<byte> destinationBase = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
nint n = (nint)((uint)dest.Length / (uint)Vector128<byte>.Count); nuint n = (uint)destination.Length / (uint)Vector128<byte>.Count;
nint m = Numerics.Modulo4(n); nuint m = Numerics.Modulo4(n);
nint u = n - m; nuint u = n - m;
for (nint i = 0; i < u; i += 4) for (nuint i = 0; i < u; i += 4)
{ {
ref Vector128<byte> vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128<byte> vs0 = ref Unsafe.Add(ref sourceBase, i);
ref Vector128<byte> vd0 = ref Unsafe.Add(ref destBase, i); ref Vector128<byte> vd0 = ref Unsafe.Add(ref destinationBase, i);
vd0 = Ssse3.Shuffle(vs0, vshuffle); vd0 = Vector128Utilities.Shuffle(vs0, mask);
Unsafe.Add(ref vd0, 1) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); Unsafe.Add(ref vd0, (nuint)1) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)1), mask);
Unsafe.Add(ref vd0, 2) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); Unsafe.Add(ref vd0, (nuint)2) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)2), mask);
Unsafe.Add(ref vd0, 3) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); Unsafe.Add(ref vd0, (nuint)3) = Vector128Utilities.Shuffle(Unsafe.Add(ref vs0, (nuint)3), mask);
} }
if (m > 0) if (m > 0)
{ {
for (nint i = u; i < n; i++) for (nuint i = u; i < n; i++)
{ {
Unsafe.Add(ref destBase, i) = Ssse3.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); Unsafe.Add(ref destinationBase, i) = Vector128Utilities.Shuffle(Unsafe.Add(ref sourceBase, i), mask);
} }
} }
} }
@ -378,24 +439,21 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Shuffle3( private static void Shuffle3(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsRightAlign)
{ {
Vector128<byte> vmask = ShuffleMaskPad4Nx16(); Vector128<byte> maskPad4Nx16 = ShuffleMaskPad4Nx16();
Vector128<byte> vmasko = ShuffleMaskSlice4Nx16(); Vector128<byte> maskSlice4Nx16 = ShuffleMaskSlice4Nx16();
Vector128<byte> vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); Vector128<byte> maskE = Vector128Utilities.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12);
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; Span<byte> bytes = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control); Shuffle.MMShuffleSpan(ref bytes, control);
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes));
ref Vector128<byte> sourceBase = ref Vector128<byte> sourceBase = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); ref Vector128<byte> destinationBase = ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
ref Vector128<byte> destBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest));
nuint n = source.Vector128Count<byte>(); nuint n = source.Vector128Count<byte>();
@ -404,36 +462,36 @@ internal static partial class SimdUtils
ref Vector128<byte> vs = ref Unsafe.Add(ref sourceBase, i); ref Vector128<byte> vs = ref Unsafe.Add(ref sourceBase, i);
Vector128<byte> v0 = vs; Vector128<byte> v0 = vs;
Vector128<byte> v1 = Unsafe.Add(ref vs, 1); Vector128<byte> v1 = Unsafe.Add(ref vs, (nuint)1);
Vector128<byte> v2 = Unsafe.Add(ref vs, 2); Vector128<byte> v2 = Unsafe.Add(ref vs, (nuint)2);
Vector128<byte> v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); Vector128<byte> v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v2 = Ssse3.AlignRight(v2, v1, 8); v2 = Vector128Utilities.AlignRight(v2, v1, 8);
v1 = Ssse3.AlignRight(v1, v0, 12); v1 = Vector128Utilities.AlignRight(v1, v0, 12);
v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vmask), vshuffle); v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16), mask);
v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vmask), vshuffle); v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16), mask);
v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vmask), vshuffle); v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16), mask);
v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vmask), vshuffle); v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16), mask);
v0 = Ssse3.Shuffle(v0, vmaske); v0 = Vector128Utilities.Shuffle(v0, maskE);
v1 = Ssse3.Shuffle(v1, vmasko); v1 = Vector128Utilities.Shuffle(v1, maskSlice4Nx16);
v2 = Ssse3.Shuffle(v2, vmaske); v2 = Vector128Utilities.Shuffle(v2, maskE);
v3 = Ssse3.Shuffle(v3, vmasko); v3 = Vector128Utilities.Shuffle(v3, maskSlice4Nx16);
v0 = Ssse3.AlignRight(v1, v0, 4); v0 = Vector128Utilities.AlignRight(v1, v0, 4);
v3 = Ssse3.AlignRight(v3, v2, 12); v3 = Vector128Utilities.AlignRight(v3, v2, 12);
v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4);
v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v1 = Ssse3.AlignRight(v2, v1, 8); v1 = Vector128Utilities.AlignRight(v2, v1, 8);
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, i); ref Vector128<byte> vd = ref Unsafe.Add(ref destinationBase, i);
vd = v0; vd = v0;
Unsafe.Add(ref vd, 1) = v1; Unsafe.Add(ref vd, (nuint)1) = v1;
Unsafe.Add(ref vd, 2) = v3; Unsafe.Add(ref vd, (nuint)2) = v3;
} }
} }
} }
@ -441,23 +499,23 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Pad3Shuffle4( private static void Pad3Shuffle4(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{ {
Vector128<byte> vmask = ShuffleMaskPad4Nx16(); Vector128<byte> maskPad4Nx16 = ShuffleMaskPad4Nx16();
Vector128<byte> vfill = Vector128.Create(0xff000000ff000000ul).AsByte(); Vector128<byte> fill = Vector128.Create(0xff000000ff000000ul).AsByte();
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control); Shuffle.MMShuffleSpan(ref temp, control);
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(temp));
ref Vector128<byte> sourceBase = ref Vector128<byte> sourceBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destBase = ref Vector128<byte> destinationBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
nuint n = source.Vector128Count<byte>(); nuint n = source.Vector128Count<byte>();
@ -466,17 +524,17 @@ internal static partial class SimdUtils
ref Vector128<byte> v0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128<byte> v0 = ref Unsafe.Add(ref sourceBase, i);
Vector128<byte> v1 = Unsafe.Add(ref v0, 1); Vector128<byte> v1 = Unsafe.Add(ref v0, 1);
Vector128<byte> v2 = Unsafe.Add(ref v0, 2); Vector128<byte> v2 = Unsafe.Add(ref v0, 2);
Vector128<byte> v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); Vector128<byte> v3 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v2 = Ssse3.AlignRight(v2, v1, 8); v2 = Vector128Utilities.AlignRight(v2, v1, 8);
v1 = Ssse3.AlignRight(v1, v0, 12); v1 = Vector128Utilities.AlignRight(v1, v0, 12);
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j); ref Vector128<byte> vd = ref Unsafe.Add(ref destinationBase, j);
vd = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v0, vmask), vfill), vshuffle); vd = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v1, vmask), vfill), vshuffle); Unsafe.Add(ref vd, 1) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v2, vmask), vfill), vshuffle); Unsafe.Add(ref vd, 2) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, maskPad4Nx16) | fill, mask);
Unsafe.Add(ref vd, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v3, vmask), vfill), vshuffle); Unsafe.Add(ref vd, 3) = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, maskPad4Nx16) | fill, mask);
} }
} }
} }
@ -484,23 +542,23 @@ internal static partial class SimdUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Shuffle4Slice3( private static void Shuffle4Slice3(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
byte control) [ConstantExpected] byte control)
{ {
if (Ssse3.IsSupported) if (Vector128.IsHardwareAccelerated && Vector128Utilities.SupportsShuffleByte && Vector128Utilities.SupportsShiftByte)
{ {
Vector128<byte> vmasko = ShuffleMaskSlice4Nx16(); Vector128<byte> maskSlice4Nx16 = ShuffleMaskSlice4Nx16();
Vector128<byte> vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); Vector128<byte> maskE = Vector128Utilities.AlignRight(maskSlice4Nx16, maskSlice4Nx16, 12);
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; Span<byte> temp = stackalloc byte[Vector128<byte>.Count];
Shuffle.MMShuffleSpan(ref bytes, control); Shuffle.MMShuffleSpan(ref temp, control);
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); Vector128<byte> mask = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(temp));
ref Vector128<byte> sourceBase = ref Vector128<byte> sourceBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source));
ref Vector128<byte> destBase = ref Vector128<byte> destinationBase =
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(destination));
nuint n = source.Vector128Count<byte>(); nuint n = source.Vector128Count<byte>();
@ -513,20 +571,20 @@ internal static partial class SimdUtils
Vector128<byte> v2 = Unsafe.Add(ref vs, 2); Vector128<byte> v2 = Unsafe.Add(ref vs, 2);
Vector128<byte> v3 = Unsafe.Add(ref vs, 3); Vector128<byte> v3 = Unsafe.Add(ref vs, 3);
v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vshuffle), vmaske); v0 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v0, mask), maskE);
v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vshuffle), vmasko); v1 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v1, mask), maskSlice4Nx16);
v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vshuffle), vmaske); v2 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v2, mask), maskE);
v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vshuffle), vmasko); v3 = Vector128Utilities.Shuffle(Vector128Utilities.Shuffle(v3, mask), maskSlice4Nx16);
v0 = Ssse3.AlignRight(v1, v0, 4); v0 = Vector128Utilities.AlignRight(v1, v0, 4);
v3 = Ssse3.AlignRight(v3, v2, 12); v3 = Vector128Utilities.AlignRight(v3, v2, 12);
v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); v1 = Vector128Utilities.ShiftLeftBytesInVector(v1, 4);
v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); v2 = Vector128Utilities.ShiftRightBytesInVector(v2, 4);
v1 = Ssse3.AlignRight(v2, v1, 8); v1 = Vector128Utilities.AlignRight(v2, v1, 8);
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j); ref Vector128<byte> vd = ref Unsafe.Add(ref destinationBase, j);
vd = v0; vd = v0;
Unsafe.Add(ref vd, 1) = v1; Unsafe.Add(ref vd, 1) = v1;

125
src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs

@ -12,140 +12,140 @@ internal static partial class SimdUtils
{ {
/// <summary> /// <summary>
/// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/> /// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <param name="source">The source span of floats.</param> /// <param name="source">The source span of floats.</param>
/// <param name="dest">The destination span of floats.</param> /// <param name="destination">The destination span of floats.</param>
/// <param name="control">The byte control.</param> /// <param name="control">The byte control.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4( public static void Shuffle4(
ReadOnlySpan<float> source, ReadOnlySpan<float> source,
Span<float> dest, Span<float> destination,
[ConstantExpected] byte control) [ConstantExpected] byte control)
{ {
VerifyShuffle4SpanInput(source, dest); VerifyShuffle4SpanInput(source, destination);
HwIntrinsics.Shuffle4Reduce(ref source, ref dest, control); HwIntrinsics.Shuffle4Reduce(ref source, ref destination, control);
// Deal with the remainder: // Deal with the remainder:
if (source.Length > 0) if (source.Length > 0)
{ {
Shuffle4Remainder(source, dest, control); Shuffle4Remainder(source, destination, control);
} }
} }
/// <summary> /// <summary>
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <typeparam name="TShuffle">The type of shuffle struct.</typeparam> /// <typeparam name="TShuffle">The type of shuffle struct.</typeparam>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="shuffle">The type of shuffle to perform.</param> /// <param name="shuffle">The type of shuffle to perform.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4<TShuffle>( public static void Shuffle4<TShuffle>(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
TShuffle shuffle) TShuffle shuffle)
where TShuffle : struct, IShuffle4 where TShuffle : struct, IShuffle4
{ {
VerifyShuffle4SpanInput(source, dest); VerifyShuffle4SpanInput(source, destination);
shuffle.ShuffleReduce(ref source, ref dest); shuffle.ShuffleReduce(ref source, ref destination);
// Deal with the remainder: // Deal with the remainder:
if (source.Length > 0) if (source.Length > 0)
{ {
shuffle.RunFallbackShuffle(source, dest); shuffle.Shuffle(source, destination);
} }
} }
/// <summary> /// <summary>
/// Shuffle 8-bit integer triplets within 128-bit lanes in <paramref name="source"/> /// Shuffle 8-bit integer triplets within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <typeparam name="TShuffle">The type of shuffle struct.</typeparam> /// <typeparam name="TShuffle">The type of shuffle struct.</typeparam>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="shuffle">The type of shuffle to perform.</param> /// <param name="shuffle">The type of shuffle to perform.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle3<TShuffle>( public static void Shuffle3<TShuffle>(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
TShuffle shuffle) TShuffle shuffle)
where TShuffle : struct, IShuffle3 where TShuffle : struct, IShuffle3
{ {
// Source length should be smaller than dest length, and divisible by 3. // Source length should be smaller than destination length, and divisible by 3.
VerifyShuffle3SpanInput(source, dest); VerifyShuffle3SpanInput(source, destination);
shuffle.ShuffleReduce(ref source, ref dest); shuffle.ShuffleReduce(ref source, ref destination);
// Deal with the remainder: // Deal with the remainder:
if (source.Length > 0) if (source.Length > 0)
{ {
shuffle.RunFallbackShuffle(source, dest); shuffle.Shuffle(source, destination);
} }
} }
/// <summary> /// <summary>
/// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <typeparam name="TShuffle">The type of shuffle struct.</typeparam> /// <typeparam name="TShuffle">The type of shuffle struct.</typeparam>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="shuffle">The type of shuffle to perform.</param> /// <param name="shuffle">The type of shuffle to perform.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Pad3Shuffle4<TShuffle>( public static void Pad3Shuffle4<TShuffle>(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
TShuffle shuffle) TShuffle shuffle)
where TShuffle : struct, IPad3Shuffle4 where TShuffle : struct, IPad3Shuffle4
{ {
VerifyPad3Shuffle4SpanInput(source, dest); VerifyPad3Shuffle4SpanInput(source, destination);
shuffle.ShuffleReduce(ref source, ref dest); shuffle.ShuffleReduce(ref source, ref destination);
// Deal with the remainder: // Deal with the remainder:
if (source.Length > 0) if (source.Length > 0)
{ {
shuffle.RunFallbackShuffle(source, dest); shuffle.Shuffle(source, destination);
} }
} }
/// <summary> /// <summary>
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/> /// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/>
/// using the control and store the results in <paramref name="dest"/>. /// using the control and store the results in <paramref name="destination"/>.
/// </summary> /// </summary>
/// <typeparam name="TShuffle">The type of shuffle struct.</typeparam> /// <typeparam name="TShuffle">The type of shuffle struct.</typeparam>
/// <param name="source">The source span of bytes.</param> /// <param name="source">The source span of bytes.</param>
/// <param name="dest">The destination span of bytes.</param> /// <param name="destination">The destination span of bytes.</param>
/// <param name="shuffle">The type of shuffle to perform.</param> /// <param name="shuffle">The type of shuffle to perform.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Shuffle4Slice3<TShuffle>( public static void Shuffle4Slice3<TShuffle>(
ReadOnlySpan<byte> source, ReadOnlySpan<byte> source,
Span<byte> dest, Span<byte> destination,
TShuffle shuffle) TShuffle shuffle)
where TShuffle : struct, IShuffle4Slice3 where TShuffle : struct, IShuffle4Slice3
{ {
VerifyShuffle4Slice3SpanInput(source, dest); VerifyShuffle4Slice3SpanInput(source, destination);
shuffle.ShuffleReduce(ref source, ref dest); shuffle.ShuffleReduce(ref source, ref destination);
// Deal with the remainder: // Deal with the remainder:
if (source.Length > 0) if (source.Length > 0)
{ {
shuffle.RunFallbackShuffle(source, dest); shuffle.Shuffle(source, destination);
} }
} }
private static void Shuffle4Remainder( private static void Shuffle4Remainder(
ReadOnlySpan<float> source, ReadOnlySpan<float> source,
Span<float> dest, Span<float> destination,
byte control) byte control)
{ {
ref float sBase = ref MemoryMarshal.GetReference(source); ref float sBase = ref MemoryMarshal.GetReference(source);
ref float dBase = ref MemoryMarshal.GetReference(dest); ref float dBase = ref MemoryMarshal.GetReference(destination);
Shuffle.InverseMMShuffle(control, out uint p3, out uint p2, out uint p1, out uint p0); Shuffle.InverseMMShuffle(control, out uint p3, out uint p2, out uint p1, out uint p0);
for (nuint i = 0; i < (uint)source.Length; i += 4) for (nuint i = 0; i < (uint)source.Length; i += 4)
@ -158,69 +158,69 @@ internal static partial class SimdUtils
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
internal static void VerifyShuffle4SpanInput<T>(ReadOnlySpan<T> source, Span<T> dest) internal static void VerifyShuffle4SpanInput<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct where T : struct
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length == dest.Length, source.Length == destination.Length,
nameof(source), nameof(source),
"Input spans must be of same length!"); "Input spans must be of same length!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length % 4 == 0, source.Length % 4 == 0,
nameof(source), nameof(source),
"Input spans must be divisable by 4!"); "Input spans must be divisible by 4!");
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> dest) private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct where T : struct
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length <= dest.Length, source.Length <= destination.Length,
nameof(source), nameof(source),
"Source should fit into dest!"); "Source should fit into destination!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length % 3 == 0, source.Length % 3 == 0,
nameof(source), nameof(source),
"Input spans must be divisable by 3!"); "Input spans must be divisible by 3!");
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan<byte> source, Span<byte> dest) private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length % 3 == 0, source.Length % 3 == 0,
nameof(source), nameof(source),
"Input span must be divisable by 3!"); "Input span must be divisible by 3!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
dest.Length % 4 == 0, destination.Length % 4 == 0,
nameof(dest), nameof(destination),
"Output span must be divisable by 4!"); "Output span must be divisible by 4!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length == dest.Length * 3 / 4, source.Length == destination.Length * 3 / 4,
nameof(source), nameof(source),
"Input span must be 3/4 the length of the output span!"); "Input span must be 3/4 the length of the output span!");
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan<byte> source, Span<byte> dest) private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan<byte> source, Span<byte> destination)
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(
source.Length % 4 == 0, source.Length % 4 == 0,
nameof(source), nameof(source),
"Input span must be divisable by 4!"); "Input span must be divisible by 4!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
dest.Length % 3 == 0, destination.Length % 3 == 0,
nameof(dest), nameof(destination),
"Output span must be divisable by 3!"); "Output span must be divisible by 3!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
dest.Length >= source.Length * 3 / 4, destination.Length >= source.Length * 3 / 4,
nameof(source), nameof(source),
"Output span must be at least 3/4 the length of the input span!"); "Output span must be at least 3/4 the length of the input span!");
} }
@ -509,6 +509,27 @@ internal static partial class SimdUtils
} }
} }
[MethodImpl(InliningOptions.ShortMethod)]
public static void MMShuffleSpan(ref Span<int> span, byte control)
{
InverseMMShuffle(
control,
out uint p3,
out uint p2,
out uint p1,
out uint p0);
ref int spanBase = ref MemoryMarshal.GetReference(span);
for (nuint i = 0; i < (uint)span.Length; i += 4)
{
Unsafe.Add(ref spanBase, i + 0) = (int)(p0 + i);
Unsafe.Add(ref spanBase, i + 1) = (int)(p1 + i);
Unsafe.Add(ref spanBase, i + 2) = (int)(p2 + i);
Unsafe.Add(ref spanBase, i + 3) = (int)(p3 + i);
}
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void InverseMMShuffle( public static void InverseMMShuffle(
byte control, byte control,

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

@ -0,0 +1,176 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector128{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector128Utilities
{
/// <summary>
/// Gets a value indicating whether shuffle operations are supported.
/// </summary>
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Sse.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Ssse3.IsSupported || AdvSimd.Arm64.IsSupported;
}
/// <summary>
/// Gets a value indicating whether right align operations are supported.
/// </summary>
public static bool SupportsRightAlign
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Ssse3.IsSupported || AdvSimd.IsSupported;
}
/// <summary>
/// Gets a value indicating whether right or left byte shift operations are supported.
/// </summary>
public static bool SupportsShiftByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Sse2.IsSupported || AdvSimd.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using the control.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector128{Single}"/>.</returns>
public static Vector128<float> Shuffle(Vector128<float> vector, [ConstantExpected] byte control)
{
if (Sse.IsSupported)
{
return Sse.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">
/// The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>
/// A new vector containing the values from <paramref name="vector" /> selected by the given <paramref name="indices" />.
/// </returns>
public static Vector128<byte> Shuffle(Vector128<byte> vector, Vector128<byte> indices)
{
if (Ssse3.IsSupported)
{
return Ssse3.Shuffle(vector, indices);
}
if (AdvSimd.Arm64.IsSupported)
{
return AdvSimd.Arm64.VectorTableLookup(vector, indices);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Shifts a 128-bit value right by a specified number of bytes while shifting in zeros.
/// </summary>
/// <param name="value">The value to shift.</param>
/// <param name="numBytes">The number of bytes to shift by.</param>
/// <returns>The <see cref="Vector128{Byte}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<byte> ShiftRightBytesInVector(Vector128<byte> value, [ConstantExpected(Max = (byte)15)] byte numBytes)
{
if (Sse2.IsSupported)
{
return Sse2.ShiftRightLogical128BitLane(value, numBytes);
}
if (AdvSimd.IsSupported)
{
return AdvSimd.ExtractVector128(value, Vector128<byte>.Zero, numBytes);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Shifts a 128-bit value left by a specified number of bytes while shifting in zeros.
/// </summary>
/// <param name="value">The value to shift.</param>
/// <param name="numBytes">The number of bytes to shift by.</param>
/// <returns>The <see cref="Vector128{Byte}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<byte> ShiftLeftBytesInVector(Vector128<byte> value, [ConstantExpected(Max = (byte)15)] byte numBytes)
{
if (Sse2.IsSupported)
{
return Sse2.ShiftLeftLogical128BitLane(value, numBytes);
}
if (AdvSimd.IsSupported)
{
#pragma warning disable CA1857 // A constant is expected for the parameter
return AdvSimd.ExtractVector128(Vector128<byte>.Zero, value, (byte)(Vector128<byte>.Count - numBytes));
#pragma warning restore CA1857 // A constant is expected for the parameter
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Right aligns elements of two source 128-bit values depending on bits in a mask.
/// </summary>
/// <param name="left">The left hand source vector.</param>
/// <param name="right">The right hand source vector.</param>
/// <param name="mask">An 8-bit mask used for the operation.</param>
/// <returns>The <see cref="Vector128{Byte}"/>.</returns>
public static Vector128<byte> AlignRight(Vector128<byte> left, Vector128<byte> right, [ConstantExpected(Max = (byte)15)] byte mask)
{
if (Ssse3.IsSupported)
{
return Ssse3.AlignRight(left, right, mask);
}
if (AdvSimd.IsSupported)
{
return AdvSimd.ExtractVector128(right, left, mask);
}
ThrowUnreachableException();
return default;
}
[DoesNotReturn]
private static void ThrowUnreachableException() => throw new UnreachableException();
}

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

@ -0,0 +1,78 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector256{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector256Utilities
{
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx2.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
public static Vector256<float> Shuffle(Vector256<float> vector, [ConstantExpected] byte control)
{
if (Avx.IsSupported)
{
return Avx.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.</summary>
/// <param name="vector">
/// The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
public static Vector256<byte> Shuffle(Vector256<byte> vector, Vector256<byte> indices)
{
if (Avx2.IsSupported)
{
return Avx2.Shuffle(vector, indices);
}
ThrowUnreachableException();
return default;
}
[DoesNotReturn]
private static void ThrowUnreachableException() => throw new UnreachableException();
}

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

@ -0,0 +1,80 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Common.Helpers;
/// <summary>
/// Defines utility methods for <see cref="Vector512{T}"/> that have either:
/// <list type="number">
/// <item>Not yet been normalized in the runtime.</item>
/// <item>Produce codegen that is poorly optimized by the runtime.</item>
/// </list>
/// Should only be used if the intrinsics are available.
/// </summary>
internal static class Vector512Utilities
{
/// <summary>
/// Gets a value indicating whether shuffle float operations are supported.
/// </summary>
public static bool SupportsShuffleFloat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx512F.IsSupported;
}
/// <summary>
/// Gets a value indicating whether shuffle byte operations are supported.
/// </summary>
public static bool SupportsShuffleByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Avx512BW.IsSupported;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using the control.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="control">The shuffle control byte.</param>
/// <returns>The <see cref="Vector512{Single}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<float> Shuffle(Vector512<float> vector, [ConstantExpected] byte control)
{
if (Avx512F.IsSupported)
{
return Avx512F.Shuffle(vector, vector, control);
}
ThrowUnreachableException();
return default;
}
/// <summary>
/// Creates a new vector by selecting values from an input vector using a set of indices.
/// </summary>
/// <param name="vector">The input vector from which values are selected.</param>
/// <param name="indices">
/// The per-element indices used to select a value from <paramref name="vector" />.
/// </param>
/// <returns>The <see cref="Vector512{Byte}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector512<byte> Shuffle(Vector512<byte> vector, Vector512<byte> indices)
{
if (Avx512BW.IsSupported)
{
return Avx512BW.Shuffle(vector, indices);
}
ThrowUnreachableException();
return default;
}
[DoesNotReturn]
private static void ThrowUnreachableException() => throw new UnreachableException();
}

4
src/ImageSharp/Formats/AnimationUtilities.cs

@ -50,7 +50,7 @@ internal static class AnimationUtilities
Span<Rgba32> next = buffers.GetSpan().Slice(currentFrame.Width * 2, currentFrame.Width); Span<Rgba32> next = buffers.GetSpan().Slice(currentFrame.Width * 2, currentFrame.Width);
Span<Rgba32> result = buffers.GetSpan()[(currentFrame.Width * 3)..]; Span<Rgba32> result = buffers.GetSpan()[(currentFrame.Width * 3)..];
Rgba32 bg = replacement; Rgba32 bg = replacement.ToPixel<Rgba32>();
int top = int.MinValue; int top = int.MinValue;
int bottom = int.MaxValue; int bottom = int.MaxValue;
@ -232,7 +232,7 @@ internal static class AnimationUtilities
ref Rgba32 r = ref Unsafe.Add(ref MemoryMarshal.GetReference(result), x); ref Rgba32 r = ref Unsafe.Add(ref MemoryMarshal.GetReference(result), x);
bool peq = c.Rgba == (previousFrame != null ? p.Rgba : bg.Rgba); bool peq = c.Rgba == (previousFrame != null ? p.Rgba : bg.Rgba);
Rgba32 val = (blend & peq) ? replacement : c; Rgba32 val = (blend & peq) ? bg : c;
peq &= nextFrame == null || (n.Rgba >> 24 >= c.Rgba >> 24); peq &= nextFrame == null || (n.Rgba >> 24 >= c.Rgba >> 24);
r = val; r = val;

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

@ -294,72 +294,60 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
private void ReadRle<TPixel>(BufferedReadStream stream, BmpCompression compression, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted) private void ReadRle<TPixel>(BufferedReadStream stream, BmpCompression compression, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; using IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean);
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean)) using IMemoryOwner<bool> undefinedPixels = this.memoryAllocator.Allocate<bool>(width * height, AllocationOptions.Clean);
using (IMemoryOwner<bool> undefinedPixels = this.memoryAllocator.Allocate<bool>(width * height, AllocationOptions.Clean)) using IMemoryOwner<bool> rowsWithUndefinedPixels = this.memoryAllocator.Allocate<bool>(height, AllocationOptions.Clean);
using (IMemoryOwner<bool> rowsWithUndefinedPixels = this.memoryAllocator.Allocate<bool>(height, AllocationOptions.Clean)) Span<bool> rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span;
Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span;
Span<byte> bufferSpan = buffer.Memory.Span;
if (compression is BmpCompression.RLE8)
{ {
Span<bool> rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span; }
Span<byte> bufferSpan = buffer.Memory.Span; else
if (compression is BmpCompression.RLE8) {
{ this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); }
}
else
{
this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
}
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = Invert(y, height, inverted); int newY = Invert(y, height, inverted);
int rowStartIdx = y * width; int rowStartIdx = y * width;
Span<byte> bufferRow = bufferSpan.Slice(rowStartIdx, width); Span<byte> bufferRow = bufferSpan.Slice(rowStartIdx, width);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
if (rowHasUndefinedPixels) if (rowHasUndefinedPixels)
{
// Slow path with undefined pixels.
for (int x = 0; x < width; x++)
{ {
// Slow path with undefined pixels. byte colorIdx = bufferRow[x];
for (int x = 0; x < width; x++) if (undefinedPixelsSpan[rowStartIdx + x])
{ {
byte colorIdx = bufferRow[x]; pixelRow[x] = this.rleSkippedPixelHandling switch
if (undefinedPixelsSpan[rowStartIdx + x])
{
switch (this.rleSkippedPixelHandling)
{
case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4]));
break;
case RleSkippedPixelHandling.Transparent:
color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
else
{ {
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4])); RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4])),
} RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero),
pixelRow[x] = color; // Default handling for skipped pixels is black (which is what System.Drawing is also doing).
_ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
};
} }
} else
else
{
// Fast path without any undefined pixels.
for (int x = 0; x < width; x++)
{ {
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4])); pixelRow[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIdx * 4]));
pixelRow[x] = color;
} }
} }
} }
else
{
// Fast path without any undefined pixels.
for (int x = 0; x < width; x++)
{
pixelRow[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]));
}
}
} }
} }
@ -375,66 +363,54 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
private void ReadRle24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted) private void ReadRle24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; using IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height * 3, AllocationOptions.Clean);
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height * 3, AllocationOptions.Clean)) using IMemoryOwner<bool> undefinedPixels = this.memoryAllocator.Allocate<bool>(width * height, AllocationOptions.Clean);
using (IMemoryOwner<bool> undefinedPixels = this.memoryAllocator.Allocate<bool>(width * height, AllocationOptions.Clean)) using IMemoryOwner<bool> rowsWithUndefinedPixels = this.memoryAllocator.Allocate<bool>(height, AllocationOptions.Clean);
using (IMemoryOwner<bool> rowsWithUndefinedPixels = this.memoryAllocator.Allocate<bool>(height, AllocationOptions.Clean)) Span<bool> rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span;
{ Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span;
Span<bool> rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; Span<byte> bufferSpan = buffer.GetSpan();
Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span;
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y];
if (rowHasUndefinedPixels)
{ {
int newY = Invert(y, height, inverted); // Slow path with undefined pixels.
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY); int yMulWidth = y * width;
bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; int rowStartIdx = yMulWidth * 3;
if (rowHasUndefinedPixels) for (int x = 0; x < width; x++)
{ {
// Slow path with undefined pixels. int idx = rowStartIdx + (x * 3);
int yMulWidth = y * width; if (undefinedPixelsSpan[yMulWidth + x])
int rowStartIdx = yMulWidth * 3;
for (int x = 0; x < width; x++)
{ {
int idx = rowStartIdx + (x * 3); pixelRow[x] = this.rleSkippedPixelHandling switch
if (undefinedPixelsSpan[yMulWidth + x])
{
switch (this.rleSkippedPixelHandling)
{
case RleSkippedPixelHandling.FirstColorOfPalette:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
break;
case RleSkippedPixelHandling.Transparent:
color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
else
{ {
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])); RleSkippedPixelHandling.FirstColorOfPalette => TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])),
} RleSkippedPixelHandling.Transparent => TPixel.FromScaledVector4(Vector4.Zero),
pixelRow[x] = color; // Default handling for skipped pixels is black (which is what System.Drawing is also doing).
_ => TPixel.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)),
};
} }
} else
else
{
// Fast path without any undefined pixels.
int rowStartIdx = y * width * 3;
for (int x = 0; x < width; x++)
{ {
int idx = rowStartIdx + (x * 3); pixelRow[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
pixelRow[x] = color;
} }
} }
} }
else
{
// Fast path without any undefined pixels.
int rowStartIdx = y * width * 3;
for (int x = 0; x < width; x++)
{
int idx = rowStartIdx + (x * 3);
pixelRow[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
}
}
} }
} }
@ -492,7 +468,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
int max = cmd[1]; int max = cmd[1];
int bytesToRead = (int)(((uint)max + 1) / 2); int bytesToRead = (int)(((uint)max + 1) / 2);
Span<byte> run = bytesToRead <= 128 ? scratchBuffer.Slice(0, bytesToRead) : new byte[bytesToRead]; Span<byte> run = bytesToRead <= 128 ? scratchBuffer[..bytesToRead] : new byte[bytesToRead];
stream.Read(run); stream.Read(run);
@ -598,7 +574,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Take this number of bytes from the stream as uncompressed data. // Take this number of bytes from the stream as uncompressed data.
int length = cmd[1]; int length = cmd[1];
Span<byte> run = length <= 128 ? scratchBuffer.Slice(0, length) : new byte[length]; Span<byte> run = length <= 128 ? scratchBuffer[..length] : new byte[length];
stream.Read(run); stream.Read(run);
@ -680,7 +656,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
int length = cmd[1]; int length = cmd[1];
int length3 = length * 3; int length3 = length * 3;
Span<byte> run = length3 <= 128 ? scratchBuffer.Slice(0, length3) : new byte[length3]; Span<byte> run = length3 <= 128 ? scratchBuffer[..length3] : new byte[length3];
stream.Read(run); stream.Read(run);
@ -835,7 +811,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
} }
using IMemoryOwner<byte> row = this.memoryAllocator.Allocate<byte>(arrayWidth + padding, AllocationOptions.Clean); using IMemoryOwner<byte> row = this.memoryAllocator.Allocate<byte>(arrayWidth + padding, AllocationOptions.Clean);
TPixel color = default;
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -856,8 +831,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry;
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIndex])); pixelRow[newX] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIndex]));
pixelRow[newX] = color;
} }
offset++; offset++;
@ -882,8 +856,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
int padding = CalculatePadding(width, 2); int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding; int stride = (width * 2) + padding;
TPixel color = default;
int rightShiftRedMask = CalculateRightShift((uint)redMask); int rightShiftRedMask = CalculateRightShift((uint)redMask);
int rightShiftGreenMask = CalculateRightShift((uint)greenMask); int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
int rightShiftBlueMask = CalculateRightShift((uint)blueMask); int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
@ -917,8 +889,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask); int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask);
Rgb24 rgb = new((byte)r, (byte)g, (byte)b); Rgb24 rgb = new((byte)r, (byte)g, (byte)b);
color.FromRgb24(rgb); pixelRow[x] = TPixel.FromRgb24(rgb);
pixelRow[x] = color;
offset += 2; offset += 2;
} }
} }
@ -1107,8 +1078,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{ {
Bgra32 bgra = bgraRowSpan[x]; Bgra32 bgra = bgraRowSpan[x];
bgra.A = byte.MaxValue; bgra.A = byte.MaxValue;
ref TPixel pixel = ref pixelSpan[x]; pixelSpan[x] = TPixel.FromBgra32(bgra);
pixel.FromBgra32(bgra);
} }
} }
} }
@ -1129,7 +1099,6 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
private void ReadRgb32BitFields<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) private void ReadRgb32BitFields<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default;
int padding = CalculatePadding(width, 4); int padding = CalculatePadding(width, 4);
int stride = (width * 4) + padding; int stride = (width * 4) + padding;
@ -1179,18 +1148,17 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
g * invMaxValueGreen, g * invMaxValueGreen,
b * invMaxValueBlue, b * invMaxValueBlue,
alpha); alpha);
color.FromScaledVector4(vector4); pixelRow[x] = TPixel.FromScaledVector4(vector4);
} }
else else
{ {
byte r = (byte)((temp & redMask) >> rightShiftRedMask); byte r = (byte)((temp & redMask) >> rightShiftRedMask);
byte g = (byte)((temp & greenMask) >> rightShiftGreenMask); byte g = (byte)((temp & greenMask) >> rightShiftGreenMask);
byte b = (byte)((temp & blueMask) >> rightShiftBlueMask); byte b = (byte)((temp & blueMask) >> rightShiftBlueMask);
byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255; byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : byte.MaxValue;
color.FromRgba32(new Rgba32(r, g, b, a)); pixelRow[x] = TPixel.FromRgba32(new(r, g, b, a));
} }
pixelRow[x] = color;
offset += 4; offset += 4;
} }
} }
@ -1468,7 +1436,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry; colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
} }
palette = Array.Empty<byte>(); palette = [];
if (colorMapSizeBytes > 0) if (colorMapSizeBytes > 0)
{ {

20
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -185,7 +185,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{ {
uint frameCount = 0; uint frameCount = 0;
ImageFrameMetadata? previousFrame = null; ImageFrameMetadata? previousFrame = null;
List<ImageFrameMetadata> framesMetadata = new(); List<ImageFrameMetadata> framesMetadata = [];
try try
{ {
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
@ -595,9 +595,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++)
{ {
int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx); int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx);
ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); Unsafe.Add(ref rowRef, (uint)x) = TPixel.FromRgb24(colorTable[index]);
Rgb24 rgb = colorTable[index];
pixel.FromRgb24(rgb);
} }
} }
else else
@ -613,9 +611,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
} }
int index = Numerics.Clamp(rawIndex, 0, colorTableMaxIdx); int index = Numerics.Clamp(rawIndex, 0, colorTableMaxIdx);
ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); Unsafe.Add(ref rowRef, (uint)x) = TPixel.FromRgb24(colorTable[index]);
Rgb24 rgb = colorTable[index];
pixel.FromRgb24(rgb);
} }
} }
} }
@ -711,10 +707,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
Color[] colorTable = new Color[this.imageDescriptor.LocalColorTableSize]; Color[] colorTable = new Color[this.imageDescriptor.LocalColorTableSize];
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]); ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]);
for (int i = 0; i < colorTable.Length; i++) Color.FromPixel(rgbTable, colorTable);
{
colorTable[i] = new Color(rgbTable[i]);
}
gifMeta.LocalColorTable = colorTable; gifMeta.LocalColorTable = colorTable;
} }
@ -789,10 +782,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
Color[] colorTable = new Color[this.logicalScreenDescriptor.GlobalColorTableSize]; Color[] colorTable = new Color[this.logicalScreenDescriptor.GlobalColorTableSize];
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(globalColorTableSpan); ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(globalColorTableSpan);
for (int i = 0; i < colorTable.Length; i++) Color.FromPixel(rgbTable, colorTable);
{
colorTable[i] = new Color(rgbTable[i]);
}
this.gifMetadata.GlobalColorTable = colorTable; this.gifMetadata.GlobalColorTable = colorTable;
} }

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

@ -169,7 +169,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
this.EncodeFirstFrame(stream, frameMetadata, quantized); this.EncodeFirstFrame(stream, frameMetadata, quantized);
// Capture the global palette for reuse on subsequent frames and cleanup the quantized frame. // Capture the global palette for reuse on subsequent frames and cleanup the quantized frame.
TPixel[] globalPalette = image.Frames.Count == 1 ? Array.Empty<TPixel>() : quantized.Palette.ToArray(); TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray();
this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMethod); this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMethod);
@ -488,8 +488,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
int index = -1; int index = -1;
if (quantized != null) if (quantized != null)
{ {
TPixel transparentPixel = default; TPixel transparentPixel = TPixel.FromScaledVector4(Vector4.Zero);
transparentPixel.FromScaledVector4(Vector4.Zero);
ReadOnlySpan<TPixel> palette = quantized.Palette.Span; ReadOnlySpan<TPixel> palette = quantized.Palette.Span;
// Transparent pixels are much more likely to be found at the end of a palette. // Transparent pixels are much more likely to be found at the end of a palette.
@ -693,7 +692,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
} }
IMemoryOwner<byte>? owner = null; IMemoryOwner<byte>? owner = null;
Span<byte> extensionBuffer = stackalloc byte[0]; // workaround compiler limitation scoped Span<byte> extensionBuffer = []; // workaround compiler limitation
if (extensionSize > 128) if (extensionSize > 128)
{ {
owner = this.memoryAllocator.Allocate<byte>(extensionSize + 3); owner = this.memoryAllocator.Allocate<byte>(extensionSize + 3);

53
src/ImageSharp/Formats/PixelTypeInfo.cs

@ -1,53 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
// TODO: Review this class as it's used to represent 2 different things.
// 1.The encoded image pixel format.
// 2. The pixel format of the decoded image.
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Contains information about the pixels that make up an images visual data.
/// </summary>
public class PixelTypeInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelTypeInfo"/> class.
/// </summary>
/// <param name="bitsPerPixel">Color depth, in number of bits per pixel.</param>
public PixelTypeInfo(int bitsPerPixel)
=> this.BitsPerPixel = bitsPerPixel;
/// <summary>
/// Initializes a new instance of the <see cref="PixelTypeInfo"/> class.
/// </summary>
/// <param name="bitsPerPixel">Color depth, in number of bits per pixel.</param>
/// <param name="alpha">The pixel alpha transparency behavior.</param>
public PixelTypeInfo(int bitsPerPixel, PixelAlphaRepresentation alpha)
{
this.BitsPerPixel = bitsPerPixel;
this.AlphaRepresentation = alpha;
}
/// <summary>
/// Gets color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; }
/// <summary>
/// Gets the pixel alpha transparency behavior.
/// <see langword="null"/> means unknown, unspecified.
/// </summary>
public PixelAlphaRepresentation? AlphaRepresentation { get; }
internal static PixelTypeInfo Create<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
=> new(Unsafe.SizeOf<TPixel>() * 8);
internal static PixelTypeInfo Create<TPixel>(PixelAlphaRepresentation alpha)
where TPixel : unmanaged, IPixel<TPixel>
=> new(Unsafe.SizeOf<TPixel>() * 8, alpha);
}

2
src/ImageSharp/Formats/Png/PngBitDepth.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
// Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it. // Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it.

9
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -1240,10 +1240,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
Color[] colorTable = new Color[palette.Length / Unsafe.SizeOf<Rgb24>()]; Color[] colorTable = new Color[palette.Length / Unsafe.SizeOf<Rgb24>()];
ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(palette); ReadOnlySpan<Rgb24> rgbTable = MemoryMarshal.Cast<byte, Rgb24>(palette);
for (int i = 0; i < colorTable.Length; i++) Color.FromPixel(rgbTable, colorTable);
{
colorTable[i] = new Color(rgbTable[i]);
}
if (alpha.Length > 0) if (alpha.Length > 0)
{ {
@ -1276,14 +1273,14 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2));
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
pngMetadata.TransparentColor = new(new Rgb48(rc, gc, bc)); pngMetadata.TransparentColor = Color.FromPixel(new Rgb48(rc, gc, bc));
return; return;
} }
byte r = ReadByteLittleEndian(alpha, 0); byte r = ReadByteLittleEndian(alpha, 0);
byte g = ReadByteLittleEndian(alpha, 2); byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4); byte b = ReadByteLittleEndian(alpha, 4);
pngMetadata.TransparentColor = new(new Rgb24(r, g, b)); pngMetadata.TransparentColor = Color.FromPixel(new Rgb24(r, g, b));
} }
} }
else if (this.pngColorType == PngColorType.Grayscale) else if (this.pngColorType == PngColorType.Grayscale)

117
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -328,18 +328,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
=> clone.ProcessPixelRows(accessor => => clone.ProcessPixelRows(accessor =>
{ {
// TODO: We should be able to speed this up with SIMD and masking. // TODO: We should be able to speed this up with SIMD and masking.
Rgba32 rgba32 = default; Rgba32 transparent = Color.Transparent.ToPixel<Rgba32>();
Rgba32 transparent = Color.Transparent;
for (int y = 0; y < accessor.Height; y++) for (int y = 0; y < accessor.Height; y++)
{ {
Span<TPixel> span = accessor.GetRowSpan(y); Span<TPixel> span = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; x++) for (int x = 0; x < accessor.Width; x++)
{ {
span[x].ToRgba32(ref rgba32); ref TPixel pixel = ref span[x];
Rgba32 rgba = pixel.ToRgba32();
if (rgba32.A is 0) if (rgba.A is 0)
{ {
span[x].FromRgba32(transparent); pixel = TPixel.FromRgba32(transparent);
} }
} }
} }
@ -1066,7 +1065,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
else else
{ {
alpha.Clear(); alpha.Clear();
Rgb24 rgb = pngMetadata.TransparentColor.Value.ToRgb24(); Rgb24 rgb = pngMetadata.TransparentColor.Value.ToPixel<Rgb24>();
alpha[1] = rgb.R; alpha[1] = rgb.R;
alpha[3] = rgb.G; alpha[3] = rgb.G;
alpha[5] = rgb.B; alpha[5] = rgb.B;
@ -1466,23 +1465,48 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
// Use options, then check metadata, if nothing set there then we suggest // Use options, then check metadata, if nothing set there then we suggest
// a sensible default based upon the pixel format. // a sensible default based upon the pixel format.
this.colorType = encoder.ColorType ?? pngMetadata.ColorType ?? SuggestColorType<TPixel>(); PngColorType? colorType = encoder.ColorType ?? pngMetadata.ColorType;
if (!encoder.FilterMethod.HasValue) byte? bits = (byte?)(encoder.BitDepth ?? pngMetadata.BitDepth);
if (colorType is null || bits is null)
{ {
// Specification recommends default filter method None for paletted images and Paeth for others. PixelTypeInfo info = TPixel.GetPixelTypeInfo();
this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth; PixelComponentInfo? componentInfo = info.ComponentInfo;
colorType ??= SuggestColorType<TPixel>(in info);
if (bits is null)
{
// TODO: Update once we stop abusing PixelTypeInfo in decoders.
if (componentInfo.HasValue)
{
PixelComponentInfo c = componentInfo.Value;
bits = (byte)SuggestBitDepth<TPixel>(in c);
}
else
{
bits = (byte)PngBitDepth.Bit8;
}
}
} }
// Ensure bit depth and color type are a supported combination. // Ensure bit depth and color type are a supported combination.
// Bit8 is the only bit depth supported by all color types. // Bit8 is the only bit depth supported by all color types.
byte bits = (byte)(encoder.BitDepth ?? pngMetadata.BitDepth ?? SuggestBitDepth<TPixel>()); byte[] validBitDepths = PngConstants.ColorTypes[colorType.Value];
byte[] validBitDepths = PngConstants.ColorTypes[this.colorType];
if (Array.IndexOf(validBitDepths, bits) == -1) if (Array.IndexOf(validBitDepths, bits) == -1)
{ {
bits = (byte)PngBitDepth.Bit8; bits = (byte)PngBitDepth.Bit8;
} }
this.bitDepth = bits; this.colorType = colorType.Value;
this.bitDepth = bits.Value;
if (!encoder.FilterMethod.HasValue)
{
// Specification recommends default filter method None for paletted images and Paeth for others.
this.filterMethod = this.colorType is PngColorType.Palette ? PngFilterMethod.None : PngFilterMethod.Paeth;
}
use16Bit = bits == (byte)PngBitDepth.Bit16; use16Bit = bits == (byte)PngBitDepth.Bit16;
bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit); bytesPerPixel = CalculateBytesPerPixel(this.colorType, use16Bit);
@ -1611,53 +1635,44 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// <summary> /// <summary>
/// Returns a suggested <see cref="PngColorType"/> for the given <typeparamref name="TPixel"/> /// Returns a suggested <see cref="PngColorType"/> for the given <typeparamref name="TPixel"/>
/// This is not exhaustive but covers many common pixel formats.
/// </summary> /// </summary>
/// <param name="info">The pixel type info.</param>
/// <typeparam name="TPixel">The type of pixel format.</typeparam> /// <typeparam name="TPixel">The type of pixel format.</typeparam>
private static PngColorType SuggestColorType<TPixel>() private static PngColorType SuggestColorType<TPixel>(in PixelTypeInfo info)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> default(TPixel) switch {
{ if (info.AlphaRepresentation == PixelAlphaRepresentation.None)
A8 => PngColorType.GrayscaleWithAlpha, {
Argb32 => PngColorType.RgbWithAlpha, return info.ColorType switch
Bgr24 => PngColorType.Rgb, {
Bgra32 => PngColorType.RgbWithAlpha, PixelColorType.Grayscale => PngColorType.Grayscale,
L8 => PngColorType.Grayscale, _ => PngColorType.Rgb,
L16 => PngColorType.Grayscale, };
La16 => PngColorType.GrayscaleWithAlpha, }
La32 => PngColorType.GrayscaleWithAlpha,
Rgb24 => PngColorType.Rgb, return info.ColorType switch
Rgba32 => PngColorType.RgbWithAlpha, {
Rgb48 => PngColorType.Rgb, PixelColorType.Grayscale | PixelColorType.Alpha or PixelColorType.Alpha => PngColorType.GrayscaleWithAlpha,
Rgba64 => PngColorType.RgbWithAlpha, _ => PngColorType.RgbWithAlpha,
RgbaVector => PngColorType.RgbWithAlpha,
_ => PngColorType.RgbWithAlpha
}; };
}
/// <summary> /// <summary>
/// Returns a suggested <see cref="PngBitDepth"/> for the given <typeparamref name="TPixel"/> /// Returns a suggested <see cref="PngBitDepth"/> for the given <typeparamref name="TPixel"/>
/// This is not exhaustive but covers many common pixel formats.
/// </summary> /// </summary>
/// <param name="info">The pixel type info.</param>
/// <typeparam name="TPixel">The type of pixel format.</typeparam> /// <typeparam name="TPixel">The type of pixel format.</typeparam>
private static PngBitDepth SuggestBitDepth<TPixel>() private static PngBitDepth SuggestBitDepth<TPixel>(in PixelComponentInfo info)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> default(TPixel) switch {
{ int bits = info.GetMaximumComponentPrecision();
A8 => PngBitDepth.Bit8, if (bits > (int)PixelComponentBitDepth.Bit8)
Argb32 => PngBitDepth.Bit8, {
Bgr24 => PngBitDepth.Bit8, return PngBitDepth.Bit16;
Bgra32 => PngBitDepth.Bit8, }
L8 => PngBitDepth.Bit8,
L16 => PngBitDepth.Bit16, return PngBitDepth.Bit8;
La16 => PngBitDepth.Bit8, }
La32 => PngBitDepth.Bit16,
Rgb24 => PngBitDepth.Bit8,
Rgba32 => PngBitDepth.Bit8,
Rgb48 => PngBitDepth.Bit16,
Rgba64 => PngBitDepth.Bit16,
RgbaVector => PngBitDepth.Bit16,
_ => PngBitDepth.Bit8
};
private unsafe struct ScratchBuffer private unsafe struct ScratchBuffer
{ {

119
src/ImageSharp/Formats/Png/PngScanlineProcessor.cs

@ -42,7 +42,6 @@ internal static class PngScanlineProcessor
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + frameControl.XOffset; uint offset = pixelOffset + frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(bitDepth) - 1); int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(bitDepth) - 1);
@ -55,8 +54,7 @@ internal static class PngScanlineProcessor
for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2) for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2)
{ {
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
pixel.FromL16(Unsafe.As<ushort, L16>(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = TPixel.FromL16(Unsafe.As<ushort, L16>(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
@ -64,8 +62,7 @@ internal static class PngScanlineProcessor
for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++) for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++)
{ {
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
pixel.FromL8(Unsafe.As<byte, L8>(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = TPixel.FromL8(Unsafe.As<byte, L8>(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -75,30 +72,22 @@ internal static class PngScanlineProcessor
if (bitDepth == 16) if (bitDepth == 16)
{ {
L16 transparent = transparentColor.Value.ToPixel<L16>(); L16 transparent = transparentColor.Value.ToPixel<L16>();
La32 source = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2) for (nuint x = offset; x < frameControl.XMax; x += increment, o += 2)
{ {
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.L = luminance; La32 source = new(luminance, luminance.Equals(transparent.PackedValue) ? ushort.MinValue : ushort.MaxValue);
source.A = luminance.Equals(transparent.PackedValue) ? ushort.MinValue : ushort.MaxValue; Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa32(source);
pixel.FromLa32(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
byte transparent = (byte)(transparentColor.Value.ToPixel<L8>().PackedValue * scaleFactor); byte transparent = (byte)(transparentColor.Value.ToPixel<L8>().PackedValue * scaleFactor);
La16 source = default;
for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++) for (nuint x = offset, o = 0; x < frameControl.XMax; x += increment, o++)
{ {
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
source.L = luminance; La16 source = new(luminance, luminance.Equals(transparent) ? byte.MinValue : byte.MaxValue);
source.A = luminance.Equals(transparent) ? byte.MinValue : byte.MaxValue; Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa16(source);
pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
} }
@ -133,34 +122,28 @@ internal static class PngScanlineProcessor
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + frameControl.XOffset; uint offset = pixelOffset + frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (bitDepth == 16) if (bitDepth == 16)
{ {
La32 source = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += 4) for (nuint x = offset; x < frameControl.XMax; x += increment, o += 4)
{ {
source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); ushort l = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
pixel.FromLa32(source); Unsafe.Add(ref rowSpanRef, (uint)x) = TPixel.FromLa32(new(l, a));
Unsafe.Add(ref rowSpanRef, (uint)x) = pixel;
} }
} }
else else
{ {
La16 source = default;
nuint offset2 = 0; nuint offset2 = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment) for (nuint x = offset; x < frameControl.XMax; x += increment)
{ {
source.L = Unsafe.Add(ref scanlineSpanRef, offset2); byte l = Unsafe.Add(ref scanlineSpanRef, offset2);
source.A = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample); byte a = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample);
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromLa16(new(l, a));
pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
offset2 += bytesPerPixel; offset2 += bytesPerPixel;
} }
} }
@ -194,7 +177,6 @@ internal static class PngScanlineProcessor
PngThrowHelper.ThrowMissingPalette(); PngThrowHelper.ThrowMissingPalette();
} }
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
ref Color paletteBase = ref MemoryMarshal.GetReference(palette.Value.Span); ref Color paletteBase = ref MemoryMarshal.GetReference(palette.Value.Span);
@ -202,8 +184,7 @@ internal static class PngScanlineProcessor
for (nuint x = pixelOffset, o = 0; x < frameControl.XMax; x += increment, o++) for (nuint x = pixelOffset, o = 0; x < frameControl.XMax; x += increment, o++)
{ {
uint index = Unsafe.Add(ref scanlineSpanRef, o); uint index = Unsafe.Add(ref scanlineSpanRef, o);
pixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToRgba32()); Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(Unsafe.Add(ref paletteBase, index).ToPixel<Rgba32>());
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -243,8 +224,6 @@ internal static class PngScanlineProcessor
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + frameControl.XOffset; uint offset = pixelOffset + frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
@ -252,16 +231,13 @@ internal static class PngScanlineProcessor
{ {
if (bitDepth == 16) if (bitDepth == 16)
{ {
Rgb48 rgb48 = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
{ {
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgb48(new(r, g, b));
pixel.FromRgb48(rgb48);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else if (pixelOffset == 0 && increment == 1) else if (pixelOffset == 0 && increment == 1)
@ -274,16 +250,13 @@ internal static class PngScanlineProcessor
} }
else else
{ {
Rgb24 rgb = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
{ {
rgb.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
rgb.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample)));
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgb24(new(r, g, b));
pixel.FromRgb24(rgb);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
@ -293,27 +266,20 @@ internal static class PngScanlineProcessor
if (bitDepth == 16) if (bitDepth == 16)
{ {
Rgb48 transparent = transparentColor.Value.ToPixel<Rgb48>(); Rgb48 transparent = transparentColor.Value.ToPixel<Rgb48>();
Rgba64 rgba = default;
Rgb48 rgb48 = default;
Rgba64 rgba64 = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
{ {
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgba.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgba.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); rgba.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
rgba.A = rgba.Rgb.Equals(transparent) ? ushort.MinValue : ushort.MaxValue;
rgba64.Rgb = rgb48; Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(rgba);
rgba64.A = rgb48.Equals(transparent) ? ushort.MinValue : ushort.MaxValue;
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else else
{ {
Rgb24 transparent = transparentColor.Value.ToPixel<Rgb24>(); Rgb24 transparent = transparentColor.Value.ToPixel<Rgb24>();
Rgba32 rgba = default; Rgba32 rgba = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
@ -322,9 +288,7 @@ internal static class PngScanlineProcessor
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample)));
rgba.A = transparent.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; rgba.A = transparent.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue;
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(rgba);
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
} }
@ -362,22 +326,18 @@ internal static class PngScanlineProcessor
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + frameControl.XOffset; uint offset = pixelOffset + frameControl.XOffset;
TPixel pixel = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (bitDepth == 16) if (bitDepth == 16)
{ {
Rgba64 rgba64 = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
{ {
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample));
rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample));
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(new(r, g, b, a));
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
else if (pixelOffset == 0 && increment == 1) else if (pixelOffset == 0 && increment == 1)
@ -391,17 +351,14 @@ internal static class PngScanlineProcessor
else else
{ {
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
Rgba32 rgba = default;
int o = 0; int o = 0;
for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel)
{ {
rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample)));
rgba.A = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); byte a = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample)));
Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(new(r, g, b, a));
pixel.FromRgba32(rgba);
Unsafe.Add(ref rowSpanRef, x) = pixel;
} }
} }
} }

14
src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs

@ -149,7 +149,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
Span<Rgba32> previouslySeenPixels = previouslySeenPixelsBuffer.GetSpan(); Span<Rgba32> previouslySeenPixels = previouslySeenPixelsBuffer.GetSpan();
Rgba32 previousPixel = new(0, 0, 0, 255); Rgba32 previousPixel = new(0, 0, 0, 255);
// We save the pixel to avoid loosing the fully opaque black pixel // We save the pixel to avoid losing the fully opaque black pixel
// See https://github.com/phoboslab/qoi/issues/258 // See https://github.com/phoboslab/qoi/issues/258
int pixelArrayPosition = GetArrayPosition(previousPixel); int pixelArrayPosition = GetArrayPosition(previousPixel);
previouslySeenPixels[pixelArrayPosition] = previousPixel; previouslySeenPixels[pixelArrayPosition] = previousPixel;
@ -174,7 +174,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
} }
readPixel.A = previousPixel.A; readPixel.A = previousPixel.A;
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel); pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel; previouslySeenPixels[pixelArrayPosition] = readPixel;
break; break;
@ -186,7 +186,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
ThrowInvalidImageContentException(); ThrowInvalidImageContentException();
} }
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel); pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel; previouslySeenPixels[pixelArrayPosition] = readPixel;
break; break;
@ -197,7 +197,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
// Getting one pixel from previously seen pixels // Getting one pixel from previously seen pixels
case QoiChunk.QoiOpIndex: case QoiChunk.QoiOpIndex:
readPixel = previouslySeenPixels[operationByte]; readPixel = previouslySeenPixels[operationByte];
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
break; break;
// Get one pixel from the difference (-2..1) of the previous pixel // Get one pixel from the difference (-2..1) of the previous pixel
@ -211,7 +211,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
G = (byte)Numerics.Modulo256(previousPixel.G + (greenDifference - 2)), G = (byte)Numerics.Modulo256(previousPixel.G + (greenDifference - 2)),
B = (byte)Numerics.Modulo256(previousPixel.B + (blueDifference - 2)) B = (byte)Numerics.Modulo256(previousPixel.B + (blueDifference - 2))
}; };
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel); pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel; previouslySeenPixels[pixelArrayPosition] = readPixel;
break; break;
@ -227,7 +227,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
int currentRed = Numerics.Modulo256(diffRedDG - 8 + (diffGreen - 32) + previousPixel.R); int currentRed = Numerics.Modulo256(diffRedDG - 8 + (diffGreen - 32) + previousPixel.R);
int currentBlue = Numerics.Modulo256(diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B); int currentBlue = Numerics.Modulo256(diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B);
readPixel = previousPixel with { R = (byte)currentRed, B = (byte)currentBlue, G = (byte)currentGreen }; readPixel = previousPixel with { R = (byte)currentRed, B = (byte)currentBlue, G = (byte)currentGreen };
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel); pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel; previouslySeenPixels[pixelArrayPosition] = readPixel;
break; break;
@ -241,7 +241,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
} }
readPixel = previousPixel; readPixel = previousPixel;
pixel.FromRgba32(readPixel); pixel = TPixel.FromRgba32(readPixel);
for (int k = -1; k < repetitions; k++, j++) for (int k = -1; k < repetitions; k++, j++)
{ {
if (j == row.Length) if (j == row.Length)

108
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -222,7 +222,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void ReadPaletted<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, Span<byte> palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) private void ReadPaletted<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, Span<byte> palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default;
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -237,14 +236,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
@ -255,14 +254,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
@ -273,14 +272,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, pixelRow);
} }
} }
@ -319,16 +318,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
switch (colorMapPixelSizeInBytes) switch (colorMapPixelSizeInBytes)
{ {
case 1: case 1:
color.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); color = TPixel.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break; break;
case 2: case 2:
this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); color = this.ReadPalettedBgra16Pixel<TPixel>(palette, bufferSpan[idx], colorMapPixelSizeInBytes);
break; break;
case 3: case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); color = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break; break;
case 4: case 4:
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); color = TPixel.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break; break;
} }
@ -350,17 +349,15 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void ReadMonoChrome<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin) private void ReadMonoChrome<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool invertX = InvertX(origin); if (InvertX(origin))
if (invertX)
{ {
TPixel color = default;
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = InvertY(y, height, origin); int newY = InvertY(y, height, origin);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
ReadL8Pixel(stream, color, x, pixelSpan); ReadL8Pixel(stream, x, pixelSpan);
} }
} }
@ -369,8 +366,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0); using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
bool invertY = InvertY(origin); if (InvertY(origin))
if (invertY)
{ {
for (int y = height - 1; y >= 0; y--) for (int y = height - 1; y >= 0; y--)
{ {
@ -398,7 +394,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void ReadBgra16<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin) private void ReadBgra16<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default;
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
@ -426,14 +421,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
{ {
color.FromLa16(Unsafe.As<byte, La16>(ref MemoryMarshal.GetReference(scratchBuffer))); pixelSpan[x] = TPixel.FromLa16(Unsafe.As<byte, La16>(ref MemoryMarshal.GetReference(scratchBuffer)));
} }
else else
{ {
color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref MemoryMarshal.GetReference(scratchBuffer))); pixelSpan[x] = TPixel.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref MemoryMarshal.GetReference(scratchBuffer)));
} }
pixelSpan[x] = color;
} }
} }
else else
@ -477,18 +470,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void ReadBgr24<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin) private void ReadBgr24<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool invertX = InvertX(origin); if (InvertX(origin))
if (invertX)
{ {
Span<byte> scratchBuffer = stackalloc byte[4]; Span<byte> scratchBuffer = stackalloc byte[4];
TPixel color = default;
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = InvertY(y, height, origin); int newY = InvertY(y, height, origin);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
ReadBgr24Pixel(stream, color, x, pixelSpan, scratchBuffer); ReadBgr24Pixel(stream, x, pixelSpan, scratchBuffer);
} }
} }
@ -497,9 +488,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0); using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
bool invertY = InvertY(origin);
if (invertY) if (InvertY(origin))
{ {
for (int y = height - 1; y >= 0; y--) for (int y = height - 1; y >= 0; y--)
{ {
@ -527,7 +517,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void ReadBgra32<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin) private void ReadBgra32<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default;
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
Guard.NotNull(this.tgaMetadata); Guard.NotNull(this.tgaMetadata);
@ -565,14 +554,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); this.ReadBgra32Pixel(stream, x, pixelRow, scratchBuffer);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); this.ReadBgra32Pixel(stream, x, pixelRow, scratchBuffer);
} }
} }
} }
@ -610,7 +599,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
switch (bytesPerPixel) switch (bytesPerPixel)
{ {
case 1: case 1:
color.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx])); color = TPixel.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx]));
break; break;
case 2: case 2:
if (!this.hasAlpha) if (!this.hasAlpha)
@ -621,26 +610,26 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
{ {
color.FromLa16(Unsafe.As<byte, La16>(ref bufferSpan[idx])); color = TPixel.FromLa16(Unsafe.As<byte, La16>(ref bufferSpan[idx]));
} }
else else
{ {
color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref bufferSpan[idx])); color = TPixel.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref bufferSpan[idx]));
} }
break; break;
case 3: case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx])); color = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
break; break;
case 4: case 4:
if (this.hasAlpha) if (this.hasAlpha)
{ {
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref bufferSpan[idx])); color = TPixel.FromBgra32(Unsafe.As<byte, Bgra32>(ref bufferSpan[idx]));
} }
else else
{ {
byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha)); color = TPixel.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha));
} }
break; break;
@ -677,16 +666,15 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReadL8Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan) private static void ReadL8Pixel<TPixel>(BufferedReadStream stream, int x, Span<TPixel> pixelSpan)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte pixelValue = (byte)stream.ReadByte(); byte pixelValue = (byte)stream.ReadByte();
color.FromL8(Unsafe.As<byte, L8>(ref pixelValue)); pixelSpan[x] = TPixel.FromL8(Unsafe.As<byte, L8>(ref pixelValue));
pixelSpan[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReadBgr24Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan, Span<byte> scratchBuffer) private static void ReadBgr24Pixel<TPixel>(BufferedReadStream stream, int x, Span<TPixel> pixelSpan, Span<byte> scratchBuffer)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = stream.Read(scratchBuffer, 0, 3); int bytesRead = stream.Read(scratchBuffer, 0, 3);
@ -695,8 +683,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel");
} }
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref MemoryMarshal.GetReference(scratchBuffer))); pixelSpan[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref MemoryMarshal.GetReference(scratchBuffer)));
pixelSpan[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -714,7 +701,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgra32Pixel<TPixel>(BufferedReadStream stream, int x, TPixel color, Span<TPixel> pixelRow, Span<byte> scratchBuffer) private void ReadBgra32Pixel<TPixel>(BufferedReadStream stream, int x, Span<TPixel> pixelRow, Span<byte> scratchBuffer)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = stream.Read(scratchBuffer, 0, 4); int bytesRead = stream.Read(scratchBuffer, 0, 4);
@ -726,8 +713,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Guard.NotNull(this.tgaMetadata); Guard.NotNull(this.tgaMetadata);
byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : scratchBuffer[3]; byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : scratchBuffer[3];
color.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha)); pixelRow[x] = TPixel.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha));
pixelRow[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -745,7 +731,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadPalettedBgra16Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span<TPixel> pixelRow) private void ReadPalettedBgra16Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = stream.ReadByte(); int colorIndex = stream.ReadByte();
@ -754,16 +740,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
} }
this.ReadPalettedBgra16Pixel(palette, colorIndex, colorMapPixelSizeInBytes, ref color); pixelRow[x] = this.ReadPalettedBgra16Pixel<TPixel>(palette, colorIndex, colorMapPixelSizeInBytes);
pixelRow[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadPalettedBgra16Pixel<TPixel>(Span<byte> palette, int index, int colorMapPixelSizeInBytes, ref TPixel color) private TPixel ReadPalettedBgra16Pixel<TPixel>(Span<byte> palette, int index, int colorMapPixelSizeInBytes)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Bgra5551 bgra = default; Bgra5551 bgra = Unsafe.As<byte, Bgra5551>(ref palette[index * colorMapPixelSizeInBytes]);
bgra.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref palette[index * colorMapPixelSizeInBytes]));
if (!this.hasAlpha) if (!this.hasAlpha)
{ {
@ -771,11 +755,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000);
} }
color.FromBgra5551(bgra); return TPixel.FromBgra5551(bgra);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReadPalettedBgr24Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span<TPixel> pixelRow) private static void ReadPalettedBgr24Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = stream.ReadByte(); int colorIndex = stream.ReadByte();
@ -784,12 +768,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
} }
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[colorIndex * colorMapPixelSizeInBytes])); pixelRow[x] = TPixel.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[colorIndex * colorMapPixelSizeInBytes]));
pixelRow[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReadPalettedBgra32Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span<TPixel> pixelRow) private static void ReadPalettedBgra32Pixel<TPixel>(BufferedReadStream stream, Span<byte> palette, int colorMapPixelSizeInBytes, int x, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = stream.ReadByte(); int colorIndex = stream.ReadByte();
@ -798,8 +781,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
} }
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[colorIndex * colorMapPixelSizeInBytes])); pixelRow[x] = TPixel.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[colorIndex * colorMapPixelSizeInBytes]));
pixelRow[x] = color;
} }
/// <summary> /// <summary>
@ -937,7 +919,9 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata();
this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth;
int alphaBits = this.fileHeader.ImageDescriptor & 0xf; // TrueColor images with 32 bits per pixel are assumed to always have 8 bit alpha channel,
// because some encoders do not set correctly the alpha bits in the image descriptor.
int alphaBits = this.IsTrueColor32BitPerPixel(this.tgaMetadata.BitsPerPixel) ? 8 : this.fileHeader.ImageDescriptor & 0xf;
if (alphaBits is not 0 and not 1 and not 8) if (alphaBits is not 0 and not 1 and not 8)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits"); TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits");
@ -949,4 +933,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
// Bits 4 and 5 describe the image origin. // Bits 4 and 5 describe the image origin.
return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
} }
private bool IsTrueColor32BitPerPixel(TgaBitsPerPixel bitsPerPixel) => bitsPerPixel == TgaBitsPerPixel.Pixel32 &&
(this.fileHeader.ImageType == TgaImageType.TrueColor ||
this.fileHeader.ImageType == TgaImageType.RleTrueColor);
} }

10
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -5,7 +5,6 @@ using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -160,7 +159,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
private void WriteRunLengthEncodedImage<TPixel>(Stream stream, ImageFrame<TPixel> image) private void WriteRunLengthEncodedImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Rgba32 color = default;
Buffer2D<TPixel> pixels = image.PixelBuffer; Buffer2D<TPixel> pixels = image.PixelBuffer;
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
@ -168,14 +166,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
for (int x = 0; x < image.Width;) for (int x = 0; x < image.Width;)
{ {
TPixel currentPixel = pixelRow[x]; TPixel currentPixel = pixelRow[x];
currentPixel.ToRgba32(ref color);
byte equalPixelCount = FindEqualPixels(pixelRow, x); byte equalPixelCount = FindEqualPixels(pixelRow, x);
if (equalPixelCount > 0) if (equalPixelCount > 0)
{ {
// Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run.
stream.WriteByte((byte)(equalPixelCount | 128)); stream.WriteByte((byte)(equalPixelCount | 128));
this.WritePixel(stream, currentPixel, color); this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
x += equalPixelCount + 1; x += equalPixelCount + 1;
} }
else else
@ -183,13 +180,12 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
// Write Raw Packet (i.e., Non-Run-Length Encoded): // Write Raw Packet (i.e., Non-Run-Length Encoded):
byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x); byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x);
stream.WriteByte(unEqualPixelCount); stream.WriteByte(unEqualPixelCount);
this.WritePixel(stream, currentPixel, color); this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
x++; x++;
for (int i = 0; i < unEqualPixelCount; i++) for (int i = 0; i < unEqualPixelCount; i++)
{ {
currentPixel = pixelRow[x]; currentPixel = pixelRow[x];
currentPixel.ToRgba32(ref color); this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
this.WritePixel(stream, currentPixel, color);
x++; x++;
} }
} }

5
src/ImageSharp/Formats/Tga/TgaFileHeader.cs

@ -131,10 +131,7 @@ internal readonly struct TgaFileHeader
/// </summary> /// </summary>
public byte ImageDescriptor { get; } public byte ImageDescriptor { get; }
public static TgaFileHeader Parse(Span<byte> data) public static TgaFileHeader Parse(Span<byte> data) => MemoryMarshal.Cast<byte, TgaFileHeader>(data)[0];
{
return MemoryMarshal.Cast<byte, TgaFileHeader>(data)[0];
}
public void WriteTo(Span<byte> buffer) public void WriteTo(Span<byte> buffer)
{ {

140
src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs

@ -102,8 +102,7 @@ internal static class HorizontalPredictor
byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R); byte r = (byte)(rowRgb[x].R - rowRgb[x - 1].R);
byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G); byte g = (byte)(rowRgb[x].G - rowRgb[x - 1].G);
byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B); byte b = (byte)(rowRgb[x].B - rowRgb[x - 1].B);
var rgb = new Rgb24(r, g, b); rowRgb[x] = new Rgb24(r, g, b);
rowRgb[x].FromRgb24(rgb);
} }
} }
} }
@ -128,8 +127,7 @@ internal static class HorizontalPredictor
for (int x = rowL16.Length - 1; x >= 1; x--) for (int x = rowL16.Length - 1; x >= 1; x--)
{ {
ushort val = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue); rowL16[x].PackedValue = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue);
rowL16[x].PackedValue = val;
} }
} }
} }
@ -181,13 +179,13 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort pixelValue = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort pixelValue = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort diff = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort diff = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
pixelValue += diff; pixelValue += diff;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, pixelValue);
offset += 2; offset += 2;
@ -200,13 +198,13 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort pixelValue = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort pixelValue = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort diff = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort diff = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
pixelValue += diff; pixelValue += diff;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, pixelValue);
offset += 2; offset += 2;
@ -225,13 +223,13 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint pixelValue = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint pixelValue = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint diff = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint diff = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
pixelValue += diff; pixelValue += diff;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, pixelValue); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, pixelValue);
offset += 4; offset += 4;
@ -244,13 +242,13 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint pixelValue = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint pixelValue = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint diff = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint diff = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
pixelValue += diff; pixelValue += diff;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, pixelValue); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, pixelValue);
offset += 4; offset += 4;
@ -278,8 +276,7 @@ internal static class HorizontalPredictor
r += pixel.R; r += pixel.R;
g += pixel.G; g += pixel.G;
b += pixel.B; b += pixel.B;
var rgb = new Rgb24(r, g, b); pixel = new Rgb24(r, g, b);
pixel.FromRgb24(rgb);
} }
} }
} }
@ -305,8 +302,7 @@ internal static class HorizontalPredictor
g += pixel.G; g += pixel.G;
b += pixel.B; b += pixel.B;
a += pixel.A; a += pixel.A;
var rgb = new Rgba32(r, g, b, a); pixel = new Rgba32(r, g, b, a);
pixel.FromRgba32(rgb);
} }
} }
} }
@ -321,29 +317,29 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaR = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaG = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaB = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b);
offset += 2; offset += 2;
@ -356,29 +352,29 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaR = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaG = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaB = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b);
offset += 2; offset += 2;
@ -397,37 +393,37 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort a = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2)); ushort a = TiffUtilities.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaR = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaG = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaB = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaA = TiffUtils.ConvertToUShortBigEndian(rowSpan); ushort deltaA = TiffUtilities.ConvertToUShortBigEndian(rowSpan);
a += deltaA; a += deltaA;
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, a); BinaryPrimitives.WriteUInt16BigEndian(rowSpan, a);
offset += 2; offset += 2;
@ -440,37 +436,37 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
ushort a = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2)); ushort a = TiffUtilities.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
offset += 2; offset += 2;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 2); Span<byte> rowSpan = rowBytes.Slice(offset, 2);
ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaR = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaG = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaB = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b);
offset += 2; offset += 2;
rowSpan = rowBytes.Slice(offset, 2); rowSpan = rowBytes.Slice(offset, 2);
ushort deltaA = TiffUtils.ConvertToUShortLittleEndian(rowSpan); ushort deltaA = TiffUtilities.ConvertToUShortLittleEndian(rowSpan);
a += deltaA; a += deltaA;
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, a); BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, a);
offset += 2; offset += 2;
@ -489,29 +485,29 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b);
offset += 4; offset += 4;
@ -524,29 +520,29 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b);
offset += 4; offset += 4;
@ -565,37 +561,37 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint a = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaR = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaG = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaB = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaA = TiffUtils.ConvertToUIntBigEndian(rowSpan); uint deltaA = TiffUtilities.ConvertToUIntBigEndian(rowSpan);
a += deltaA; a += deltaA;
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a); BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a);
offset += 4; offset += 4;
@ -608,37 +604,37 @@ internal static class HorizontalPredictor
{ {
int offset = 0; int offset = 0;
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount); Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
uint a = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
offset += 4; offset += 4;
for (int x = 1; x < width; x++) for (int x = 1; x < width; x++)
{ {
Span<byte> rowSpan = rowBytes.Slice(offset, 4); Span<byte> rowSpan = rowBytes.Slice(offset, 4);
uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaR = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
r += deltaR; r += deltaR;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaG = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
g += deltaG; g += deltaG;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaB = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
b += deltaB; b += deltaB;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b);
offset += 4; offset += 4;
rowSpan = rowBytes.Slice(offset, 4); rowSpan = rowBytes.Slice(offset, 4);
uint deltaA = TiffUtils.ConvertToUIntLittleEndian(rowSpan); uint deltaA = TiffUtilities.ConvertToUIntLittleEndian(rowSpan);
a += deltaA; a += deltaA;
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a); BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a);
offset += 4; offset += 4;

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

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation for 16-bit grayscale images. /// Implements the 'BlackIsZero' photometric interpretation for 16-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -32,9 +33,8 @@ internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
L16 l16 = TiffUtils.L16Default; L16 l16 = TiffUtilities.L16Default;
var color = default(TPixel); TPixel color = TPixel.FromScaledVector4(Vector4.Zero);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
@ -44,10 +44,10 @@ internal class BlackIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ushort intensity = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort intensity = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); pixelRow[x] = TPixel.FromL16(new(intensity));
} }
} }
else else

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

@ -19,11 +19,9 @@ internal class BlackIsZero1TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
nuint offset = 0; nuint offset = 0;
TPixel colorBlack = default; TPixel colorBlack = TPixel.FromRgba32(Color.Black.ToPixel<Rgba32>());
TPixel colorWhite = default; TPixel colorWhite = TPixel.FromRgba32(Color.White.ToPixel<Rgba32>());
colorBlack.FromRgba32(Color.Black);
colorWhite.FromRgba32(Color.White);
ref byte dataRef = ref MemoryMarshal.GetReference(data); ref byte dataRef = ref MemoryMarshal.GetReference(data);
for (nuint y = (uint)top; y < (uint)(top + height); y++) for (nuint y = (uint)top; y < (uint)(top + height); y++)
{ {

12
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation for 24-bit grayscale images. /// Implements the 'BlackIsZero' photometric interpretation for 24-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,8 +25,6 @@ internal class BlackIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0; int bufferStartIdx = this.isBigEndian ? 1 : 0;
@ -40,10 +38,10 @@ internal class BlackIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong intensity = TiffUtils.ConvertToUIntBigEndian(buffer); uint intensity = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(intensity);
} }
} }
else else
@ -51,10 +49,10 @@ internal class BlackIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong intensity = TiffUtils.ConvertToUIntLittleEndian(buffer); uint intensity = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(intensity);
} }
} }
} }

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

@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation for 32-bit float grayscale images. /// Implements the 'BlackIsZero' photometric interpretation for 32-bit float grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -24,8 +25,6 @@ internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
TPixel color = default;
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int offset = 0; int offset = 0;
@ -41,9 +40,7 @@ internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = BitConverter.ToSingle(buffer); float intensity = BitConverter.ToSingle(buffer);
offset += 4; offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
else else
@ -53,9 +50,7 @@ internal class BlackIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = BitConverter.ToSingle(data.Slice(offset, 4)); float intensity = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4; offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
} }

13
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation for 32-bit grayscale images. /// Implements the 'BlackIsZero' photometric interpretation for 32-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,9 +25,6 @@ internal class BlackIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -36,20 +33,20 @@ internal class BlackIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong intensity = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint intensity = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(intensity);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong intensity = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint intensity = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(intensity);
} }
} }
} }

25
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs

@ -9,47 +9,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation (optimized for 4-bit grayscale images). /// Implements the 'BlackIsZero' photometric interpretation (optimized for 4-bit grayscale images).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZero4TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZero4TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
int offset = 0; int offset = 0;
bool isOddWidth = (width & 1) == 1; bool isOddWidth = (width & 1) == 1;
var l8 = default(L8);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
for (int x = left; x < left + width - 1;) for (int x = left; x < left + width - 1;)
{ {
byte byteData = data[offset++]; byte byteData = data[offset++];
pixelRowSpan[x++] = TPixel.FromL8(new((byte)(((byteData & 0xF0) >> 4) * 17)));
byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17); pixelRowSpan[x++] = TPixel.FromL8(new((byte)((byteData & 0x0F) * 17)));
l8.PackedValue = intensity1;
color.FromL8(l8);
pixelRowSpan[x++] = color;
byte intensity2 = (byte)((byteData & 0x0F) * 17);
l8.PackedValue = intensity2;
color.FromL8(l8);
pixelRowSpan[x++] = color;
} }
if (isOddWidth) if (isOddWidth)
{ {
byte byteData = data[offset++]; byte byteData = data[offset++];
pixelRowSpan[left + width - 1] = TPixel.FromL8(new((byte)(((byteData & 0xF0) >> 4) * 17)));
byte intensity1 = (byte)(((byteData & 0xF0) >> 4) * 17);
l8.PackedValue = intensity1;
color.FromL8(l8);
pixelRowSpan[left + width - 1] = color;
} }
} }
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,25 +10,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'BlackIsZero' photometric interpretation (for all bit depths). /// Implements the 'BlackIsZero' photometric interpretation (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class BlackIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class BlackIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly ushort bitsPerSample0; private readonly ushort bitsPerSample0;
private readonly float factor; private readonly float factor;
public BlackIsZeroTiffColor(TiffBitsPerSample bitsPerSample) public BlackIsZeroTiffColor(TiffBitsPerSample bitsPerSample)
{ {
this.bitsPerSample0 = bitsPerSample.Channel0; this.bitsPerSample0 = bitsPerSample.Channel0;
this.factor = (1 << this.bitsPerSample0) - 1.0f; this.factor = (1 << this.bitsPerSample0) - 1f;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel); BitReader bitReader = new(data);
var bitReader = new BitReader(data);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -38,9 +35,7 @@ internal class BlackIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
int value = bitReader.ReadBits(this.bitsPerSample0); int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = value / this.factor; float intensity = value / this.factor;
pixelRow[x] = TPixel.FromScaledVector4(new(intensity, intensity, intensity, 1f));
color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixelRow[x] = color;
} }
bitReader.NextRow(); bitReader.NextRow();

15
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -13,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'CieLab' with the planar configuration. /// Implements decoding pixel data with photometric interpretation of type 'CieLab' with the planar configuration.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class CieLabPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class CieLabPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -23,22 +23,19 @@ internal class CieLabPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
Span<byte> l = data[0].GetSpan();
Span<byte> a = data[1].GetSpan();
Span<byte> b = data[2].GetSpan(); Span<byte> b = data[2].GetSpan();
Span<byte> a = data[1].GetSpan();
Span<byte> l = data[0].GetSpan();
var color = default(TPixel);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
var lab = new CieLab((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]); CieLab lab = new((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]);
var rgb = ColorSpaceConverter.ToRgb(lab); Rgb rgb = ColorSpaceConverter.ToRgb(lab);
pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f));
color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = color;
offset++; offset++;
} }

10
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -12,17 +11,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'CieLab'. /// Implements decoding pixel data with photometric interpretation of type 'CieLab'.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class CieLabTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class CieLabTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private static readonly ColorSpaceConverter ColorSpaceConverter = new(); private static readonly ColorSpaceConverter ColorSpaceConverter = new();
private const float Inv255 = 1f / 255f;
private const float Inv255 = 1.0f / 255.0f;
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
TPixel color = default;
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -33,9 +31,7 @@ internal class CieLabTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float l = (data[offset] & 0xFF) * 100f * Inv255; float l = (data[offset] & 0xFF) * 100f * Inv255;
CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]); CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]);
Rgb rgb = ColorSpaceConverter.ToRgb(lab); Rgb rgb = ColorSpaceConverter.ToRgb(lab);
pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1f));
color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = color;
offset += 3; offset += 3;
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -12,12 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
internal class CmykTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class CmykTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private const float Inv255 = 1 / 255.0f; private const float Inv255 = 1f / 255f;
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
TPixel color = default;
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -26,9 +24,7 @@ internal class CmykTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255); Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255);
Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk); Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk);
pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f));
color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = color;
offset += 4; offset += 4;
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths). /// Implements the 'PaletteTiffColor' photometric interpretation (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -18,8 +18,11 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
private readonly TPixel[] palette; private readonly TPixel[] palette;
private const float InvMax = 1.0f / 65535F; private const float InvMax = 1f / 65535f;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteTiffColor{TPixel}"/> class.
/// </summary>
/// <param name="bitsPerSample">The number of bits per sample for each pixel.</param> /// <param name="bitsPerSample">The number of bits per sample for each pixel.</param>
/// <param name="colorMap">The RGB color lookup table to use for decoding the image.</param> /// <param name="colorMap">The RGB color lookup table to use for decoding the image.</param>
public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap)
@ -32,7 +35,7 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var bitReader = new BitReader(data); BitReader bitReader = new(data);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -49,7 +52,7 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount)
{ {
var palette = new TPixel[colorCount]; TPixel[] palette = new TPixel[colorCount];
const int rOffset = 0; const int rOffset = 0;
int gOffset = colorCount; int gOffset = colorCount;
@ -60,7 +63,7 @@ internal class PaletteTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float r = colorMap[rOffset + i] * InvMax; float r = colorMap[rOffset + i] * InvMax;
float g = colorMap[gOffset + i] * InvMax; float g = colorMap[gOffset + i] * InvMax;
float b = colorMap[bOffset + i] * InvMax; float b = colorMap[bOffset + i] * InvMax;
palette[i].FromScaledVector4(new Vector4(r, g, b, 1.0f)); palette[i] = TPixel.FromScaledVector4(new(r, g, b, 1f));
} }
return palette; return palette;

15
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,11 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 16 bits for each channel. /// Implements the 'RGB' photometric interpretation with 16 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgb161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly bool isBigEndian; private readonly bool isBigEndian;
private readonly Configuration configuration; private readonly Configuration configuration;
/// <summary> /// <summary>
@ -32,10 +31,6 @@ internal class Rgb161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
@ -46,14 +41,14 @@ internal class Rgb161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); pixelRow[x] = TPixel.FromRgb48(new(r, g, b));
} }
} }
else else

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

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 16 bit. /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 16 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgb16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -26,10 +26,6 @@ internal class Rgb16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> redData = data[0].GetSpan(); Span<byte> redData = data[0].GetSpan();
Span<byte> greenData = data[1].GetSpan(); Span<byte> greenData = data[1].GetSpan();
Span<byte> blueData = data[2].GetSpan(); Span<byte> blueData = data[2].GetSpan();
@ -42,26 +38,26 @@ internal class Rgb16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(blueData.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); pixelRow[x] = TPixel.FromRgb48(new(r, g, b));
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color); pixelRow[x] = TPixel.FromRgb48(new(r, g, b));
} }
} }
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 24 bits for each channel. /// Implements the 'RGB' photometric interpretation with 24 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgb242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,8 +25,6 @@ internal class Rgb242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0; int bufferStartIdx = this.isBigEndian ? 1 : 0;
@ -41,18 +39,18 @@ internal class Rgb242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); uint r = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); uint g = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); uint b = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b);
} }
} }
else else
@ -60,18 +58,18 @@ internal class Rgb242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b);
} }
} }
} }

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

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 24 bit. /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 24 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgb24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -26,8 +26,6 @@ internal class Rgb24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0; int bufferStartIdx = this.isBigEndian ? 1 : 0;
@ -45,15 +43,15 @@ internal class Rgb24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
redData.Slice(offset, 3).CopyTo(bufferSpan); redData.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); uint r = TiffUtilities.ConvertToUIntBigEndian(buffer);
greenData.Slice(offset, 3).CopyTo(bufferSpan); greenData.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); uint g = TiffUtilities.ConvertToUIntBigEndian(buffer);
blueData.Slice(offset, 3).CopyTo(bufferSpan); blueData.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); uint b = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b);
} }
} }
else else
@ -61,15 +59,15 @@ internal class Rgb24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
redData.Slice(offset, 3).CopyTo(bufferSpan); redData.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer);
greenData.Slice(offset, 3).CopyTo(bufferSpan); greenData.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer);
blueData.Slice(offset, 3).CopyTo(bufferSpan); blueData.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b);
} }
} }
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 32 bits for each channel. /// Implements the 'RGB' photometric interpretation with 32 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgb323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,8 +25,6 @@ internal class Rgb323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
@ -37,32 +35,32 @@ internal class Rgb323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b);
} }
} }
} }

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

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 32 bit. /// Implements the 'RGB' photometric interpretation with 'Planar' layout for each color channel with 32 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgb32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -26,9 +26,6 @@ internal class Rgb32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> redData = data[0].GetSpan(); Span<byte> redData = data[0].GetSpan();
Span<byte> greenData = data[1].GetSpan(); Span<byte> greenData = data[1].GetSpan();
Span<byte> blueData = data[2].GetSpan(); Span<byte> blueData = data[2].GetSpan();
@ -41,26 +38,26 @@ internal class Rgb32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntBigEndian(redData.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(redData.Slice(offset, 4));
ulong g = TiffUtils.ConvertToUIntBigEndian(greenData.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(greenData.Slice(offset, 4));
ulong b = TiffUtils.ConvertToUIntBigEndian(blueData.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(blueData.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(redData.Slice(offset, 4));
ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(greenData.Slice(offset, 4));
ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(blueData.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b);
} }
} }
} }

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

@ -9,17 +9,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation for 4 bits per color channel images. /// Implements the 'RGB' photometric interpretation for 4 bits per color channel images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
int offset = 0; int offset = 0;
var bgra = default(Bgra4444);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y);
@ -31,9 +29,8 @@ internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
offset++; offset++;
byte b = (byte)((data[offset] & 0xF0) >> 4); byte b = (byte)((data[offset] & 0xF0) >> 4);
bgra.PackedValue = ToBgraPackedValue(b, g, r); Bgra4444 bgra = new() { PackedValue = ToBgraPackedValue(b, g, r) };
color.FromScaledVector4(bgra.ToScaledVector4()); pixelRow[x] = TPixel.FromScaledVector4(bgra.ToScaledVector4());
pixelRow[x] = color;
if (x + 1 >= pixelRow.Length) if (x + 1 >= pixelRow.Length)
{ {
offset++; offset++;
@ -47,8 +44,7 @@ internal class Rgb444TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
offset++; offset++;
bgra.PackedValue = ToBgraPackedValue(b, g, r); bgra.PackedValue = ToBgraPackedValue(b, g, r);
color.FromScaledVector4(bgra.ToScaledVector4()); pixelRow[x + 1] = TPixel.FromScaledVector4(bgra.ToScaledVector4());
pixelRow[x + 1] = color;
} }
} }
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -10,6 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 32 bits for each channel. /// Implements the 'RGB' photometric interpretation with 32 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -24,8 +24,6 @@ internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
@ -52,9 +50,7 @@ internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float b = BitConverter.ToSingle(buffer); float b = BitConverter.ToSingle(buffer);
offset += 4; offset += 4;
var colorVector = new Vector4(r, g, b, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
else else
@ -70,9 +66,7 @@ internal class RgbFloat323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float b = BitConverter.ToSingle(data.Slice(offset, 4)); float b = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4; offset += 4;
var colorVector = new Vector4(r, g, b, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
} }

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

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths). /// Implements the 'RGB' photometric interpretation with 'Planar' layout (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -49,11 +49,9 @@ internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
/// <param name="height">The height of the image block.</param> /// <param name="height">The height of the image block.</param>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel); BitReader rBitReader = new(data[0].GetSpan());
BitReader gBitReader = new(data[1].GetSpan());
var rBitReader = new BitReader(data[0].GetSpan()); BitReader bBitReader = new(data[2].GetSpan());
var gBitReader = new BitReader(data[1].GetSpan());
var bBitReader = new BitReader(data[2].GetSpan());
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -64,8 +62,7 @@ internal class RgbPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
color.FromScaledVector4(new Vector4(r, g, b, 1.0f)); pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, 1f));
pixelRow[x] = color;
} }
rBitReader.NextRow(); rBitReader.NextRow();

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

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation (for all bit depths). /// Implements the 'RGB' photometric interpretation (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class RgbTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -40,9 +41,7 @@ internal class RgbTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel); BitReader bitReader = new(data);
var bitReader = new BitReader(data);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -53,8 +52,7 @@ internal class RgbTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
color.FromScaledVector4(new Vector4(r, g, b, 1.0f)); pixelRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, 1f));
pixelRow[x] = color;
} }
bitReader.NextRow(); bitReader.NextRow();

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

@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -29,8 +30,8 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="memoryAllocator">The memory allocator.</param> /// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
/// <param name="extraSamplesType">The type of the extra samples.</param> /// <param name="extraSamplesType">The type of the extra samples.</param>
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian) public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{ {
this.configuration = configuration; this.configuration = configuration;
@ -42,15 +43,11 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0; int offset = 0;
using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null; using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null;
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span<Vector4>.Empty; Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : [];
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
@ -59,18 +56,18 @@ internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); ushort a = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : ? TiffUtilities.ColorFromRgba64Premultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); : TPixel.FromRgba64(new(r, g, b, a));
} }
} }
else else

34
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -33,10 +33,6 @@ internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> redData = data[0].GetSpan(); Span<byte> redData = data[0].GetSpan();
Span<byte> greenData = data[1].GetSpan(); Span<byte> greenData = data[1].GetSpan();
Span<byte> blueData = data[2].GetSpan(); Span<byte> blueData = data[2].GetSpan();
@ -51,32 +47,32 @@ internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortBigEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortBigEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortBigEndian(blueData.Slice(offset, 2));
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2)); ushort a = TiffUtilities.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : ? TiffUtilities.ColorFromRgba64Premultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); : TPixel.FromRgba64(new(r, g, b, a));
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); ushort r = TiffUtilities.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); ushort g = TiffUtilities.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); ushort b = TiffUtilities.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
ulong a = TiffUtils.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2)); ushort a = TiffUtilities.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2));
offset += 2; offset += 2;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : ? TiffUtilities.ColorFromRgba64Premultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); : TPixel.FromRgba64(new(r, g, b, a));
} }
} }
} }

33
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -32,9 +32,6 @@ internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0; int offset = 0;
@ -51,24 +48,24 @@ internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); uint r = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); uint g = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); uint b = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer); uint a = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo24BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b, a);
} }
} }
else else
@ -76,24 +73,24 @@ internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer); uint a = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo24BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b, a);
} }
} }
} }

32
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -33,8 +33,6 @@ internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0; int bufferStartIdx = this.isBigEndian ? 1 : 0;
@ -54,19 +52,19 @@ internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
redData.Slice(offset, 3).CopyTo(bufferSpan); redData.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); uint r = TiffUtilities.ConvertToUIntBigEndian(buffer);
greenData.Slice(offset, 3).CopyTo(bufferSpan); greenData.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); uint g = TiffUtilities.ConvertToUIntBigEndian(buffer);
blueData.Slice(offset, 3).CopyTo(bufferSpan); blueData.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); uint b = TiffUtilities.ConvertToUIntBigEndian(buffer);
alphaData.Slice(offset, 3).CopyTo(bufferSpan); alphaData.Slice(offset, 3).CopyTo(bufferSpan);
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer); uint a = TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo24BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b, a);
} }
} }
else else
@ -74,19 +72,19 @@ internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
redData.Slice(offset, 3).CopyTo(bufferSpan); redData.Slice(offset, 3).CopyTo(bufferSpan);
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); uint r = TiffUtilities.ConvertToUIntLittleEndian(buffer);
greenData.Slice(offset, 3).CopyTo(bufferSpan); greenData.Slice(offset, 3).CopyTo(bufferSpan);
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); uint g = TiffUtilities.ConvertToUIntLittleEndian(buffer);
blueData.Slice(offset, 3).CopyTo(bufferSpan); blueData.Slice(offset, 3).CopyTo(bufferSpan);
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); uint b = TiffUtilities.ConvertToUIntLittleEndian(buffer);
alphaData.Slice(offset, 3).CopyTo(bufferSpan); alphaData.Slice(offset, 3).CopyTo(bufferSpan);
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer); uint a = TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo24BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo24Bit<TPixel>(r, g, b, a);
} }
} }
} }

33
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -32,9 +32,6 @@ internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0; int offset = 0;
@ -46,42 +43,42 @@ internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo32BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b, a);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo32BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b, a);
} }
} }
} }

34
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and a 'Planar' layout for each color channel with 32 bit. /// Implements the 'RGB' photometric interpretation with an alpha channel and a 'Planar' layout for each color channel with 32 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class Rgba32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly bool isBigEndian; private readonly bool isBigEndian;
private readonly TiffExtraSampleType? extraSamplesType; private readonly TiffExtraSampleType? extraSamplesType;
/// <summary> /// <summary>
@ -33,9 +32,6 @@ internal class Rgba32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> redData = data[0].GetSpan(); Span<byte> redData = data[0].GetSpan();
Span<byte> greenData = data[1].GetSpan(); Span<byte> greenData = data[1].GetSpan();
Span<byte> blueData = data[2].GetSpan(); Span<byte> blueData = data[2].GetSpan();
@ -50,32 +46,32 @@ internal class Rgba32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntBigEndian(redData.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntBigEndian(redData.Slice(offset, 4));
ulong g = TiffUtils.ConvertToUIntBigEndian(greenData.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntBigEndian(greenData.Slice(offset, 4));
ulong b = TiffUtils.ConvertToUIntBigEndian(blueData.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntBigEndian(blueData.Slice(offset, 4));
ulong a = TiffUtils.ConvertToUIntBigEndian(alphaData.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntBigEndian(alphaData.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo32BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b, a);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); uint r = TiffUtilities.ConvertToUIntLittleEndian(redData.Slice(offset, 4));
ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); uint g = TiffUtilities.ConvertToUIntLittleEndian(greenData.Slice(offset, 4));
ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); uint b = TiffUtilities.ConvertToUIntLittleEndian(blueData.Slice(offset, 4));
ulong a = TiffUtils.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4)); uint a = TiffUtilities.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = hasAssociatedAlpha ? pixelRow[x] = hasAssociatedAlpha
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : ? TiffUtilities.ColorScaleTo32BitPremultiplied<TPixel>(r, g, b, a)
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); : TiffUtilities.ColorScaleTo32Bit<TPixel>(r, g, b, a);
} }
} }
} }

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

@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and 8 bits per channel. /// Implements the 'RGB' photometric interpretation with an alpha channel and 8 bits per channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -34,10 +35,8 @@ internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
int offset = 0; int offset = 0;
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null; using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null;
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span<Vector4>.Empty; Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : [];
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -10,6 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel. /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -24,8 +24,6 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
@ -57,9 +55,7 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float a = BitConverter.ToSingle(buffer); float a = BitConverter.ToSingle(buffer);
offset += 4; offset += 4;
var colorVector = new Vector4(r, g, b, a); pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, a));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
else else
@ -78,9 +74,7 @@ internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float a = BitConverter.ToSingle(data.Slice(offset, 4)); float a = BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4; offset += 4;
var colorVector = new Vector4(r, g, b, a); pixelRow[x] = TPixel.FromScaledVector4(new(r, g, b, a));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
} }

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

@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout (for all bit depths). /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -59,13 +60,12 @@ internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
/// <param name="height">The height of the image block.</param> /// <param name="height">The height of the image block.</param>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
bool hasAssociatedAlpha = this.extraSampleType.HasValue && this.extraSampleType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSampleType.HasValue && this.extraSampleType == TiffExtraSampleType.AssociatedAlphaData;
var rBitReader = new BitReader(data[0].GetSpan()); BitReader rBitReader = new(data[0].GetSpan());
var gBitReader = new BitReader(data[1].GetSpan()); BitReader gBitReader = new(data[1].GetSpan());
var bBitReader = new BitReader(data[2].GetSpan()); BitReader bBitReader = new(data[2].GetSpan());
var aBitReader = new BitReader(data[3].GetSpan()); BitReader aBitReader = new(data[3].GetSpan());
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -77,17 +77,15 @@ internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor; float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor;
var vec = new Vector4(r, g, b, a); Vector4 vector = new(r, g, b, a);
if (hasAssociatedAlpha) if (hasAssociatedAlpha)
{ {
color = TiffUtils.UnPremultiply(ref vec, color); pixelRow[x] = TiffUtilities.UnPremultiply<TPixel>(ref vector);
} }
else else
{ {
color.FromScaledVector4(vec); pixelRow[x] = TPixel.FromScaledVector4(vector);
} }
pixelRow[x] = color;
} }
rBitReader.NextRow(); rBitReader.NextRow();

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

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'RGB' photometric interpretation with alpha channel (for all bit depths). /// Implements the 'RGB' photometric interpretation with alpha channel (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -50,9 +51,7 @@ internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel); BitReader bitReader = new(data);
var bitReader = new BitReader(data);
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
@ -66,15 +65,14 @@ internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor; float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor;
var vec = new Vector4(r, g, b, a); Vector4 vector = new(r, g, b, a);
if (hasAssociatedAlpha) if (hasAssociatedAlpha)
{ {
pixelRow[x] = TiffUtils.UnPremultiply(ref vec, color); pixelRow[x] = TiffUtilities.UnPremultiply<TPixel>(ref vector);
} }
else else
{ {
color.FromScaledVector4(vec); pixelRow[x] = TPixel.FromScaledVector4(vector);
pixelRow[x] = color;
} }
} }

14
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 16-bit grayscale images. /// Implements the 'WhiteIsZero' photometric interpretation for 16-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,10 +25,6 @@ internal class WhiteIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
L16 l16 = TiffUtils.L16Default;
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
int offset = 0; int offset = 0;
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -37,20 +33,20 @@ internal class WhiteIsZero16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2))); ushort intensity = (ushort)(ushort.MaxValue - TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); pixelRow[x] = TPixel.FromL16(new(intensity));
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2))); ushort intensity = (ushort)(ushort.MaxValue - TiffUtilities.ConvertToUShortLittleEndian(data.Slice(offset, 2)));
offset += 2; offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color); pixelRow[x] = TPixel.FromL16(new(intensity));
} }
} }
} }

7
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation (optimized for bilevel images). /// Implements the 'WhiteIsZero' photometric interpretation (optimized for bilevel images).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero1TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero1TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -18,11 +19,9 @@ internal class WhiteIsZero1TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
nuint offset = 0; nuint offset = 0;
var colorBlack = default(TPixel); TPixel colorBlack = TPixel.FromRgba32(Color.Black.ToPixel<Rgba32>());
var colorWhite = default(TPixel); TPixel colorWhite = TPixel.FromRgba32(Color.White.ToPixel<Rgba32>());
colorBlack.FromRgba32(Color.Black);
colorWhite.FromRgba32(Color.White);
ref byte dataRef = ref MemoryMarshal.GetReference(data); ref byte dataRef = ref MemoryMarshal.GetReference(data);
for (nuint y = (uint)top; y < (uint)(top + height); y++) for (nuint y = (uint)top; y < (uint)(top + height); y++)
{ {

12
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 24-bit grayscale images. /// Implements the 'WhiteIsZero' photometric interpretation for 24-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,8 +25,6 @@ internal class WhiteIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0; int bufferStartIdx = this.isBigEndian ? 1 : 0;
const uint maxValue = 0xFFFFFF; const uint maxValue = 0xFFFFFF;
@ -41,10 +39,10 @@ internal class WhiteIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(buffer); uint intensity = maxValue - TiffUtilities.ConvertToUIntBigEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(intensity);
} }
} }
else else
@ -52,10 +50,10 @@ internal class WhiteIsZero24TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
data.Slice(offset, 3).CopyTo(bufferSpan); data.Slice(offset, 3).CopyTo(bufferSpan);
ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(buffer); uint intensity = maxValue - TiffUtilities.ConvertToUIntLittleEndian(buffer);
offset += 3; offset += 3;
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo24Bit<TPixel>(intensity);
} }
} }
} }

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

@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images. /// Implements the 'WhiteIsZero' photometric interpretation for 32-bit float grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -24,8 +25,6 @@ internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
int offset = 0; int offset = 0;
@ -41,9 +40,7 @@ internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = 1.0f - BitConverter.ToSingle(buffer); float intensity = 1.0f - BitConverter.ToSingle(buffer);
offset += 4; offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new(intensity, intensity, intensity, 1f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
else else
@ -53,9 +50,7 @@ internal class WhiteIsZero32FloatTiffColor<TPixel> : TiffBaseColorDecoder<TPixel
float intensity = 1.0f - BitConverter.ToSingle(data.Slice(offset, 4)); float intensity = 1.0f - BitConverter.ToSingle(data.Slice(offset, 4));
offset += 4; offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f); pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
color.FromScaledVector4(colorVector);
pixelRow[x] = color;
} }
} }
} }

12
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation for 32-bit grayscale images. /// Implements the 'WhiteIsZero' photometric interpretation for 32-bit grayscale images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -25,8 +25,6 @@ internal class WhiteIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
color.FromScaledVector4(Vector4.Zero);
const uint maxValue = 0xFFFFFFFF; const uint maxValue = 0xFFFFFFFF;
int offset = 0; int offset = 0;
@ -37,20 +35,20 @@ internal class WhiteIsZero32TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong intensity = maxValue - TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); uint intensity = maxValue - TiffUtilities.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(intensity);
} }
} }
else else
{ {
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
ulong intensity = maxValue - TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); uint intensity = maxValue - TiffUtilities.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4; offset += 4;
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(intensity, color); pixelRow[x] = TiffUtilities.ColorScaleTo32Bit<TPixel>(intensity);
} }
} }
} }

25
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs

@ -9,47 +9,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation (optimized for 4-bit grayscale images). /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 4-bit grayscale images).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero4TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero4TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
int offset = 0; int offset = 0;
bool isOddWidth = (width & 1) == 1; bool isOddWidth = (width & 1) == 1;
var l8 = default(L8);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
for (int x = left; x < left + width - 1;) for (int x = left; x < left + width - 1;)
{ {
byte byteData = data[offset++]; byte byteData = data[offset++];
pixelRowSpan[x++] = TPixel.FromL8(new((byte)((15 - ((byteData & 0xF0) >> 4)) * 17)));
byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); pixelRowSpan[x++] = TPixel.FromL8(new((byte)((15 - (byteData & 0x0F)) * 17)));
l8.PackedValue = intensity1;
color.FromL8(l8);
pixelRowSpan[x++] = color;
byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17);
l8.PackedValue = intensity2;
color.FromL8(l8);
pixelRowSpan[x++] = color;
} }
if (isOddWidth) if (isOddWidth)
{ {
byte byteData = data[offset++]; byte byteData = data[offset++];
pixelRowSpan[left + width - 1] = TPixel.FromL8(new((byte)((15 - ((byteData & 0xF0) >> 4)) * 17)));
byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17);
l8.PackedValue = intensity1;
color.FromL8(l8);
pixelRowSpan[left + width - 1] = color;
} }
} }
} }

8
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero8TiffColor{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -10,24 +9,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation (optimized for 8-bit grayscale images). /// Implements the 'WhiteIsZero' photometric interpretation (optimized for 8-bit grayscale images).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZero8TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZero8TiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel);
int offset = 0; int offset = 0;
var l8 = default(L8);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
byte intensity = (byte)(byte.MaxValue - data[offset++]); byte intensity = (byte)(byte.MaxValue - data[offset++]);
pixelRow[x] = TiffUtils.ColorFromL8(l8, intensity, color); pixelRow[x] = TPixel.FromL8(new(intensity));
} }
} }
} }

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

@ -11,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths). /// Implements the 'WhiteIsZero' photometric interpretation (for all bit depths).
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class WhiteIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class WhiteIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly ushort bitsPerSample0; private readonly ushort bitsPerSample0;
private readonly float factor; private readonly float factor;
public WhiteIsZeroTiffColor(TiffBitsPerSample bitsPerSample) public WhiteIsZeroTiffColor(TiffBitsPerSample bitsPerSample)
@ -27,9 +27,7 @@ internal class WhiteIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{ {
var color = default(TPixel); BitReader bitReader = new(data);
var bitReader = new BitReader(data);
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
{ {
@ -37,10 +35,8 @@ internal class WhiteIsZeroTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
int value = bitReader.ReadBits(this.bitsPerSample0); int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = 1.0f - (value / this.factor); float intensity = 1f - (value / this.factor);
pixelRow[x] = TPixel.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1f));
color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixelRow[x] = color;
} }
bitReader.NextRow(); bitReader.NextRow();

12
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs

@ -11,11 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'YCbCr' with the planar configuration. /// Implements decoding pixel data with photometric interpretation of type 'YCbCr' with the planar configuration.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly YCbCrConverter converter; private readonly YCbCrConverter converter;
private readonly ushort[] ycbcrSubSampling; private readonly ushort[] ycbcrSubSampling;
public YCbCrPlanarTiffColor(Rational[] referenceBlackAndWhite, Rational[] coefficients, ushort[] ycbcrSubSampling) public YCbCrPlanarTiffColor(Rational[] referenceBlackAndWhite, Rational[] coefficients, ushort[] ycbcrSubSampling)
@ -36,13 +36,12 @@ internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData); ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData);
} }
var color = default(TPixel);
int offset = 0; int offset = 0;
int widthPadding = 0; int widthPadding = 0;
if (this.ycbcrSubSampling != null) if (this.ycbcrSubSampling != null)
{ {
// Round to the next integer multiple of horizontalSubSampling. // Round to the next integer multiple of horizontalSubSampling.
widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); widthPadding = TiffUtilities.PaddingToNextInteger(width, this.ycbcrSubSampling[0]);
} }
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
@ -51,8 +50,7 @@ internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]); Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]);
color.FromRgba32(rgba); pixelRow[x] = TPixel.FromRgba32(rgba);
pixelRow[x] = color;
offset++; offset++;
} }
@ -64,8 +62,8 @@ internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
{ {
// If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively,
// then the source data will be padded. // then the source data will be padded.
width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); width += TiffUtilities.PaddingToNextInteger(width, horizontalSubSampling);
height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); height += TiffUtilities.PaddingToNextInteger(height, verticalSubSampling);
for (int row = height - 1; row >= 0; row--) for (int row = height - 1; row >= 0; row--)
{ {

11
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
/// <summary> /// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'YCbCr'. /// Implements decoding pixel data with photometric interpretation of type 'YCbCr'.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -50,13 +51,12 @@ internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
private void DecodeYCbCrData(Buffer2D<TPixel> pixels, int left, int top, int width, int height, ReadOnlySpan<byte> ycbcrData) private void DecodeYCbCrData(Buffer2D<TPixel> pixels, int left, int top, int width, int height, ReadOnlySpan<byte> ycbcrData)
{ {
var color = default(TPixel);
int offset = 0; int offset = 0;
int widthPadding = 0; int widthPadding = 0;
if (this.ycbcrSubSampling != null) if (this.ycbcrSubSampling != null)
{ {
// Round to the next integer multiple of horizontalSubSampling. // Round to the next integer multiple of horizontalSubSampling.
widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); widthPadding = TiffUtilities.PaddingToNextInteger(width, this.ycbcrSubSampling[0]);
} }
for (int y = top; y < top + height; y++) for (int y = top; y < top + height; y++)
@ -65,8 +65,7 @@ internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
for (int x = 0; x < pixelRow.Length; x++) for (int x = 0; x < pixelRow.Length; x++)
{ {
Rgba32 rgba = this.converter.ConvertToRgba32(ycbcrData[offset], ycbcrData[offset + 1], ycbcrData[offset + 2]); Rgba32 rgba = this.converter.ConvertToRgba32(ycbcrData[offset], ycbcrData[offset + 1], ycbcrData[offset + 2]);
color.FromRgba32(rgba); pixelRow[x] = TPixel.FromRgba32(rgba);
pixelRow[x] = color;
offset += 3; offset += 3;
} }
@ -78,8 +77,8 @@ internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
{ {
// If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively,
// then the source data will be padded. // then the source data will be padded.
width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); width += TiffUtilities.PaddingToNextInteger(width, horizontalSubSampling);
height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); height += TiffUtilities.PaddingToNextInteger(height, verticalSubSampling);
int blockWidth = width / horizontalSubSampling; int blockWidth = width / horizontalSubSampling;
int blockHeight = height / verticalSubSampling; int blockHeight = height / verticalSubSampling;
int cbCrOffsetInBlock = horizontalSubSampling * verticalSubSampling; int cbCrOffsetInBlock = horizontalSubSampling * verticalSubSampling;

6
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -163,8 +163,8 @@ internal class TiffDecoderCore : IImageDecoderInternals
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
List<ImageFrame<TPixel>> frames = new(); List<ImageFrame<TPixel>> frames = [];
List<ImageFrameMetadata> framesMetadata = new(); List<ImageFrameMetadata> framesMetadata = [];
try try
{ {
this.inputStream = stream; this.inputStream = stream;
@ -221,7 +221,7 @@ internal class TiffDecoderCore : IImageDecoderInternals
DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); DirectoryReader reader = new(stream, this.configuration.MemoryAllocator);
IList<ExifProfile> directories = reader.Read(); IList<ExifProfile> directories = reader.Read();
List<ImageFrameMetadata> framesMetadata = new(); List<ImageFrameMetadata> framesMetadata = [];
foreach (ExifProfile dir in directories) foreach (ExifProfile dir in directories)
{ {
framesMetadata.Add(this.CreateFrameMetadata(dir)); framesMetadata.Add(this.CreateFrameMetadata(dir));

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

@ -0,0 +1,120 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Utils;
/// <summary>
/// Helper methods for TIFF decoding.
/// </summary>
internal static class TiffUtilities
{
private const float Scale24Bit = 1f / 0xFFFFFF;
private static readonly Vector4 Scale24BitVector = Vector128.Create(Scale24Bit, Scale24Bit, Scale24Bit, 1f).AsVector4();
private const float Scale32Bit = 1f / 0xFFFFFFFF;
private static readonly Vector4 Scale32BitVector = Vector128.Create(Scale32Bit, Scale32Bit, Scale32Bit, 1f).AsVector4();
public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0);
public static L16 L16Default { get; } = new(0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToUShortBigEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToUShortLittleEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ConvertToUIntBigEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt32BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ConvertToUIntLittleEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt32LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromRgba64Premultiplied<TPixel>(ushort r, ushort g, ushort b, ushort a)
where TPixel : unmanaged, IPixel<TPixel>
{
if (a == 0)
{
return TPixel.FromRgba64(default);
}
return TPixel.FromRgba64(new((ushort)(r / a), (ushort)(g / a), (ushort)(b / a), a));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(uint r, uint g, uint b)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(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);
[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);
}
[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);
[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);
[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);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(uint intensity)
where TPixel : unmanaged, IPixel<TPixel>
=> TPixel.FromScaledVector4(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);
}
/// <summary>
/// Finds the padding needed to round 'valueToRoundUp' to the next integer multiple of subSampling value.
/// </summary>
/// <param name="valueToRoundUp">The width or height to round up.</param>
/// <param name="subSampling">The sub sampling.</param>
/// <returns>The padding.</returns>
public static int PaddingToNextInteger(int valueToRoundUp, int subSampling)
{
if (valueToRoundUp % subSampling == 0)
{
return 0;
}
return subSampling - (valueToRoundUp % subSampling);
}
}

176
src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs

@ -1,176 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Utils;
/// <summary>
/// Helper methods for TIFF decoding.
/// </summary>
internal static class TiffUtils
{
private const float Scale24Bit = 1.0f / 0xFFFFFF;
private const float Scale32Bit = 1.0f / 0xFFFFFFFF;
public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0);
public static L16 L16Default { get; } = new(0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToUShortBigEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToUShortLittleEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ConvertToUIntBigEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt32BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ConvertToUIntLittleEndian(ReadOnlySpan<byte> buffer) => BinaryPrimitives.ReadUInt32LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL8<TPixel>(L8 l8, byte intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
l8.PackedValue = intensity;
color.FromL8(l8);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromRgb64<TPixel>(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48);
color.FromRgba64(rgba);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromRgba64<TPixel>(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48);
color.FromRgba64(rgba);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromRgba64Premultiplied<TPixel>(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48);
var vec = rgba.ToVector4();
return UnPremultiply(ref vec, color);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
var colorVector = new Vector4(r * Scale24Bit, g * Scale24Bit, b * Scale24Bit, 1.0f);
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit;
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24BitPremultiplied<TPixel>(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit;
return UnPremultiply(ref colorVector, color);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
var colorVector = new Vector4(r * Scale32Bit, g * Scale32Bit, b * Scale32Bit, 1.0f);
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit;
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32BitPremultiplied<TPixel>(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit;
return UnPremultiply(ref colorVector, color);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL16<TPixel>(L16 l16, ushort intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
l16.PackedValue = intensity;
color.FromL16(l16);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit<TPixel>(ulong intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
var colorVector = new Vector4(intensity * Scale24Bit, intensity * Scale24Bit, intensity * Scale24Bit, 1.0f);
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit<TPixel>(ulong intensity, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
var colorVector = new Vector4(intensity * Scale32Bit, intensity * Scale32Bit, intensity * Scale32Bit, 1.0f);
color.FromScaledVector4(colorVector);
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel UnPremultiply<TPixel>(ref Vector4 vector, TPixel color)
where TPixel : unmanaged, IPixel<TPixel>
{
Numerics.UnPremultiply(ref vector);
color.FromScaledVector4(vector);
return color;
}
/// <summary>
/// Finds the padding needed to round 'valueToRoundUp' to the next integer multiple of subSampling value.
/// </summary>
/// <param name="valueToRoundUp">The width or height to round up.</param>
/// <param name="subSampling">The sub sampling.</param>
/// <returns>The padding.</returns>
public static int PaddingToNextInteger(int valueToRoundUp, int subSampling)
{
if (valueToRoundUp % subSampling == 0)
{
return 0;
}
return subSampling - (valueToRoundUp % subSampling);
}
}

4
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -1,11 +1,11 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Chunks;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Webp.BitWriter; namespace SixLabors.ImageSharp.Formats.Webp.BitWriter;
@ -157,7 +157,7 @@ internal abstract class BitWriterBase
/// <param name="loopCount">The number of times to loop the animation. If it is 0, this means infinitely.</param> /// <param name="loopCount">The number of times to loop the animation. If it is 0, this means infinitely.</param>
public static void WriteAnimationParameter(Stream stream, Color background, ushort loopCount) public static void WriteAnimationParameter(Stream stream, Color background, ushort loopCount)
{ {
WebpAnimationParameter chunk = new(background.ToBgra32().PackedValue, loopCount); WebpAnimationParameter chunk = new(background.ToPixel<Bgra32>().PackedValue, loopCount);
chunk.WriteTo(stream); chunk.WriteTo(stream);
} }

1
src/ImageSharp/Formats/Webp/Chunks/WebpAnimationParameter.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary; using System.Buffers.Binary;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Webp.Chunks; namespace SixLabors.ImageSharp.Formats.Webp.Chunks;

2
src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Webp.Chunks; namespace SixLabors.ImageSharp.Formats.Webp.Chunks;
internal readonly struct WebpFrameData internal readonly struct WebpFrameData

2
src/ImageSharp/Formats/Webp/Chunks/WebpVp8X.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Webp.Chunks; namespace SixLabors.ImageSharp.Formats.Webp.Chunks;
internal readonly struct WebpVp8X internal readonly struct WebpVp8X

1
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -6,7 +6,6 @@ using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Formats.Webp.BitWriter;
using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Chunks;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;

1
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -4,7 +4,6 @@
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Formats.Webp.BitWriter;
using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Chunks;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;

4
src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs

@ -140,7 +140,6 @@ internal sealed class WebpLossyDecoder
private static void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> decodedPixels, IMemoryOwner<byte> alpha) private static void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> decodedPixels, IMemoryOwner<byte> alpha)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default;
Span<byte> alphaSpan = alpha.Memory.Span; Span<byte> alphaSpan = alpha.Memory.Span;
Span<Bgr24> pixelsBgr = MemoryMarshal.Cast<byte, Bgr24>(pixelData); Span<Bgr24> pixelsBgr = MemoryMarshal.Cast<byte, Bgr24>(pixelData);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -151,8 +150,7 @@ internal sealed class WebpLossyDecoder
{ {
int offset = yMulWidth + x; int offset = yMulWidth + x;
Bgr24 bgr = pixelsBgr[offset]; Bgr24 bgr = pixelsBgr[offset];
color.FromBgra32(new Bgra32(bgr.R, bgr.G, bgr.B, alphaSpan[offset])); decodedPixelRow[x] = TPixel.FromBgra32(new(bgr.R, bgr.G, bgr.B, alphaSpan[offset]));
decodedPixelRow[x] = color;
} }
} }
} }

2
src/ImageSharp/Common/Helpers/RiffHelper.cs → src/ImageSharp/Formats/Webp/RiffHelper.cs

@ -4,7 +4,7 @@
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
namespace SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Webp;
internal static class RiffHelper internal static class RiffHelper
{ {

2
src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

@ -102,7 +102,7 @@ internal class WebpAnimationDecoder : IDisposable
{ {
case WebpChunkType.FrameData: case WebpChunkType.FrameData:
Color backgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore Color backgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore
? new Color(new Bgra32(0, 0, 0, 0)) ? Color.FromPixel(new Bgra32(0, 0, 0, 0))
: features.AnimationBackgroundColor!.Value; : features.AnimationBackgroundColor!.Value;
uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, backgroundColor); uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, backgroundColor);
remainingBytes -= (int)dataSize; remainingBytes -= (int)dataSize;

2
src/ImageSharp/Formats/Webp/WebpDecoderCore.cs

@ -438,7 +438,7 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
byte green = (byte)stream.ReadByte(); byte green = (byte)stream.ReadByte();
byte red = (byte)stream.ReadByte(); byte red = (byte)stream.ReadByte();
byte alpha = (byte)stream.ReadByte(); byte alpha = (byte)stream.ReadByte();
features.AnimationBackgroundColor = new Color(new Rgba32(red, green, blue, alpha)); features.AnimationBackgroundColor = Color.FromPixel(new Rgba32(red, green, blue, alpha));
int bytesRead = stream.Read(buffer, 0, 2); int bytesRead = stream.Read(buffer, 0, 2);
if (bytesRead != 2) if (bytesRead != 2)
{ {

2
src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs

@ -48,7 +48,7 @@ public class WebpFrameMetadata : IDeepCloneable
internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata)
=> new() => new()
{ {
FrameDelay = (uint)metadata.Duration.Milliseconds, FrameDelay = (uint)metadata.Duration.TotalMilliseconds,
BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over, BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over,
DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose
}; };

2
src/ImageSharp/ImageInfo.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;

9
src/ImageSharp/Image{TPixel}.cs

@ -4,7 +4,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -78,7 +77,7 @@ public sealed class Image<TPixel> : Image
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param> /// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata) internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata ?? new(), width, height) : base(configuration, TPixel.GetPixelTypeInfo(), metadata ?? new(), width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel)); => this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
/// <summary> /// <summary>
@ -111,7 +110,7 @@ public sealed class Image<TPixel> : Image
int width, int width,
int height, int height,
ImageMetadata metadata) ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height) : base(configuration, TPixel.GetPixelTypeInfo(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, memoryGroup); => this.frames = new ImageFrameCollection<TPixel>(this, width, height, memoryGroup);
/// <summary> /// <summary>
@ -129,7 +128,7 @@ public sealed class Image<TPixel> : Image
int height, int height,
TPixel backgroundColor, TPixel backgroundColor,
ImageMetadata? metadata) ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata ?? new(), width, height) : base(configuration, TPixel.GetPixelTypeInfo(), metadata ?? new(), width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor); => this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor);
/// <summary> /// <summary>
@ -140,7 +139,7 @@ public sealed class Image<TPixel> : Image
/// <param name="metadata">The images metadata.</param> /// <param name="metadata">The images metadata.</param>
/// <param name="frames">The frames that will be owned by this image instance.</param> /// <param name="frames">The frames that will be owned by this image instance.</param>
internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable<ImageFrame<TPixel>> frames) internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable<ImageFrame<TPixel>> frames)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, ValidateFramesAndGetSize(frames)) : base(configuration, TPixel.GetPixelTypeInfo(), metadata, ValidateFramesAndGetSize(frames))
=> this.frames = new ImageFrameCollection<TPixel>(this, frames); => this.frames = new ImageFrameCollection<TPixel>(this, frames);
/// <inheritdoc /> /// <inheritdoc />

2
src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.PixelFormats;

119
src/ImageSharp/PixelFormats/IPixel.cs

@ -14,127 +14,150 @@ namespace SixLabors.ImageSharp.PixelFormats;
public interface IPixel<TSelf> : IPixel, IEquatable<TSelf> public interface IPixel<TSelf> : IPixel, IEquatable<TSelf>
where TSelf : unmanaged, IPixel<TSelf> where TSelf : unmanaged, IPixel<TSelf>
{ {
#pragma warning disable CA1000 // Do not declare static members on generic types
/// <summary> /// <summary>
/// Creates a <see cref="PixelOperations{TPixel}"/> instance for this pixel type. /// 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. /// This method is not intended to be consumed directly. Use <see cref="PixelOperations{TPixel}.Instance"/> instead.
/// </summary> /// </summary>
/// <returns>The <see cref="PixelOperations{TPixel}"/> instance.</returns> /// <returns>The <see cref="PixelOperations{TPixel}"/> instance.</returns>
PixelOperations<TSelf> CreatePixelOperations(); static abstract PixelOperations<TSelf> CreatePixelOperations();
}
/// <summary>
/// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type.
/// </summary>
public interface IPixel
{
/// <summary> /// <summary>
/// Initializes the pixel instance from a generic ("scaled") <see cref="Vector4"/>. /// Initializes the pixel instance from a generic scaled <see cref="Vector4"/>.
/// </summary> /// </summary>
/// <param name="vector">The vector to load the pixel from.</param> /// <param name="source">The vector to load the pixel from.</param>
void FromScaledVector4(Vector4 vector); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromScaledVector4(Vector4 source);
/// <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>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToScaledVector4();
/// <summary> /// <summary>
/// Initializes the pixel instance from a <see cref="Vector4"/> which is specific to the current pixel type. /// Initializes the pixel instance from a <see cref="Vector4"/> which is specific to the current pixel type.
/// </summary> /// </summary>
/// <param name="vector">The vector to load the pixel from.</param> /// <param name="source">The vector to load the pixel from.</param>
void FromVector4(Vector4 vector); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromVector4(Vector4 source);
/// <summary> /// <summary>
/// Expands the pixel into a <see cref="Vector4"/> which is specific to the current pixel type. /// Initializes the pixel instance from an <see cref="Abgr32"/> value.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary> /// </summary>
/// <returns>The <see cref="Vector4"/>.</returns> /// <param name="source">The <see cref="Abgr32"/> value.</param>
Vector4 ToVector4(); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromAbgr32(Abgr32 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Argb32"/> value. /// Initializes the pixel instance from an <see cref="Argb32"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Argb32"/> value.</param> /// <param name="source">The <see cref="Argb32"/> value.</param>
void FromArgb32(Argb32 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromArgb32(Argb32 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Bgra5551"/> value. /// Initializes the pixel instance from an <see cref="Bgra5551"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Bgra5551"/> value.</param> /// <param name="source">The <see cref="Bgra5551"/> value.</param>
void FromBgra5551(Bgra5551 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgra5551(Bgra5551 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Bgr24"/> value. /// Initializes the pixel instance from an <see cref="Bgr24"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Bgr24"/> value.</param> /// <param name="source">The <see cref="Bgr24"/> value.</param>
void FromBgr24(Bgr24 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgr24(Bgr24 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Bgra32"/> value. /// Initializes the pixel instance from an <see cref="Bgra32"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Bgra32"/> value.</param> /// <param name="source">The <see cref="Bgra32"/> value.</param>
void FromBgra32(Bgra32 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromBgra32(Bgra32 source);
/// <summary>
/// Initializes the pixel instance from an <see cref="Abgr32"/> value.
/// </summary>
/// <param name="source">The <see cref="Abgr32"/> value.</param>
void FromAbgr32(Abgr32 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="L8"/> value. /// Initializes the pixel instance from an <see cref="L8"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="L8"/> value.</param> /// <param name="source">The <see cref="L8"/> value.</param>
void FromL8(L8 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromL8(L8 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="L16"/> value. /// Initializes the pixel instance from an <see cref="L16"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="L16"/> value.</param> /// <param name="source">The <see cref="L16"/> value.</param>
void FromL16(L16 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromL16(L16 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="La16"/> value. /// Initializes the pixel instance from an <see cref="La16"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="La16"/> value.</param> /// <param name="source">The <see cref="La16"/> value.</param>
void FromLa16(La16 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromLa16(La16 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="La32"/> value. /// Initializes the pixel instance from an <see cref="La32"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="La32"/> value.</param> /// <param name="source">The <see cref="La32"/> value.</param>
void FromLa32(La32 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromLa32(La32 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Rgb24"/> value. /// Initializes the pixel instance from an <see cref="Rgb24"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Rgb24"/> value.</param> /// <param name="source">The <see cref="Rgb24"/> value.</param>
void FromRgb24(Rgb24 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgb24(Rgb24 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Rgba32"/> value. /// Initializes the pixel instance from an <see cref="Rgba32"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Rgba32"/> value.</param> /// <param name="source">The <see cref="Rgba32"/> value.</param>
void FromRgba32(Rgba32 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgba32(Rgba32 source);
/// <summary>
/// Convert the pixel instance into <see cref="Rgba32"/> representation.
/// </summary>
/// <param name="dest">The reference to the destination <see cref="Rgba32"/> pixel</param>
void ToRgba32(ref Rgba32 dest);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Rgb48"/> value. /// Initializes the pixel instance from an <see cref="Rgb48"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Rgb48"/> value.</param> /// <param name="source">The <see cref="Rgb48"/> value.</param>
void FromRgb48(Rgb48 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgb48(Rgb48 source);
/// <summary> /// <summary>
/// Initializes the pixel instance from an <see cref="Rgba64"/> value. /// Initializes the pixel instance from an <see cref="Rgba64"/> value.
/// </summary> /// </summary>
/// <param name="source">The <see cref="Rgba64"/> value.</param> /// <param name="source">The <see cref="Rgba64"/> value.</param>
void FromRgba64(Rgba64 source); /// <returns>The <typeparamref name="TSelf"/>.</returns>
static abstract TSelf FromRgba64(Rgba64 source);
#pragma warning restore CA1000 // Do not declare static members on generic types
}
/// <summary>
/// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type.
/// </summary>
public interface IPixel
{
/// <summary>
/// Gets the pixel type information.
/// </summary>
/// <returns>The <see cref="PixelTypeInfo"/>.</returns>
static abstract PixelTypeInfo GetPixelTypeInfo();
/// <summary>
/// Convert the pixel instance into <see cref="Rgba32"/> representation.
/// </summary>
/// <returns>The <see cref="Rgba32"/></returns>
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>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToScaledVector4();
/// <summary>
/// Expands the pixel into a <see cref="Vector4"/> which is specific to the current pixel type.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
} }

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

File diff suppressed because it is too large

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

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

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

File diff suppressed because it is too large

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

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

46
src/ImageSharp/PixelFormats/PixelColorType.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Represents the color type and format of a pixel.
/// </summary>
[Flags]
public enum PixelColorType
{
/// <summary>
/// Represents the Red component of the color.
/// </summary>
Red = 1 << 0,
/// <summary>
/// Represents the Green component of the color.
/// </summary>
Green = 1 << 1,
/// <summary>
/// Represents the Blue component of the color.
/// </summary>
Blue = 1 << 2,
/// <summary>
/// Represents the Alpha component of the color for transparency.
/// </summary>
Alpha = 1 << 3,
/// <summary>
/// Indicates that the color is in grayscale.
/// </summary>
Grayscale = 1 << 4,
/// <summary>
/// Indicates that the color is in RGB (Red, Green, Blue) format.
/// </summary>
RGB = Red | Green | Blue | (1 << 5),
/// <summary>
/// Indicates that the color is in BGR (Blue, Green, Red) format.
/// </summary>
BGR = Blue | Green | Red | (1 << 6)
}

50
src/ImageSharp/PixelFormats/PixelComponentBitDepth.cs

@ -0,0 +1,50 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Provides enumeration of the precision in bits of individual components within a pixel format.
/// </summary>
public enum PixelComponentBitDepth
{
/// <summary>
/// 1 bit per component.
/// </summary>
Bit1 = 1,
/// <summary>
/// 2 bits per component.
/// </summary>
Bit2 = 2,
/// <summary>
/// 4 bits per component.
/// </summary>
Bit4 = 4,
/// <summary>
/// 8 bits per component.
/// </summary>
Bit8 = 8,
/// <summary>
/// 16 bits per component.
/// </summary>
Bit16 = 16,
/// <summary>
/// 32 bits per component.
/// </summary>
Bit32 = 32,
/// <summary>
/// 64 bits per component.
/// </summary>
Bit64 = 64,
/// <summary>
/// 128 bits per component.
/// </summary>
Bit128 = 128
}

111
src/ImageSharp/PixelFormats/PixelComponentInfo.cs

@ -0,0 +1,111 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats;
/// <summary>
/// Represents pixel component information within a pixel format.
/// </summary>
public readonly struct PixelComponentInfo
{
private readonly long precisionData1;
private readonly long precisionData2;
private PixelComponentInfo(int count, int padding, long precisionData1, long precisionData2)
{
this.ComponentCount = count;
this.Padding = padding;
this.precisionData1 = precisionData1;
this.precisionData2 = precisionData2;
}
/// <summary>
/// Gets the number of components within the pixel.
/// </summary>
public int ComponentCount { get; }
/// <summary>
/// Gets the number of bytes of padding within the pixel.
/// </summary>
public int Padding { get; }
/// <summary>
/// Creates a new <see cref="PixelComponentInfo"/> instance.
/// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
/// <param name="count">The number of components within the pixel format.</param>
/// <param name="precision">The precision in bits of each component.</param>
/// <returns>The <see cref="PixelComponentInfo"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">The component precision and index cannot exceed the component range.</exception>
public static PixelComponentInfo Create<TPixel>(int count, params int[] precision)
where TPixel : unmanaged, IPixel<TPixel>
{
if (precision.Length != count || precision.Length > 16)
{
throw new ArgumentException($"Count must match the length of precision array and cannot exceed 16.");
}
long precisionData1 = 0;
long precisionData2 = 0;
int sum = 0;
for (int i = 0; i < precision.Length; i++)
{
int p = precision[i];
if (p is < 0 or > 255)
{
throw new ArgumentException("Precision must be between 0 and 255.");
}
if (i < 8)
{
precisionData1 |= ((long)p) << (8 * i);
}
else
{
precisionData2 |= ((long)p) << (8 * (i - 8));
}
sum += p;
}
return new PixelComponentInfo(count, (Unsafe.SizeOf<TPixel>() * 8) - sum, precisionData1, precisionData2);
}
/// <summary>
/// Returns the precision of the component in bits at the given index.
/// </summary>
/// <param name="componentIndex">The component index.</param>
/// <returns>The <see cref="int"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">The component index cannot exceed the component range.</exception>
public int GetComponentPrecision(int componentIndex)
{
if (componentIndex < 0 || componentIndex >= this.ComponentCount)
{
throw new ArgumentOutOfRangeException($"Component index must be between 0 and {this.ComponentCount - 1} inclusive.");
}
long selectedPrecisionData = componentIndex < 8 ? this.precisionData1 : this.precisionData2;
return (int)((selectedPrecisionData >> (8 * (componentIndex & 7))) & 0xFF);
}
/// <summary>
/// Returns the maximum precision in bits of all components.
/// </summary>
/// <returns>The <see cref="int"/>.</returns>
public int GetMaximumComponentPrecision()
{
int maxPrecision = 0;
for (int i = 0; i < this.ComponentCount; i++)
{
int componentPrecision = this.GetComponentPrecision(i);
if (componentPrecision > maxPrecision)
{
maxPrecision = componentPrecision;
}
}
return maxPrecision;
}
}

8
src/ImageSharp/PixelFormats/PixelConversionModifiers.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.ColorSpaces.Companding;
@ -7,9 +7,9 @@ namespace SixLabors.ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Flags responsible to select additional operations which could be efficiently applied in /// Flags responsible to select additional operations which could be efficiently applied in
/// <see cref="PixelOperations{TPixel}.ToVector4(SixLabors.ImageSharp.Configuration,System.ReadOnlySpan{TPixel},System.Span{System.Numerics.Vector4},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/> /// <see cref="PixelOperations{TPixel}.ToVector4(Configuration,ReadOnlySpan{TPixel},Span{System.Numerics.Vector4},PixelConversionModifiers)"/>
/// or /// or
/// <see cref="PixelOperations{TPixel}.FromVector4Destructive(SixLabors.ImageSharp.Configuration,System.Span{System.Numerics.Vector4},System.Span{TPixel},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/> /// <see cref="PixelOperations{TPixel}.FromVector4Destructive(Configuration,Span{System.Numerics.Vector4},Span{TPixel},PixelConversionModifiers)"/>
/// knowing the pixel type. /// knowing the pixel type.
/// </summary> /// </summary>
[Flags] [Flags]
@ -21,7 +21,7 @@ public enum PixelConversionModifiers
None = 0, None = 0,
/// <summary> /// <summary>
/// Select <see cref="IPixel.ToScaledVector4"/> and <see cref="IPixel.FromScaledVector4"/> instead the standard (non scaled) variants. /// Select <see cref="IPixel.ToScaledVector4"/> and <see cref="IPixel{T}.FromScaledVector4"/> instead the standard (non scaled) variants.
/// </summary> /// </summary>
Scale = 1 << 0, Scale = 1 << 0,

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

Loading…
Cancel
Save