Browse Source

Merge remote-tracking branch 'upstream/main' into tiff-frames-meta

pull/2363/head
Ildar Khayrutdinov 3 years ago
parent
commit
e1e5f2573f
  1. 24
      .github/workflows/build-and-test.yml
  2. 2
      ci-pack.ps1
  3. 2
      ci-test.ps1
  4. 2
      shared-infrastructure
  5. 5
      src/ImageSharp/Color/Color.cs
  6. 20
      src/ImageSharp/Common/Helpers/Numerics.cs
  7. 113
      src/ImageSharp/Compression/Zlib/Adler32.cs
  8. 105
      src/ImageSharp/Compression/Zlib/Crc32.cs
  9. 179
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  10. 90
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  11. 14
      src/ImageSharp/Formats/DecoderOptions.cs
  12. 143
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  13. 27
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  14. 3
      src/ImageSharp/Formats/Gif/MetadataExtensions.cs
  15. 3
      src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs
  16. 34
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  17. 79
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  18. 29
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  19. 46
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  20. 308
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  21. 42
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  22. 31
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  23. 5
      src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
  24. 2
      src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
  25. 20
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  26. 20
      src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
  27. 9
      src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
  28. 22
      src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs
  29. 142
      src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
  30. 12
      src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs
  31. 41
      src/ImageSharp/IO/ChunkedMemoryStream.cs
  32. 2
      src/ImageSharp/Image.Decode.cs
  33. 6
      src/ImageSharp/Image.cs
  34. 4
      src/ImageSharp/ImageInfo.cs
  35. 4
      src/ImageSharp/Image{TPixel}.cs
  36. 15
      src/ImageSharp/Metadata/ImageFrameMetadata.cs
  37. 39
      src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
  38. 10
      src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs
  39. 13
      src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs
  40. 3
      src/ImageSharp/PixelFormats/PixelImplementations/A8.cs
  41. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs
  42. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs
  43. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs
  44. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs
  45. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs
  46. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs
  47. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs
  48. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs
  49. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs
  50. 3
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs
  51. 3
      src/ImageSharp/PixelFormats/PixelImplementations/L16.cs
  52. 3
      src/ImageSharp/PixelFormats/PixelImplementations/L8.cs
  53. 3
      src/ImageSharp/PixelFormats/PixelImplementations/La16.cs
  54. 3
      src/ImageSharp/PixelFormats/PixelImplementations/La32.cs
  55. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs
  56. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs
  57. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs
  58. 3
      src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs
  59. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs
  60. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
  61. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs
  62. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs
  63. 7
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs
  64. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs
  65. 3
      src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs
  66. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs
  67. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs
  68. 24
      src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs
  69. 7
      src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
  70. 16
      src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs
  71. 32
      src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs
  72. 16
      src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs
  73. 18
      src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
  74. 56
      src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs
  75. 18
      src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
  76. 18
      src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
  77. 10
      src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs
  78. 40
      src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs
  79. 18
      src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs
  80. 32
      src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs
  81. 12
      src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs
  82. 10
      src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs
  83. 10
      src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs
  84. 10
      src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs
  85. 10
      src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs
  86. 10
      src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs
  87. 16
      src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs
  88. 10
      src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs
  89. 10
      src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs
  90. 10
      src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs
  91. 8
      src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs
  92. 8
      src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs
  93. 10
      src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs
  94. 8
      src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs
  95. 10
      src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs
  96. 18
      src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs
  97. 10
      src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs
  98. 16
      src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs
  99. 48
      src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs
  100. 40
      src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs

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

@ -9,10 +9,13 @@ on:
pull_request:
branches:
- main
types: [ labeled, opened, synchronize, reopened ]
jobs:
Build:
strategy:
matrix:
isARM:
- ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }}
options:
- os: ubuntu-latest
framework: net7.0
@ -32,6 +35,12 @@ jobs:
sdk-preview: true
runtime: -x64
codecov: false
- os: buildjet-4vcpu-ubuntu-2204-arm
framework: net7.0
sdk: 7.0.x
sdk-preview: true
runtime: -x64
codecov: false
- os: ubuntu-latest
framework: net6.0
sdk: 6.0.x
@ -47,10 +56,23 @@ jobs:
sdk: 6.0.x
runtime: -x64
codecov: false
- os: buildjet-4vcpu-ubuntu-2204-arm
framework: net6.0
sdk: 6.0.x
runtime: -x64
codecov: false
exclude:
- isARM: false
options:
os: buildjet-4vcpu-ubuntu-2204-arm
runs-on: ${{matrix.options.os}}
steps:
- name: Install libgdi+, which is required for tests running on ubuntu
if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }}
run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev
- name: Git Config
shell: bash
run: |
@ -92,7 +114,6 @@ jobs:
if: ${{ matrix.options.sdk-preview != true }}
uses: actions/setup-dotnet@v3
with:
include-prerelease: true
dotnet-version: |
6.0.x
@ -100,7 +121,6 @@ jobs:
if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v3
with:
include-prerelease: true
dotnet-version: |
7.0.x

2
ci-pack.ps1

@ -3,4 +3,4 @@ dotnet clean -c Release
$repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY"
# Building for packing and publishing.
dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:RepositoryUrl=$repositoryUrl
dotnet pack -c Release -p:PackageOutputPath="$PSScriptRoot/artifacts" -p:RepositoryUrl=$repositoryUrl

2
ci-test.ps1

@ -33,5 +33,5 @@ elseif ($platform -eq '-x86' -and $targetFramework -match $netFxRegex) {
}
else {
dotnet test --no-build -c Release -f $targetFramework
dotnet test --no-build -c Release -f $targetFramework --blame --diag .tests\Images\ActualOutput\diaglog.txt
}

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 9a82679e92df9476725fd2a2038604fd412af56c
Subproject commit 6c52486c512357475cbb92bbb7c4c0af4d85b1db

5
src/ImageSharp/Color/Color.cs

@ -286,13 +286,10 @@ public readonly partial struct Color : IEquatable<Color>
/// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)]
#pragma warning disable RCS1163 // Unused parameter.
public static void ToPixel<TPixel>(Configuration configuration, ReadOnlySpan<Color> source, Span<TPixel> destination)
#pragma warning restore RCS1163 // Unused parameter.
public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel>
{
// TODO: Investigate bulk operations utilizing configuration parameter here.

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

@ -5,6 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp;
@ -808,6 +809,25 @@ internal static class Numerics
return Sse2.ConvertToInt32(vsum);
}
/// <summary>
/// Reduces elements of the vector into one sum.
/// </summary>
/// <param name="accumulator">The accumulator to reduce.</param>
/// <returns>The sum of all elements.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static int ReduceSumArm(Vector128<uint> accumulator)
{
if (AdvSimd.Arm64.IsSupported)
{
Vector64<uint> sum = AdvSimd.Arm64.AddAcross(accumulator);
return (int)AdvSimd.Extract(sum, 0);
}
Vector128<ulong> sum2 = AdvSimd.AddPairwiseWidening(accumulator);
Vector64<uint> sum3 = AdvSimd.Add(sum2.GetLower().AsUInt32(), sum2.GetUpper().AsUInt32());
return (int)AdvSimd.Extract(sum3, 0);
}
/// <summary>
/// Reduces even elements of the vector into one sum.
/// </summary>

113
src/ImageSharp/Compression/Zlib/Adler32.cs

@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
#pragma warning disable IDE0007 // Use implicit type
@ -70,6 +71,11 @@ internal static class Adler32
return CalculateSse(adler, buffer);
}
if (AdvSimd.IsSupported)
{
return CalculateArm(adler, buffer);
}
return CalculateScalar(adler, buffer);
}
@ -95,7 +101,7 @@ internal static class Adler32
Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
Vector128<sbyte> tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
Vector128<byte> zero = Vector128<byte>.Zero;
var ones = Vector128.Create((short)1);
Vector128<short> ones = Vector128.Create((short)1);
while (blocks > 0)
{
@ -179,13 +185,13 @@ internal static class Adler32
byte* localBufferPtr = bufferPtr;
Vector256<byte> zero = Vector256<byte>.Zero;
var dot3v = Vector256.Create((short)1);
var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
Vector256<short> dot3v = Vector256.Create((short)1);
Vector256<sbyte> dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
// Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE.
var vs1 = Vector256.CreateScalar(s1);
var vs2 = Vector256.CreateScalar(s2);
Vector256<uint> vs1 = Vector256.CreateScalar(s1);
Vector256<uint> vs2 = Vector256.CreateScalar(s2);
while (length >= 32)
{
@ -243,6 +249,100 @@ internal static class Adler32
}
}
// Based on: https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateArm(uint adler, ReadOnlySpan<byte> buffer)
{
// Split Adler-32 into component sums.
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint length = (uint)buffer.Length;
// Process the data in blocks.
long blocks = length / BlockSize;
length -= (uint)(blocks * BlockSize);
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
byte* localBufferPtr = bufferPtr;
while (blocks != 0)
{
uint n = NMAX / BlockSize;
if (n > blocks)
{
n = (uint)blocks;
}
blocks -= n;
// Process n blocks of data. At most nMax data bytes can be
// processed before s2 must be reduced modulo Base.
Vector128<uint> vs1 = Vector128<uint>.Zero;
Vector128<uint> vs2 = vs1.WithElement(3, s1 * n);
Vector128<ushort> vColumnSum1 = Vector128<ushort>.Zero;
Vector128<ushort> vColumnSum2 = Vector128<ushort>.Zero;
Vector128<ushort> vColumnSum3 = Vector128<ushort>.Zero;
Vector128<ushort> vColumnSum4 = Vector128<ushort>.Zero;
do
{
// Load 32 input bytes.
Vector128<ushort> bytes1 = AdvSimd.LoadVector128(localBufferPtr).AsUInt16();
Vector128<ushort> bytes2 = AdvSimd.LoadVector128(localBufferPtr + 0x10).AsUInt16();
// Add previous block byte sum to v_s2.
vs2 = AdvSimd.Add(vs2, vs1);
// Horizontally add the bytes for s1.
vs1 = AdvSimd.AddPairwiseWideningAndAdd(
vs1.AsUInt32(),
AdvSimd.AddPairwiseWideningAndAdd(AdvSimd.AddPairwiseWidening(bytes1.AsByte()).AsUInt16(), bytes2.AsByte()));
// Vertically add the bytes for s2.
vColumnSum1 = AdvSimd.AddWideningLower(vColumnSum1, bytes1.GetLower().AsByte());
vColumnSum2 = AdvSimd.AddWideningLower(vColumnSum2, bytes1.GetUpper().AsByte());
vColumnSum3 = AdvSimd.AddWideningLower(vColumnSum3, bytes2.GetLower().AsByte());
vColumnSum4 = AdvSimd.AddWideningLower(vColumnSum4, bytes2.GetUpper().AsByte());
localBufferPtr += BlockSize;
}
while (--n > 0);
vs2 = AdvSimd.ShiftLeftLogical(vs2, 5);
// Multiply-add bytes by [ 32, 31, 30, ... ] for s2.
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum1.GetLower(), Vector64.Create((ushort)32, 31, 30, 29));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum1.GetUpper(), Vector64.Create((ushort)28, 27, 26, 25));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum2.GetLower(), Vector64.Create((ushort)24, 23, 22, 21));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum2.GetUpper(), Vector64.Create((ushort)20, 19, 18, 17));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum3.GetLower(), Vector64.Create((ushort)16, 15, 14, 13));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum3.GetUpper(), Vector64.Create((ushort)12, 11, 10, 9));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum4.GetLower(), Vector64.Create((ushort)8, 7, 6, 5));
vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum4.GetUpper(), Vector64.Create((ushort)4, 3, 2, 1));
// Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
Vector64<uint> sum1 = AdvSimd.AddPairwise(vs1.GetLower(), vs1.GetUpper());
Vector64<uint> sum2 = AdvSimd.AddPairwise(vs2.GetLower(), vs2.GetUpper());
Vector64<uint> s1s2 = AdvSimd.AddPairwise(sum1, sum2);
// Store the results.
s1 += AdvSimd.Extract(s1s2, 0);
s2 += AdvSimd.Extract(s1s2, 1);
// Reduce.
s1 %= BASE;
s2 %= BASE;
}
if (length > 0)
{
HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
}
return s1 | (s2 << 16);
}
}
private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
{
if (length >= 16)
@ -286,7 +386,6 @@ internal static class Adler32
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint k;
fixed (byte* bufferPtr = buffer)
{
@ -295,7 +394,7 @@ internal static class Adler32
while (length > 0)
{
k = length < NMAX ? length : NMAX;
uint k = length < NMAX ? length : NMAX;
length -= k;
while (k >= 16)

105
src/ImageSharp/Compression/Zlib/Crc32.cs

@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using ArmCrc32 = System.Runtime.Intrinsics.Arm.Crc32;
namespace SixLabors.ImageSharp.Compression.Zlib;
@ -60,6 +61,16 @@ internal static partial class Crc32
return ~CalculateSse(~crc, buffer);
}
if (ArmCrc32.Arm64.IsSupported)
{
return ~CalculateArm64(~crc, buffer);
}
if (ArmCrc32.IsSupported)
{
return ~CalculateArm(~crc, buffer);
}
return ~CalculateScalar(~crc, buffer);
}
@ -187,6 +198,100 @@ internal static partial class Crc32
}
}
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateArm(uint crc, ReadOnlySpan<byte> buffer)
{
fixed (byte* bufferPtr = buffer)
{
byte* localBufferPtr = bufferPtr;
int len = buffer.Length;
while (len > 0 && ((ulong)localBufferPtr & 3) != 0)
{
crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++);
len--;
}
uint* intBufferPtr = (uint*)localBufferPtr;
while (len >= 8 * sizeof(uint))
{
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
len -= 8 * sizeof(uint);
}
while (len >= sizeof(uint))
{
crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++);
len -= sizeof(uint);
}
localBufferPtr = (byte*)intBufferPtr;
while (len > 0)
{
crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++);
len--;
}
return crc;
}
}
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateArm64(uint crc, ReadOnlySpan<byte> buffer)
{
fixed (byte* bufferPtr = buffer)
{
byte* localBufferPtr = bufferPtr;
int len = buffer.Length;
while (len > 0 && ((ulong)localBufferPtr & 7) != 0)
{
crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++);
len--;
}
ulong* longBufferPtr = (ulong*)localBufferPtr;
while (len >= 8 * sizeof(ulong))
{
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
len -= 8 * sizeof(ulong);
}
while (len >= sizeof(ulong))
{
crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++);
len -= sizeof(ulong);
}
localBufferPtr = (byte*)longBufferPtr;
while (len > 0)
{
crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++);
len--;
}
return crc;
}
}
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static uint CalculateScalar(uint crc, ReadOnlySpan<byte> buffer)
{

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

@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers;
@ -58,20 +58,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// </summary>
private const int RleDelta = 0x02;
/// <summary>
/// The stream to decode from.
/// </summary>
private BufferedReadStream stream;
/// <summary>
/// The metadata.
/// </summary>
private ImageMetadata metadata;
private ImageMetadata? metadata;
/// <summary>
/// The bitmap specific metadata.
/// </summary>
private BmpMetadata bmpMetadata;
private BmpMetadata? bmpMetadata;
/// <summary>
/// The file header containing general information.
@ -126,7 +121,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Image<TPixel> image = null;
Image<TPixel>? image = null;
try
{
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
@ -142,24 +137,25 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{
if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
{
this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else
{
this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(
stream,
pixels,
palette,
this.infoHeader.Width,
@ -172,19 +168,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break;
case BmpCompression.RLE24:
this.ReadRle24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRle24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
case BmpCompression.RLE8:
case BmpCompression.RLE4:
this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
this.ReadRle(stream, this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
case BmpCompression.BitFields:
case BmpCompression.BI_ALPHABITFIELDS:
this.ReadBitFields(pixels, inverted);
this.ReadBitFields(stream, pixels, inverted);
break;
@ -250,14 +246,16 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// which will be used to determine which bits belong to that channel.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadBitFields<TPixel>(Buffer2D<TPixel> pixels, bool inverted)
private void ReadBitFields<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, bool inverted)
where TPixel : unmanaged, IPixel<TPixel>
{
if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(
stream,
pixels,
this.infoHeader.Width,
this.infoHeader.Height,
@ -269,6 +267,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
else
{
this.ReadRgb32BitFields(
stream,
pixels,
this.infoHeader.Width,
this.infoHeader.Height,
@ -282,17 +281,17 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data.
/// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte}, Span{bool}, Span{bool})"/>
/// Compressed RLE4 stream is uncompressed by <see cref="UncompressRle4(int, Span{byte}, Span{bool}, Span{bool})"/>
/// Compressed RLE4 stream is uncompressed by <see cref="UncompressRle4(BufferedReadStream, int, Span{byte}, Span{bool}, Span{bool})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="compression">The compression type. Either RLE4 or RLE8.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle<TPixel>(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>
{
TPixel color = default;
@ -305,11 +304,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
Span<byte> bufferSpan = buffer.Memory.Span;
if (compression is BmpCompression.RLE8)
{
this.UncompressRle8(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
}
else
{
this.UncompressRle4(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
}
for (int y = 0; y < height; y++)
@ -368,11 +367,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Looks up color values and builds the image from de-compressed RLE24.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle24<TPixel>(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>
{
TPixel color = default;
@ -384,7 +384,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
Span<bool> undefinedPixelsSpan = undefinedPixels.Memory.Span;
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle24(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan);
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
@ -446,18 +446,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte contains two color indexes.
/// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track over skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle4(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
private void UncompressRle4(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{
Span<byte> cmd = stackalloc byte[2];
int count = 0;
while (count < buffer.Length)
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
if (stream.Read(cmd, 0, cmd.Length) != 2)
{
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
}
@ -478,8 +479,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break;
case RleDelta:
int dx = this.stream.ReadByte();
int dy = this.stream.ReadByte();
int dx = stream.ReadByte();
int dy = stream.ReadByte();
count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break;
@ -492,7 +493,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length);
stream.Read(run, 0, run.Length);
int idx = 0;
for (int i = 0; i < max; i++)
@ -512,7 +513,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary.
int padding = bytesToRead & 1;
this.stream.Skip(padding);
stream.Skip(padding);
break;
}
@ -551,18 +552,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte is the color for the run.
/// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle8(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
private void UncompressRle8(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{
Span<byte> cmd = stackalloc byte[2];
int count = 0;
while (count < buffer.Length)
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
if (stream.Read(cmd, 0, cmd.Length) != 2)
{
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
}
@ -583,8 +585,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break;
case RleDelta:
int dx = this.stream.ReadByte();
int dy = this.stream.ReadByte();
int dx = stream.ReadByte();
int dy = stream.ReadByte();
count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break;
@ -596,7 +598,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[length];
this.stream.Read(run, 0, run.Length);
stream.Read(run, 0, run.Length);
run.AsSpan().CopyTo(buffer[count..]);
@ -605,7 +607,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary.
int padding = length & 1;
this.stream.Skip(padding);
stream.Skip(padding);
break;
}
@ -630,18 +632,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and following three bytes are the color for the run.
/// </remarks>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="undefinedPixels">Keeps track of skipped and therefore undefined pixels.</param>
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
private void UncompressRle24(int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
private void UncompressRle24(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
{
Span<byte> cmd = stackalloc byte[2];
int uncompressedPixels = 0;
while (uncompressedPixels < buffer.Length)
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
if (stream.Read(cmd, 0, cmd.Length) != 2)
{
BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap.");
}
@ -662,8 +665,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
break;
case RleDelta:
int dx = this.stream.ReadByte();
int dy = this.stream.ReadByte();
int dx = stream.ReadByte();
int dy = stream.ReadByte();
uncompressedPixels += RleSkipDelta(uncompressedPixels, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels);
break;
@ -675,7 +678,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
byte[] run = new byte[length * 3];
this.stream.Read(run, 0, run.Length);
stream.Read(run, 0, run.Length);
run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]);
@ -684,7 +687,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Absolute mode data is aligned to two-byte word-boundary.
int padding = run.Length & 1;
this.stream.Skip(padding);
stream.Skip(padding);
break;
}
@ -693,8 +696,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
{
int max = uncompressedPixels + cmd[0];
byte blueIdx = cmd[1];
byte greenIdx = (byte)this.stream.ReadByte();
byte redIdx = (byte)this.stream.ReadByte();
byte greenIdx = (byte)stream.ReadByte();
byte redIdx = (byte)stream.ReadByte();
int bufferIdx = uncompressedPixels * 3;
for (; uncompressedPixels < max; uncompressedPixels++)
@ -800,6 +803,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
@ -808,7 +812,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="bytesPerColorMapEntry">Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
private void ReadRgbPalette<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
where TPixel : unmanaged, IPixel<TPixel>
{
// Pixels per byte (bits per pixel).
@ -833,7 +837,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -861,6 +865,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 16 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
@ -868,7 +873,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="redMask">The bitmask for the red channel.</param>
/// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param>
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
private void ReadRgb16<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
where TPixel : unmanaged, IPixel<TPixel>
{
int padding = CalculatePadding(width, 2);
@ -889,7 +894,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++)
{
if (this.stream.Read(bufferSpan) == 0)
if (stream.Read(bufferSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -935,11 +940,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 24 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel>
{
int padding = CalculatePadding(width, 3);
@ -948,7 +954,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++)
{
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -967,11 +973,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Reads the 32 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb32Fast<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
@ -980,7 +987,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++)
{
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -1000,11 +1007,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb32Slow<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : unmanaged, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
@ -1012,7 +1020,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
using IMemoryOwner<Bgra32> bgraRow = this.memoryAllocator.Allocate<Bgra32>(width);
Span<byte> rowSpan = row.GetSpan();
Span<Bgra32> bgraRowSpan = bgraRow.GetSpan();
long currentPosition = this.stream.Position;
long currentPosition = stream.Position;
bool hasAlpha = false;
// Loop though the rows checking each pixel. We start by assuming it's
@ -1020,7 +1028,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// actually a BGRA image, and change tactics accordingly.
for (int y = 0; y < height; y++)
{
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -1049,14 +1057,14 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
}
// Reset our stream for a second pass.
this.stream.Position = currentPosition;
stream.Position = currentPosition;
// Process the pixels in bulk taking the raw alpha component value.
if (hasAlpha)
{
for (int y = 0; y < height; y++)
{
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -1077,7 +1085,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Slow path. We need to set each alpha component value to fully opaque.
for (int y = 0; y < height; y++)
{
if (this.stream.Read(rowSpan) == 0)
if (stream.Read(rowSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -1105,6 +1113,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// Decode an 32 Bit Bitmap containing a bitmask for each color channel.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
@ -1113,7 +1122,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param>
/// <param name="alphaMask">The bitmask for the alpha channel.</param>
private void ReadRgb32BitFields<TPixel>(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>
{
TPixel color = default;
@ -1142,7 +1151,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
for (int y = 0; y < height; y++)
{
if (this.stream.Read(bufferSpan) == 0)
if (stream.Read(bufferSpan) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!");
}
@ -1228,10 +1237,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the <see cref="BmpInfoHeader"/> from the stream.
/// </summary>
private void ReadInfoHeader()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(bmpMetadata))]
private void ReadInfoHeader(BufferedReadStream stream)
{
Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
long infoHeaderStart = this.stream.Position;
long infoHeaderStart = stream.Position;
// Resolution is stored in PPM.
this.metadata = new ImageMetadata
@ -1240,7 +1252,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
};
// Read the header size.
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize is < BmpInfoHeader.CoreSize or > BmpInfoHeader.MaxHeaderSize)
@ -1249,7 +1261,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
}
// Read the rest of the header.
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2;
if (headerSize == BmpInfoHeader.CoreSize)
@ -1275,7 +1287,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
if (this.infoHeader.Compression == BmpCompression.BitFields)
{
byte[] bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12);
stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
@ -1284,7 +1296,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
{
byte[] bitfieldsBuffer = new byte[16];
this.stream.Read(bitfieldsBuffer, 0, 16);
stream.Read(bitfieldsBuffer, 0, 16);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
@ -1324,12 +1336,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
if (this.infoHeader.ProfileData != 0 && this.infoHeader.ProfileSize != 0)
{
// Read color profile.
long streamPosition = this.stream.Position;
long streamPosition = stream.Position;
byte[] iccProfileData = new byte[this.infoHeader.ProfileSize];
this.stream.Position = infoHeaderStart + this.infoHeader.ProfileData;
this.stream.Read(iccProfileData);
stream.Position = infoHeaderStart + this.infoHeader.ProfileData;
stream.Read(iccProfileData);
this.metadata.IccProfile = new IccProfile(iccProfileData);
this.stream.Position = streamPosition;
stream.Position = streamPosition;
}
}
else
@ -1358,10 +1370,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> from the stream.
/// </summary>
private void ReadFileHeader()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadFileHeader(BufferedReadStream stream)
{
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
this.stream.Read(buffer, 0, BmpFileHeader.Size);
stream.Read(buffer, 0, BmpFileHeader.Size);
short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer);
switch (fileTypeMarker)
@ -1375,7 +1388,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
// Because we only decode the first bitmap in the array, the array header will be ignored.
// The bitmap file header of the first image follows the array header.
this.stream.Read(buffer, 0, BmpFileHeader.Size);
stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
{
@ -1398,12 +1411,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// <param name="palette">The color palette.</param>
/// <returns>Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</returns>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(bmpMetadata))]
private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette)
{
this.stream = stream;
this.ReadFileHeader();
this.ReadInfoHeader();
this.ReadFileHeader(stream);
this.ReadInfoHeader(stream);
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
@ -1451,13 +1464,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
}
palette = null;
palette = Array.Empty<byte>();
if (colorMapSizeBytes > 0)
{
// Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit.
// Make sure, that we will not read pass the bitmap offset (starting position of image data).
if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset)
if ((stream.Position + colorMapSizeBytes) > this.fileHeader.Offset)
{
BmpThrowHelper.ThrowInvalidImageContentException(
$"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset.");
@ -1465,21 +1478,21 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
palette = new byte[colorMapSizeBytes];
if (this.stream.Read(palette, 0, colorMapSizeBytes) == 0)
if (stream.Read(palette, 0, colorMapSizeBytes) == 0)
{
BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!");
}
}
int skipAmount = this.fileHeader.Offset - (int)this.stream.Position;
if ((skipAmount + (int)this.stream.Position) > this.stream.Length)
int skipAmount = this.fileHeader.Offset - (int)stream.Position;
if ((skipAmount + (int)stream.Position) > stream.Length)
{
BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length.");
}
if (skipAmount > 0)
{
this.stream.Skip(skipAmount);
stream.Skip(skipAmount);
}
return bytesPerColorMapEntry;

90
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Buffers.Binary;
@ -69,11 +68,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The color depth, in number of bits per pixel.
/// </summary>
@ -124,7 +118,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
Configuration configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
BmpMetadata bmpMetadata = metadata.GetBmpMetadata();
this.bitsPerPixel ??= bmpMetadata.BitsPerPixel;
@ -142,7 +136,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
_ => 0
};
byte[] iccProfileData = null;
byte[]? iccProfileData = null;
int iccProfileSize = 0;
if (metadata.IccProfile != null)
{
@ -165,7 +159,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
this.WriteImage(stream, image);
this.WriteImage(configuration, stream, image);
WriteColorProfile(stream, iccProfileData, buffer);
stream.Flush();
@ -182,7 +176,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// <param name="metadata">The metadata.</param>
/// <param name="iccProfileData">The icc profile data.</param>
/// <returns>The bitmap information header.</returns>
private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[] iccProfileData)
private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[]? iccProfileData)
{
int hResolution = 0;
int vResolution = 0;
@ -234,7 +228,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
infoHeader.Compression = BmpCompression.BitFields;
}
if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && metadata.IccProfile != null)
if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && iccProfileData != null)
{
infoHeader.ProfileSize = iccProfileData.Length;
infoHeader.CsType = BmpColorSpace.PROFILE_EMBEDDED;
@ -250,7 +244,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// <param name="stream">The stream to write to.</param>
/// <param name="iccProfileData">The color profile data.</param>
/// <param name="buffer">The buffer.</param>
private static void WriteColorProfile(Stream stream, byte[] iccProfileData, Span<byte> buffer)
private static void WriteColorProfile(Stream stream, byte[]? iccProfileData, Span<byte> buffer)
{
if (iccProfileData != null)
{
@ -313,42 +307,43 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(Stream stream, Image<TPixel> image)
private void WriteImage<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer;
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32BitPixelData(stream, pixels);
this.Write32BitPixelData(configuration, stream, pixels);
break;
case BmpBitsPerPixel.Pixel24:
this.Write24BitPixelData(stream, pixels);
this.Write24BitPixelData(configuration, stream, pixels);
break;
case BmpBitsPerPixel.Pixel16:
this.Write16BitPixelData(stream, pixels);
this.Write16BitPixelData(configuration, stream, pixels);
break;
case BmpBitsPerPixel.Pixel8:
this.Write8BitPixelData(stream, image);
this.Write8BitPixelData(configuration, stream, image);
break;
case BmpBitsPerPixel.Pixel4:
this.Write4BitPixelData(stream, image);
this.Write4BitPixelData(configuration, stream, image);
break;
case BmpBitsPerPixel.Pixel2:
this.Write2BitPixelData(stream, image);
this.Write2BitPixelData(configuration, stream, image);
break;
case BmpBitsPerPixel.Pixel1:
this.Write1BitPixelData(stream, image);
this.Write1BitPixelData(configuration, stream, image);
break;
}
}
@ -360,9 +355,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 32-bit data with a color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write32BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write32BitPixelData<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4);
@ -372,7 +368,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(
this.configuration,
configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@ -384,9 +380,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 24-bit pixel data with a color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write24BitPixelData<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
@ -398,7 +395,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration,
configuration,
pixelSpan,
row.Slice(0, rowBytesWithoutPadding),
width);
@ -410,10 +407,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 16-bit pixel data with a color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write16BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
private void Write16BitPixelData<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = pixels.Width;
int rowBytesWithoutPadding = width * 2;
@ -425,7 +423,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra5551Bytes(
this.configuration,
configuration,
pixelSpan,
row.Slice(0, rowBytesWithoutPadding),
pixelSpan.Length);
@ -438,9 +436,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 8 bit pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</param>
private void Write8BitPixelData<TPixel>(Stream stream, Image<TPixel> image)
private void Write8BitPixelData<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
bool isL8 = typeof(TPixel) == typeof(L8);
@ -453,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
}
else
{
this.Write8BitColor(stream, image, colorPalette);
this.Write8BitColor(configuration, stream, image, colorPalette);
}
}
@ -461,19 +460,20 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes an 8 bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</param>
/// <param name="colorPalette">A byte span of size 1024 for the color palette.</param>
private void Write8BitColor<TPixel>(Stream stream, Image<TPixel> image, Span<byte> colorPalette)
private void Write8BitColor<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image, Span<byte> colorPalette)
where TPixel : unmanaged, IPixel<TPixel>
{
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration);
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration);
frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
for (int y = image.Height - 1; y >= 0; y--)
{
@ -529,12 +529,13 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 4 bit pixel data with a color palette. The color palette has 16 entry's with 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
private void Write4BitPixelData<TPixel>(Stream stream, Image<TPixel> image)
private void Write4BitPixelData<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions()
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions()
{
MaxColors = 16
});
@ -546,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding;
@ -576,12 +577,13 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 2 bit pixel data with a color palette. The color palette has 4 entry's with 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
private void Write2BitPixelData<TPixel>(Stream stream, Image<TPixel> image)
private void Write2BitPixelData<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions()
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions()
{
MaxColors = 4
});
@ -593,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding;
@ -632,12 +634,13 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 1 bit pixel data with a color palette. The color palette has 2 entry's with 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
private void Write1BitPixelData<TPixel>(Stream stream, Image<TPixel> image)
private void Write1BitPixelData<TPixel>(Configuration configuration, Stream stream, Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, new QuantizerOptions()
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration, new QuantizerOptions()
{
MaxColors = 2
});
@ -649,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette);
WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0);
int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding;
@ -681,14 +684,15 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes the color palette to the stream. The color palette has 4 bytes for each entry.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="quantizedColorPalette">The color palette from the quantized image.</param>
/// <param name="colorPalette">A temporary byte span to write the color palette to.</param>
private void WriteColorPalette<TPixel>(Stream stream, ReadOnlySpan<TPixel> quantizedColorPalette, Span<byte> colorPalette)
private static void WriteColorPalette<TPixel>(Configuration configuration, Stream stream, ReadOnlySpan<TPixel> quantizedColorPalette, Span<byte> colorPalette)
where TPixel : unmanaged, IPixel<TPixel>
{
int quantizedColorBytes = quantizedColorPalette.Length * 4;
PixelOperations<TPixel>.Instance.ToBgra32(this.configuration, quantizedColorPalette, MemoryMarshal.Cast<byte, Bgra32>(colorPalette[..quantizedColorBytes]));
PixelOperations<TPixel>.Instance.ToBgra32(configuration, quantizedColorPalette, MemoryMarshal.Cast<byte, Bgra32>(colorPalette[..quantizedColorBytes]));
Span<uint> colorPaletteAsUInt = MemoryMarshal.Cast<byte, uint>(colorPalette);
for (int i = 0; i < colorPaletteAsUInt.Length; i++)
{

14
src/ImageSharp/Formats/DecoderOptions.cs

@ -15,15 +15,25 @@ public sealed class DecoderOptions
private uint maxFrames = int.MaxValue;
// Used by the FileProvider in the unit tests to set the configuration on the fly.
#pragma warning disable IDE0032 // Use auto property
private Configuration configuration = Configuration.Default;
#pragma warning restore IDE0032 // Use auto property
/// <summary>
/// Gets the shared default general decoder options instance.
/// Used internally to reduce allocations for default decoding operations.
/// </summary>
internal static DecoderOptions Default { get; } = LazyOptions.Value;
/// <summary>
/// Gets a custom configuration instance to be used by the image processing pipeline.
/// </summary>
public Configuration Configuration { get; internal set; } = Configuration.Default;
#pragma warning disable IDE0032 // Use auto property
#pragma warning disable RCS1085 // Use auto-implemented property.
public Configuration Configuration { get => this.configuration; init => this.configuration = value; }
#pragma warning restore RCS1085 // Use auto-implemented property.
#pragma warning restore IDE0032 // Use auto property
/// <summary>
/// Gets the target size to decode the image into. Scaling should use an operation equivalent to <see cref="ResizeMode.Max"/>.
@ -44,4 +54,6 @@ public sealed class DecoderOptions
/// Gets the maximum number of image frames to decode, inclusive.
/// </summary>
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
internal void SetConfiguration(Configuration configuration) => this.configuration = configuration;
}

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

@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@ -24,15 +24,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// The currently loaded stream.
/// </summary>
private BufferedReadStream stream;
/// <summary>
/// The global color table.
/// </summary>
private IMemoryOwner<byte> globalColorTable;
private IMemoryOwner<byte>? globalColorTable;
/// <summary>
/// The area to restore.
@ -77,12 +72,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// The abstract metadata.
/// </summary>
private ImageMetadata metadata;
private ImageMetadata? metadata;
/// <summary>
/// The gif specific metadata.
/// </summary>
private GifMetadata gifMetadata;
private GifMetadata? gifMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
@ -108,8 +103,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel>
{
uint frameCount = 0;
Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null;
Image<TPixel>? image = null;
ImageFrame<TPixel>? previousFrame = null;
try
{
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
@ -125,7 +120,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
break;
}
this.ReadFrame(ref image, ref previousFrame);
this.ReadFrame(stream, ref image, ref previousFrame);
// Reset per-frame state.
this.imageDescriptor = default;
@ -136,16 +131,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
this.ReadGraphicalControlExtension(stream);
break;
case GifConstants.CommentLabel:
this.ReadComments();
this.ReadComments(stream);
break;
case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension();
this.ReadApplicationExtension(stream);
break;
case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder.
SkipBlock(stream); // Not supported by any known decoder.
break;
}
}
@ -187,23 +182,23 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{
if (nextFlag == GifConstants.ImageLabel)
{
this.ReadImageDescriptor();
this.ReadImageDescriptor(stream);
}
else if (nextFlag == GifConstants.ExtensionIntroducer)
{
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
this.SkipBlock(); // Skip graphic control extension block
SkipBlock(stream); // Skip graphic control extension block
break;
case GifConstants.CommentLabel:
this.ReadComments();
this.ReadComments(stream);
break;
case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension();
this.ReadApplicationExtension(stream);
break;
case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder.
SkipBlock(stream); // Not supported by any known decoder.
break;
}
}
@ -239,9 +234,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the graphic control extension.
/// </summary>
private void ReadGraphicalControlExtension()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadGraphicalControlExtension(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 6);
int bytesRead = stream.Read(this.buffer, 0, 6);
if (bytesRead != 6)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
@ -253,9 +249,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the image descriptor.
/// </summary>
private void ReadImageDescriptor()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadImageDescriptor(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 9);
int bytesRead = stream.Read(this.buffer, 0, 9);
if (bytesRead != 9)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
@ -271,9 +268,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the logical screen descriptor.
/// </summary>
private void ReadLogicalScreenDescriptor()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadLogicalScreenDescriptor(BufferedReadStream stream)
{
int bytesRead = this.stream.Read(this.buffer, 0, 7);
int bytesRead = stream.Read(this.buffer, 0, 7);
if (bytesRead != 7)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
@ -286,84 +284,87 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads the application extension block parsing any animation or XMP information
/// if present.
/// </summary>
private void ReadApplicationExtension()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadApplicationExtension(BufferedReadStream stream)
{
int appLength = this.stream.ReadByte();
int appLength = stream.ReadByte();
// If the length is 11 then it's a valid extension and most likely
// a NETSCAPE, XMP or ANIMEXTS extension. We want the loop count from this.
long position = this.stream.Position;
long position = stream.Position;
if (appLength == GifConstants.ApplicationBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
if (isXmp && !this.skipMetadata)
{
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator);
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator);
if (extension.Data.Length > 0)
{
this.metadata.XmpProfile = new XmpProfile(extension.Data);
this.metadata!.XmpProfile = new XmpProfile(extension.Data);
}
else
{
// Reset the stream position and continue.
this.stream.Position = position;
this.SkipBlock(appLength);
stream.Position = position;
SkipBlock(stream, appLength);
}
return;
}
int subBlockSize = this.stream.ReadByte();
int subBlockSize = stream.ReadByte();
// TODO: There's also a NETSCAPE buffer extension.
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
this.stream.Skip(1); // Skip the terminator.
stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
stream.Skip(1); // Skip the terminator.
return;
}
// Could be something else not supported yet.
// Skip the subblock and terminator.
this.SkipBlock(subBlockSize);
SkipBlock(stream, subBlockSize);
return;
}
this.SkipBlock(appLength); // Not supported by any known decoder.
SkipBlock(stream, appLength); // Not supported by any known decoder.
}
/// <summary>
/// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// </summary>
private void SkipBlock(int blockSize = 0)
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="blockSize">The length of the block to skip.</param>
private static void SkipBlock(BufferedReadStream stream, int blockSize = 0)
{
if (blockSize > 0)
{
this.stream.Skip(blockSize);
stream.Skip(blockSize);
}
int flag;
while ((flag = this.stream.ReadByte()) > 0)
while ((flag = stream.ReadByte()) > 0)
{
this.stream.Skip(flag);
stream.Skip(flag);
}
}
/// <summary>
/// Reads the gif comments.
/// </summary>
private void ReadComments()
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadComments(BufferedReadStream stream)
{
int length;
var stringBuilder = new StringBuilder();
while ((length = this.stream.ReadByte()) != 0)
StringBuilder stringBuilder = new();
while ((length = stream.ReadByte()) != 0)
{
if (length > GifConstants.MaxCommentSubBlockLength)
{
@ -372,21 +373,21 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
if (this.skipMetadata)
{
this.stream.Seek(length, SeekOrigin.Current);
stream.Seek(length, SeekOrigin.Current);
continue;
}
using IMemoryOwner<byte> commentsBuffer = this.memoryAllocator.Allocate<byte>(length);
Span<byte> commentsSpan = commentsBuffer.GetSpan();
this.stream.Read(commentsSpan);
stream.Read(commentsSpan);
string commentPart = GifConstants.Encoding.GetString(commentsSpan);
stringBuilder.Append(commentPart);
}
if (stringBuilder.Length > 0)
{
this.gifMetadata.Comments.Add(stringBuilder.ToString());
this.gifMetadata!.Comments.Add(stringBuilder.ToString());
}
}
@ -394,15 +395,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads an individual gif frame.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame)
private void ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? image, ref ImageFrame<TPixel>? previousFrame)
where TPixel : unmanaged, IPixel<TPixel>
{
this.ReadImageDescriptor();
this.ReadImageDescriptor(stream);
IMemoryOwner<byte> localColorTable = null;
Buffer2D<byte> indices = null;
IMemoryOwner<byte>? localColorTable = null;
Buffer2D<byte>? indices = null;
try
{
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
@ -410,11 +412,11 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{
int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
this.stream.Read(localColorTable.GetSpan());
stream.Read(localColorTable.GetSpan());
}
indices = this.configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
this.ReadFrameIndices(indices);
this.ReadFrameIndices(stream, indices);
Span<byte> rawColorTable = default;
if (localColorTable != null)
@ -430,7 +432,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
// Skip any remaining blocks
this.SkipBlock();
SkipBlock(stream);
}
finally
{
@ -442,12 +444,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary>
/// Reads the frame indices marking the color to use for each pixel.
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="indices">The 2D pixel buffer to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(Buffer2D<byte> indices)
private void ReadFrameIndices(BufferedReadStream stream, Buffer2D<byte> indices)
{
int minCodeSize = this.stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream);
int minCodeSize = stream.ReadByte();
using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream);
lzwDecoder.DecodePixels(minCodeSize, indices);
}
@ -460,15 +463,15 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Buffer2D<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
private void ReadFrameColors<TPixel>(ref Image<TPixel>? image, ref ImageFrame<TPixel>? previousFrame, Buffer2D<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
where TPixel : unmanaged, IPixel<TPixel>
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
bool transFlag = this.graphicsControlExtension.TransparencyFlag;
ImageFrame<TPixel> prevFrame = null;
ImageFrame<TPixel> currentFrame = null;
ImageFrame<TPixel>? prevFrame = null;
ImageFrame<TPixel>? currentFrame = null;
ImageFrame<TPixel> imageFrame;
if (previousFrame is null)
@ -494,7 +497,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
prevFrame = previousFrame;
}
currentFrame = image.Frames.CreateFrame();
currentFrame = image!.Frames.CreateFrame();
this.SetFrameMetadata(currentFrame.Metadata, false);
@ -661,13 +664,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads the logical screen descriptor and global color table blocks
/// </summary>
/// <param name="stream">The stream containing image data. </param>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(gifMetadata))]
private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream)
{
this.stream = stream;
// Skip the identifier
this.stream.Skip(6);
this.ReadLogicalScreenDescriptor();
stream.Skip(6);
this.ReadLogicalScreenDescriptor(stream);
ImageMetadata meta = new();

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@ -93,7 +92,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
IndexedImageFrame<TPixel> quantized;
IndexedImageFrame<TPixel>? quantized;
using (IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration))
{
if (useGlobalTable)
@ -129,7 +128,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
this.WriteComments(gifMetadata, stream);
// Write application extensions.
XmpProfile xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
XmpProfile? xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile);
}
@ -152,8 +151,8 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
// Gather the metadata for this frame.
ImageFrame<TPixel> frame = image.Frames[i];
ImageFrameMetadata metadata = frame.Metadata;
bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata frameMetadata);
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata.ColorTableMode == GifColorTableMode.Local);
bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata? frameMetadata);
bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata!.ColorTableMode == GifColorTableMode.Local);
if (!useLocal && !hasPaletteQuantizer && i > 0)
{
@ -164,14 +163,16 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
paletteQuantizer = new(this.configuration, this.quantizer.Options, palette);
}
this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized, ref paletteQuantizer);
this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized!, ref paletteQuantizer);
// Clean up for the next run.
quantized.Dispose();
quantized = null;
}
paletteQuantizer.Dispose();
if (hasPaletteQuantizer)
{
paletteQuantizer.Dispose();
}
}
private void EncodeFrame<TPixel>(
@ -179,7 +180,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
ImageFrame<TPixel> frame,
int frameIndex,
bool useLocal,
GifFrameMetadata metadata,
GifFrameMetadata? metadata,
ref IndexedImageFrame<TPixel> quantized,
ref PaletteQuantizer<TPixel> paletteQuantizer)
where TPixel : unmanaged, IPixel<TPixel>
@ -190,7 +191,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
if (useLocal)
{
// Reassign using the current frame and details.
QuantizerOptions options = null;
QuantizerOptions? options = null;
int colorTableLength = metadata?.ColorTableLength ?? 0;
if (colorTableLength > 0)
{
@ -335,7 +336,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
/// <param name="frameCount">The frame count fo this image.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="xmpProfile">The XMP metadata profile. Null if profile is not to be written.</param>
private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile xmpProfile)
private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile? xmpProfile)
{
// Application Extension: Loop repeat count.
if (frameCount > 1 && repeatCount != 1)
@ -347,7 +348,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
// Application Extension: XMP Profile.
if (xmpProfile != null)
{
GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data);
GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data!);
this.WriteExtension(xmpExtension, stream);
}
}
@ -436,7 +437,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
private void WriteExtension<TGifExtension>(TGifExtension extension, Stream stream)
where TGifExtension : struct, IGifExtension
{
IMemoryOwner<byte> owner = null;
IMemoryOwner<byte>? owner = null;
Span<byte> extensionBuffer;
int extensionSize = extension.ContentLength;

3
src/ImageSharp/Formats/Gif/MetadataExtensions.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Metadata;
@ -37,5 +38,5 @@ public static partial class MetadataExtensions
/// <returns>
/// <see langword="true"/> if the gif frame metadata exists; otherwise, <see langword="false"/>.
/// </returns>
public static bool TryGetGifMetadata(this ImageFrameMetadata source, out GifFrameMetadata metadata) => source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out GifFrameMetadata? metadata) => source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
}

3
src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -45,7 +44,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
/// <summary>
/// The <see cref="ImageMetadata"/> decoded by this decoder instance.
/// </summary>
private ImageMetadata metadata;
private ImageMetadata? metadata;
/// <summary>
/// Initializes a new instance of the <see cref="PbmDecoderCore" /> class.

34
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -35,6 +36,10 @@ internal static class AverageFilter
{
DecodeSse2(scanline, previousScanline);
}
else if (AdvSimd.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline, previousScanline);
}
else
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
@ -48,7 +53,7 @@ internal static class AverageFilter
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
Vector128<byte> d = Vector128<byte>.Zero;
var ones = Vector128.Create((byte)1);
Vector128<byte> ones = Vector128.Create((byte)1);
int rb = scanline.Length;
nint offset = 1;
@ -75,6 +80,33 @@ internal static class AverageFilter
}
}
public static void DecodeArm(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
Vector64<byte> d = Vector64<byte>.Zero;
int rb = scanline.Length;
int offset = 1;
const int bytesPerBatch = 4;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector64<byte> a = d;
Vector64<byte> b = Vector64.CreateScalar(Unsafe.As<byte, int>(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte();
d = Vector64.CreateScalar(Unsafe.As<byte, int>(ref scanRef)).AsByte();
Vector64<byte> avg = AdvSimd.FusedAddHalving(a, b);
d = AdvSimd.Add(d, avg);
Unsafe.As<byte, int>(ref scanRef) = d.AsInt32().ToScalar();
rb -= bytesPerBatch;
offset += bytesPerBatch;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
{

79
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -5,6 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -38,6 +39,10 @@ internal static class PaethFilter
{
DecodeSse41(scanline, previousScanline);
}
else if (AdvSimd.Arm64.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline, previousScanline);
}
else
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
@ -99,6 +104,80 @@ internal static class PaethFilter
}
}
public static void DecodeArm(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
Vector128<byte> b = Vector128<byte>.Zero;
Vector128<byte> d = Vector128<byte>.Zero;
int rb = scanline.Length;
nint offset = 1;
const int bytesPerBatch = 4;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> c = b;
Vector128<byte> a = d;
b = AdvSimd.Arm64.ZipLow(
Vector128.CreateScalar(Unsafe.As<byte, int>(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte(),
Vector128<byte>.Zero).AsByte();
d = AdvSimd.Arm64.ZipLow(
Vector128.CreateScalar(Unsafe.As<byte, int>(ref scanRef)).AsByte(),
Vector128<byte>.Zero).AsByte();
// (p-a) == (a+b-c - a) == (b-c)
Vector128<short> pa = AdvSimd.Subtract(b.AsInt16(), c.AsInt16());
// (p-b) == (a+b-c - b) == (a-c)
Vector128<short> pb = AdvSimd.Subtract(a.AsInt16(), c.AsInt16());
// (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c)
Vector128<short> pc = AdvSimd.Add(pa.AsInt16(), pb.AsInt16());
pa = AdvSimd.Abs(pa.AsInt16()).AsInt16(); /* |p-a| */
pb = AdvSimd.Abs(pb.AsInt16()).AsInt16(); /* |p-b| */
pc = AdvSimd.Abs(pc.AsInt16()).AsInt16(); /* |p-c| */
Vector128<short> smallest = AdvSimd.Min(pc, AdvSimd.Min(pa, pb));
// Paeth breaks ties favoring a over b over c.
Vector128<byte> mask = BlendVariable(c, b, AdvSimd.CompareEqual(smallest, pb).AsByte());
Vector128<byte> nearest = BlendVariable(mask, a, AdvSimd.CompareEqual(smallest, pa).AsByte());
d = AdvSimd.Add(d, nearest);
Vector64<byte> e = AdvSimd.ExtractNarrowingSaturateUnsignedLower(d.AsInt16());
Unsafe.As<byte, int>(ref scanRef) = Vector128.Create(e, e).AsInt32().ToScalar();
rb -= bytesPerBatch;
offset += bytesPerBatch;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector128<byte> BlendVariable(Vector128<byte> a, Vector128<byte> b, Vector128<byte> c)
{
// Equivalent of Sse41.BlendVariable:
// Blend packed 8-bit integers from a and b using mask, and store the results in
// dst.
//
// FOR j := 0 to 15
// i := j*8
// IF mask[i+7]
// dst[i+7:i] := b[i+7:i]
// ELSE
// dst[i+7:i] := a[i+7:i]
// FI
// ENDFOR
//
// Use a signed shift right to create a mask with the sign bit.
Vector128<short> mask = AdvSimd.ShiftRightArithmetic(c.AsInt16(), 7);
return AdvSimd.BitwiseSelect(mask, b.AsInt16(), a.AsInt16()).AsByte();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
{

29
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -5,6 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -29,6 +30,10 @@ internal static class SubFilter
{
DecodeSse2(scanline);
}
else if (AdvSimd.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline);
}
else
{
DecodeScalar(scanline, bytesPerPixel);
@ -58,6 +63,30 @@ internal static class SubFilter
}
}
public static void DecodeArm(Span<byte> scanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
Vector64<byte> d = Vector64<byte>.Zero;
int rb = scanline.Length;
int offset = 1;
const int bytesPerBatch = 4;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector64<byte> a = d;
d = Vector64.CreateScalar(Unsafe.As<byte, int>(ref scanRef)).AsByte();
d = AdvSimd.Add(d, a);
Unsafe.As<byte, int>(ref scanRef) = d.AsInt32().ToScalar();
rb -= bytesPerBatch;
offset += bytesPerBatch;
}
}
private static void DecodeScalar(Span<byte> scanline, int bytesPerPixel)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);

46
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -5,6 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -34,6 +35,10 @@ internal static class UpFilter
{
DecodeSse2(scanline, previousScanline);
}
else if (AdvSimd.IsSupported)
{
DecodeArm(scanline, previousScanline);
}
else
{
DecodeScalar(scanline, previousScanline);
@ -51,11 +56,10 @@ internal static class UpFilter
while (rb >= Vector256<byte>.Count)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector256<byte> current = Unsafe.As<byte, Vector256<byte>>(ref scanRef);
Vector256<byte> prior = Unsafe.As<byte, Vector256<byte>>(ref scanRef);
Vector256<byte> up = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref prevBaseRef, offset));
Vector256<byte> sum = Avx2.Add(up, current);
Unsafe.As<byte, Vector256<byte>>(ref scanRef) = sum;
Unsafe.As<byte, Vector256<byte>>(ref scanRef) = Avx2.Add(up, prior);
offset += Vector256<byte>.Count;
rb -= Vector256<byte>.Count;
@ -82,11 +86,10 @@ internal static class UpFilter
while (rb >= Vector128<byte>.Count)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> current = Unsafe.As<byte, Vector128<byte>>(ref scanRef);
Vector128<byte> prior = Unsafe.As<byte, Vector128<byte>>(ref scanRef);
Vector128<byte> up = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref prevBaseRef, offset));
Vector128<byte> sum = Sse2.Add(up, current);
Unsafe.As<byte, Vector128<byte>>(ref scanRef) = sum;
Unsafe.As<byte, Vector128<byte>>(ref scanRef) = Sse2.Add(up, prior);
offset += Vector128<byte>.Count;
rb -= Vector128<byte>.Count;
@ -102,6 +105,37 @@ internal static class UpFilter
}
}
private static void DecodeArm(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Up(x) + Prior(x)
int rb = scanline.Length;
nint offset = 1;
const int bytesPerBatch = 16;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> prior = Unsafe.As<byte, Vector128<byte>>(ref scanRef);
Vector128<byte> up = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref prevBaseRef, offset));
Unsafe.As<byte, Vector128<byte>>(ref scanRef) = AdvSimd.Add(prior, up);
offset += bytesPerBatch;
rb -= bytesPerBatch;
}
// Handle left over.
for (nint i = offset; i < scanline.Length; i++)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset);
byte above = Unsafe.Add(ref prevBaseRef, offset);
scan = (byte)(scan + above);
offset++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline)
{

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

@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -29,12 +29,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// <summary>
/// The metadata.
/// </summary>
private ImageMetadata metadata;
private ImageMetadata? metadata;
/// <summary>
/// The tga specific metadata.
/// </summary>
private TgaMetadata tgaMetadata;
private TgaMetadata? tgaMetadata;
/// <summary>
/// The file header containing general information about the image.
@ -46,11 +46,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The stream to decode from.
/// </summary>
private BufferedReadStream currentStream;
/// <summary>
/// Indicates whether there is a alpha channel present.
/// </summary>
@ -80,7 +75,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
try
{
TgaImageOrigin origin = this.ReadFileHeader(stream);
this.currentStream.Skip(this.fileHeader.IdLength);
stream.Skip(this.fileHeader.IdLength);
// Parse the color map, if present.
if (this.fileHeader.ColorMapType is not 0 and not 1)
@ -93,7 +88,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
throw new UnknownImageFormatException("Width or height cannot be 0");
}
var image = Image.CreateUninitialized<TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Image<TPixel> image = Image.CreateUninitialized<TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1)
@ -113,7 +108,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
using (IMemoryOwner<byte> palette = this.memoryAllocator.Allocate<byte>(colorMapSizeInBytes, AllocationOptions.Clean))
{
Span<byte> paletteSpan = palette.GetSpan();
int bytesRead = this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
int bytesRead = stream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
if (bytesRead != colorMapSizeInBytes)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map");
@ -122,6 +117,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.ImageType == TgaImageType.RleColorMapped)
{
this.ReadPalettedRle(
stream,
this.fileHeader.Width,
this.fileHeader.Height,
pixels,
@ -132,6 +128,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
else
{
this.ReadPaletted(
stream,
this.fileHeader.Width,
this.fileHeader.Height,
pixels,
@ -148,7 +145,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.CMapLength > 0)
{
int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
stream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
}
switch (this.fileHeader.PixelDepth)
@ -156,11 +153,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 8:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin);
this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin);
}
else
{
this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
this.ReadMonoChrome(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@ -169,11 +166,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 16:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin);
this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin);
}
else
{
this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
this.ReadBgra16(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@ -181,11 +178,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 24:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin);
this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin);
}
else
{
this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
this.ReadBgr24(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@ -193,11 +190,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 32:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin);
this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin);
}
else
{
this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
this.ReadBgra32(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@ -219,13 +216,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image with a palette.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="palette">The color palette.</param>
/// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
/// <param name="origin">The image origin.</param>
private void ReadPaletted<TPixel>(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>
{
TPixel color = default;
@ -243,14 +241,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@ -261,14 +259,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@ -279,14 +277,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@ -299,48 +297,47 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a run length encoded TGA image with a palette.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="palette">The color palette.</param>
/// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
/// <param name="origin">The image origin.</param>
private void ReadPalettedRle<TPixel>(int width, int height, Buffer2D<TPixel> pixels, Span<byte> palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
private void ReadPalettedRle<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, Span<byte> palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel>
{
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean))
{
TPixel color = default;
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1);
using IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean);
TPixel color = default;
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1);
for (int y = 0; y < height; y++)
for (int y = 0; y < height; y++)
{
int newY = InvertY(y, height, origin);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
int rowStartIdx = y * width;
for (int x = 0; x < width; x++)
{
int newY = InvertY(y, height, origin);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
int rowStartIdx = y * width;
for (int x = 0; x < width; x++)
int idx = rowStartIdx + x;
switch (colorMapPixelSizeInBytes)
{
int idx = rowStartIdx + x;
switch (colorMapPixelSizeInBytes)
{
case 1:
color.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
case 2:
this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
break;
case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
case 4:
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
}
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
case 1:
color.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
case 2:
this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
break;
case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
case 4:
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
}
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
}
}
}
@ -349,11 +346,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed monochrome TGA image.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">the image origin.</param>
private void ReadMonoChrome<TPixel>(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>
{
bool invertX = InvertX(origin);
@ -366,7 +364,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--)
{
this.ReadL8Pixel(color, x, pixelSpan);
ReadL8Pixel(stream, color, x, pixelSpan);
}
}
@ -380,14 +378,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
this.ReadL8Row(width, pixels, rowSpan, y);
this.ReadL8Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
this.ReadL8Row(width, pixels, rowSpan, y);
this.ReadL8Row(stream, width, pixels, rowSpan, y);
}
}
}
@ -396,11 +394,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 16 bit.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</param>
private void ReadBgra16<TPixel>(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>
{
TPixel color = default;
@ -417,7 +416,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 2);
int bytesRead = stream.Read(this.scratchBuffer, 0, 2);
if (bytesRead != 2)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -442,7 +441,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
else
{
int bytesRead = this.currentStream.Read(rowSpan);
int bytesRead = stream.Read(rowSpan);
if (bytesRead != rowSpan.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -473,11 +472,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 24 bit.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</param>
private void ReadBgr24<TPixel>(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>
{
bool invertX = InvertX(origin);
@ -490,7 +490,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--)
{
this.ReadBgr24Pixel(color, x, pixelSpan);
this.ReadBgr24Pixel(stream, color, x, pixelSpan);
}
}
@ -505,14 +505,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
this.ReadBgr24Row(width, pixels, rowSpan, y);
this.ReadBgr24Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
this.ReadBgr24Row(width, pixels, rowSpan, y);
this.ReadBgr24Row(stream, width, pixels, rowSpan, y);
}
}
}
@ -521,15 +521,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 32 bit.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</param>
private void ReadBgra32<TPixel>(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>
{
TPixel color = default;
bool invertX = InvertX(origin);
Guard.NotNull(this.tgaMetadata);
if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX)
{
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0);
@ -539,14 +543,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
this.ReadBgra32Row(width, pixels, rowSpan, y);
this.ReadBgra32Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
this.ReadBgra32Row(width, pixels, rowSpan, y);
this.ReadBgra32Row(stream, width, pixels, rowSpan, y);
}
}
@ -561,14 +565,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
this.ReadBgra32Pixel(x, color, pixelRow);
this.ReadBgra32Pixel(stream, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
this.ReadBgra32Pixel(x, color, pixelRow);
this.ReadBgra32Pixel(stream, x, color, pixelRow);
}
}
}
@ -578,70 +582,72 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a run length encoded TGA image.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="origin">The image origin.</param>
private void ReadRle<TPixel>(int width, int height, Buffer2D<TPixel> pixels, int bytesPerPixel, TgaImageOrigin origin)
private void ReadRle<TPixel>(BufferedReadStream stream, int width, int height, Buffer2D<TPixel> pixels, int bytesPerPixel, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel<TPixel>
{
TPixel color = default;
Guard.NotNull(this.tgaMetadata);
byte alphaBits = this.tgaMetadata.AlphaChannelBits;
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
using IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height * bytesPerPixel, AllocationOptions.Clean);
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel);
for (int y = 0; y < height; y++)
{
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle(width, height, bufferSpan, bytesPerPixel);
for (int y = 0; y < height; y++)
int newY = InvertY(y, height, origin);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
int rowStartIdx = y * width * bytesPerPixel;
for (int x = 0; x < width; x++)
{
int newY = InvertY(y, height, origin);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
int rowStartIdx = y * width * bytesPerPixel;
for (int x = 0; x < width; x++)
int idx = rowStartIdx + (x * bytesPerPixel);
switch (bytesPerPixel)
{
int idx = rowStartIdx + (x * bytesPerPixel);
switch (bytesPerPixel)
{
case 1:
color.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx]));
break;
case 2:
if (!this.hasAlpha)
{
// Set alpha value to 1, to treat it as opaque for Bgra5551.
bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
}
if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
{
color.FromLa16(Unsafe.As<byte, La16>(ref bufferSpan[idx]));
}
else
{
color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref bufferSpan[idx]));
}
break;
case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
break;
case 4:
if (this.hasAlpha)
{
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref bufferSpan[idx]));
}
else
{
byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha));
}
break;
}
case 1:
color.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx]));
break;
case 2:
if (!this.hasAlpha)
{
// Set alpha value to 1, to treat it as opaque for Bgra5551.
bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
}
if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
{
color.FromLa16(Unsafe.As<byte, La16>(ref bufferSpan[idx]));
}
else
{
color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref bufferSpan[idx]));
}
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
break;
case 3:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
break;
case 4:
if (this.hasAlpha)
{
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref bufferSpan[idx]));
}
else
{
byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha));
}
break;
}
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
}
}
}
@ -658,10 +664,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadL8Row<TPixel>(int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
private void ReadL8Row<TPixel>(BufferedReadStream stream, int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesRead = this.currentStream.Read(row);
int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -672,19 +678,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadL8Pixel<TPixel>(TPixel color, int x, Span<TPixel> pixelSpan)
private static void ReadL8Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan)
where TPixel : unmanaged, IPixel<TPixel>
{
byte pixelValue = (byte)this.currentStream.ReadByte();
byte pixelValue = (byte)stream.ReadByte();
color.FromL8(Unsafe.As<byte, L8>(ref pixelValue));
pixelSpan[x] = color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgr24Pixel<TPixel>(TPixel color, int x, Span<TPixel> pixelSpan)
private void ReadBgr24Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 3);
int bytesRead = stream.Read(this.scratchBuffer, 0, 3);
if (bytesRead != 3)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel");
@ -695,10 +701,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgr24Row<TPixel>(int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
private void ReadBgr24Row<TPixel>(BufferedReadStream stream, int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesRead = this.currentStream.Read(row);
int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -709,25 +715,27 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgra32Pixel<TPixel>(int x, TPixel color, Span<TPixel> pixelRow)
private void ReadBgra32Pixel<TPixel>(BufferedReadStream stream, int x, TPixel color, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 4);
int bytesRead = stream.Read(this.scratchBuffer, 0, 4);
if (bytesRead != 4)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel");
}
Guard.NotNull(this.tgaMetadata);
byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3];
color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha));
pixelRow[x] = color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgra32Row<TPixel>(int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
private void ReadBgra32Row<TPixel>(BufferedReadStream stream, int width, Buffer2D<TPixel> pixels, Span<byte> row, int y)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesRead = this.currentStream.Read(row);
int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -738,10 +746,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadPalettedBgra16Pixel<TPixel>(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, TPixel color, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel>
{
int colorIndex = this.currentStream.ReadByte();
int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -768,10 +776,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadPalettedBgr24Pixel<TPixel>(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, TPixel color, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel>
{
int colorIndex = this.currentStream.ReadByte();
int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -782,10 +790,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadPalettedBgra32Pixel<TPixel>(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, TPixel color, Span<TPixel> pixelRow)
where TPixel : unmanaged, IPixel<TPixel>
{
int colorIndex = this.currentStream.ReadByte();
int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -798,25 +806,26 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// <summary>
/// Produce uncompressed tga data from a run length encoded stream.
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="bytesPerPixel">The bytes used per pixel.</param>
private void UncompressRle(int width, int height, Span<byte> buffer, int bytesPerPixel)
private void UncompressRle(BufferedReadStream stream, int width, int height, Span<byte> buffer, int bytesPerPixel)
{
int uncompressedPixels = 0;
Span<byte> pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel);
int totalPixels = width * height;
while (uncompressedPixels < totalPixels)
{
byte runLengthByte = (byte)this.currentStream.ReadByte();
byte runLengthByte = (byte)stream.ReadByte();
// The high bit of a run length packet is set to 1.
int highBit = runLengthByte >> 7;
if (highBit == 1)
{
int runLength = runLengthByte & 127;
int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel);
int bytesRead = stream.Read(pixel, 0, bytesPerPixel);
if (bytesRead != bytesPerPixel)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@ -836,7 +845,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
int bufferIdx = uncompressedPixels * bytesPerPixel;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++)
{
int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel);
int bytesRead = stream.Read(pixel, 0, bytesPerPixel);
if (bytesRead != bytesPerPixel)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@ -917,13 +926,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <returns>The image origin.</returns>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(tgaMetadata))]
private TgaImageOrigin ReadFileHeader(BufferedReadStream stream)
{
this.currentStream = stream;
Span<byte> buffer = stackalloc byte[TgaFileHeader.Size];
this.currentStream.Read(buffer, 0, TgaFileHeader.Size);
stream.Read(buffer, 0, TgaFileHeader.Size);
this.fileHeader = TgaFileHeader.Parse(buffer);
this.metadata = new ImageMetadata();
this.tgaMetadata = this.metadata.GetTgaMetadata();
@ -939,7 +948,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
this.hasAlpha = alphaBits > 0;
// Bits 4 and 5 describe the image origin.
var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
return origin;
return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
}
}

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Buffers.Binary;
@ -23,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// Reusable buffer for writing data.
/// </summary>
@ -68,7 +62,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
TgaMetadata tgaMetadata = metadata.GetTgaMetadata();
this.bitsPerPixel ??= tgaMetadata.BitsPerPixel;
@ -124,7 +117,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
}
else
{
this.WriteImage(stream, image.Frames.RootFrame);
this.WriteImage(image.GetConfiguration(), stream, image.Frames.RootFrame);
}
stream.Flush();
@ -134,30 +127,31 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
private void WriteImage<TPixel>(Configuration configuration, Stream stream, ImageFrame<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> pixels = image.PixelBuffer;
switch (this.bitsPerPixel)
{
case TgaBitsPerPixel.Pixel8:
this.Write8Bit(stream, pixels);
this.Write8Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel16:
this.Write16Bit(stream, pixels);
this.Write16Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
this.Write24Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
this.Write32Bit(configuration, stream, pixels);
break;
}
}
@ -227,7 +221,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
case TgaBitsPerPixel.Pixel16:
Bgra5551 bgra5551 = new(color.ToVector4());
BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
BinaryPrimitives.WriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
stream.WriteByte(this.buffer[0]);
stream.WriteByte(this.buffer[1]);
@ -321,9 +315,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 8bit pixels uncompressed to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write8Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write8Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 1);
@ -333,7 +328,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@ -345,9 +340,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 16bit pixels uncompressed to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write16Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write16Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 2);
@ -357,7 +353,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra5551Bytes(
this.configuration,
configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@ -369,9 +365,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 24bit pixels uncompressed to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write24Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 3);
@ -381,7 +378,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration,
configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@ -393,9 +390,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 32bit pixels uncompressed to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The global configuration.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
private void Write32Bit<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4);
@ -405,7 +403,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(
this.configuration,
configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);

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

@ -67,6 +67,11 @@ internal static class HorizontalPredictor
{
ApplyHorizontalPrediction8Bit(rows, width);
}
else if (bitsPerPixel == 16)
{
// Assume rows are L16 grayscale since that's currently the only way 16 bits is supported by encoder
ApplyHorizontalPrediction16Bit(rows, width);
}
else if (bitsPerPixel == 24)
{
ApplyHorizontalPrediction24Bit(rows, width);
@ -102,6 +107,32 @@ internal static class HorizontalPredictor
}
}
/// <summary>
/// Applies a horizontal predictor to the L16 row.
/// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next.
/// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus
/// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly.
/// </summary>
/// <param name="rows">The L16 pixel rows.</param>
/// <param name="width">The width.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static void ApplyHorizontalPrediction16Bit(Span<byte> rows, int width)
{
DebugGuard.IsTrue(rows.Length % width == 0, "Values must be equals");
int height = rows.Length / width;
for (int y = 0; y < height; y++)
{
Span<byte> rowSpan = rows.Slice(y * width, width);
Span<L16> rowL16 = MemoryMarshal.Cast<byte, L16>(rowSpan);
for (int x = rowL16.Length - 1; x >= 1; x--)
{
ushort val = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue);
rowL16[x].PackedValue = val;
}
}
}
/// <summary>
/// Applies a horizontal predictor to a gray pixel row.
/// </summary>

5
src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs

@ -73,6 +73,11 @@ internal static class TiffConstants
/// </summary>
public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0);
/// <summary>
/// The bits per sample for 16-bit grayscale images.
/// </summary>
public static readonly TiffBitsPerSample BitsPerSample16Bit = new TiffBitsPerSample(16, 0, 0);
/// <summary>
/// The bits per sample for color images with 8 bits for each color channel.
/// </summary>

2
src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs

@ -54,7 +54,7 @@ public enum TiffBitsPerPixel
/// <summary>
/// 16 bits per pixel, for gray images.
///
/// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead.
/// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 16 bits grayscale instead.
/// </summary>
Bit16 = 16,

20
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -376,11 +376,14 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
case TiffBitsPerPixel.Bit8:
this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break;
case TiffBitsPerPixel.Bit16:
// Assume desire to encode as L16 grayscale
this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break;
case TiffBitsPerPixel.Bit6:
case TiffBitsPerPixel.Bit10:
case TiffBitsPerPixel.Bit12:
case TiffBitsPerPixel.Bit14:
case TiffBitsPerPixel.Bit16:
case TiffBitsPerPixel.Bit30:
case TiffBitsPerPixel.Bit36:
case TiffBitsPerPixel.Bit42:
@ -413,13 +416,20 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return;
}
// At the moment only 8 and 32 bits per pixel can be preserved by the tiff encoder.
// At the moment only 8, 16 and 32 bits per pixel can be preserved by the tiff encoder.
if (inputBitsPerPixel == 8)
{
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
return;
}
if (inputBitsPerPixel == 16)
{
// Assume desire to encode as L16 grayscale
this.SetEncoderOptions(TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
return;
}
this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor);
return;
}
@ -434,6 +444,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return;
}
if (inputBitsPerPixel == 16)
{
this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor);
return;
}
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor);
return;

20
src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs

@ -343,20 +343,20 @@ internal class TiffEncoderEntriesCollector
return TiffConstants.BitsPerSampleRgb8Bit.ToArray();
case TiffPhotometricInterpretation.WhiteIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1)
return encoder.BitsPerPixel switch
{
return TiffConstants.BitsPerSample1Bit.ToArray();
}
return TiffConstants.BitsPerSample8Bit.ToArray();
TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
};
case TiffPhotometricInterpretation.BlackIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1)
return encoder.BitsPerPixel switch
{
return TiffConstants.BitsPerSample1Bit.ToArray();
}
return TiffConstants.BitsPerSample8Bit.ToArray();
TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
};
default:
return TiffConstants.BitsPerSampleRgb8Bit.ToArray();

9
src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs

@ -27,12 +27,13 @@ internal static class TiffColorWriterFactory
return new TiffPaletteWriter<TPixel>(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
if (bitsPerPixel == 1)
return bitsPerPixel switch
{
return new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
}
1 => new TiffBiColorWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector),
16 => new TiffGrayL16Writer<TPixel>(image, memoryAllocator, configuration, entriesCollector),
_ => new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector)
};
return new TiffGrayWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
default:
return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector);
}

22
src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Writers;
internal sealed class TiffGrayL16Writer<TPixel> : TiffCompositeColorWriter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
public TiffGrayL16Writer(ImageFrame<TPixel> image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
: base(image, memoryAllocator, configuration, entriesCollector)
{
}
/// <inheritdoc />
public override int BitsPerPixel => 16;
/// <inheritdoc />
protected override void EncodePixels(Span<TPixel> pixels, Span<byte> buffer) => PixelOperations<TPixel>.Instance.ToL16Bytes(this.Configuration, pixels, buffer, pixels.Length);
}

142
src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs

@ -5,6 +5,7 @@ using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
// ReSharper disable InconsistentNaming
@ -14,7 +15,7 @@ internal static class LossyUtils
{
// Note: method name in libwebp reference implementation is called VP8SSE16x16.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b)
public static int Vp8_Sse16x16(Span<byte> a, Span<byte> b)
{
if (Avx2.IsSupported)
{
@ -26,12 +27,17 @@ internal static class LossyUtils
return Vp8_Sse16xN_Sse2(a, b, 8);
}
if (AdvSimd.IsSupported)
{
return Vp8_Sse16x16_Neon(a, b);
}
return Vp8_SseNxN(a, b, 16, 16);
}
// Note: method name in libwebp reference implementation is called VP8SSE16x8.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b)
public static int Vp8_Sse16x8(Span<byte> a, Span<byte> b)
{
if (Avx2.IsSupported)
{
@ -43,12 +49,17 @@ internal static class LossyUtils
return Vp8_Sse16xN_Sse2(a, b, 4);
}
if (AdvSimd.IsSupported)
{
return Vp8_Sse16x8_Neon(a, b);
}
return Vp8_SseNxN(a, b, 16, 8);
}
// Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse4X4(Span<byte> a, Span<byte> b)
public static int Vp8_Sse4x4(Span<byte> a, Span<byte> b)
{
if (Avx2.IsSupported)
{
@ -77,8 +88,8 @@ internal static class LossyUtils
Vector256<byte> b01s = Avx2.UnpackLow(b01.AsByte(), Vector256<byte>.Zero);
// subtract, square and accumulate.
Vector256<byte> d0 = Avx2.SubtractSaturate(a01s, b01s);
Vector256<int> e0 = Avx2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16());
Vector256<short> d0 = Avx2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16());
Vector256<int> e0 = Avx2.MultiplyAddAdjacent(d0, d0);
return Numerics.ReduceSum(e0);
}
@ -110,15 +121,20 @@ internal static class LossyUtils
Vector128<byte> b23s = Sse2.UnpackLow(b23.AsByte(), Vector128<byte>.Zero);
// subtract, square and accumulate.
Vector128<byte> d0 = Sse2.SubtractSaturate(a01s, b01s);
Vector128<byte> d1 = Sse2.SubtractSaturate(a23s, b23s);
Vector128<int> e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16());
Vector128<int> e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16());
Vector128<short> d0 = Sse2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16());
Vector128<short> d1 = Sse2.SubtractSaturate(a23s.AsInt16(), b23s.AsInt16());
Vector128<int> e0 = Sse2.MultiplyAddAdjacent(d0, d0);
Vector128<int> e1 = Sse2.MultiplyAddAdjacent(d1, d1);
Vector128<int> sum = Sse2.Add(e0, e1);
return Numerics.ReduceSum(sum);
}
if (AdvSimd.IsSupported)
{
return Vp8_Sse4x4_Neon(a, b);
}
return Vp8_SseNxN(a, b, 4, 4);
}
@ -126,18 +142,16 @@ internal static class LossyUtils
public static int Vp8_SseNxN(Span<byte> a, Span<byte> b, int w, int h)
{
int count = 0;
int aOffset = 0;
int bOffset = 0;
int offset = 0;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int diff = a[aOffset + x] - b[bOffset + x];
int diff = a[offset + x] - b[offset + x];
count += diff * diff;
}
aOffset += WebpConstants.Bps;
bOffset += WebpConstants.Bps;
offset += WebpConstants.Bps;
}
return count;
@ -201,6 +215,106 @@ internal static class LossyUtils
return Numerics.ReduceSum(sum);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static unsafe int Vp8_Sse16x16_Neon(Span<byte> a, Span<byte> b)
{
Vector128<uint> sum = Vector128<uint>.Zero;
fixed (byte* aRef = &MemoryMarshal.GetReference(a))
{
fixed (byte* bRef = &MemoryMarshal.GetReference(b))
{
for (int y = 0; y < 16; y++)
{
sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum);
}
}
}
#if NET7_0_OR_GREATER
return (int)Vector128.Sum(sum);
#else
return Numerics.ReduceSumArm(sum);
#endif
}
[MethodImpl(InliningOptions.ShortMethod)]
private static unsafe int Vp8_Sse16x8_Neon(Span<byte> a, Span<byte> b)
{
Vector128<uint> sum = Vector128<uint>.Zero;
fixed (byte* aRef = &MemoryMarshal.GetReference(a))
{
fixed (byte* bRef = &MemoryMarshal.GetReference(b))
{
for (int y = 0; y < 8; y++)
{
sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum);
}
}
}
#if NET7_0_OR_GREATER
return (int)Vector128.Sum(sum);
#else
return Numerics.ReduceSumArm(sum);
#endif
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8_Sse4x4_Neon(Span<byte> a, Span<byte> b)
{
Vector128<byte> a0 = Load4x4Neon(a).AsByte();
Vector128<byte> b0 = Load4x4Neon(b).AsByte();
Vector128<byte> absDiff = AdvSimd.AbsoluteDifference(a0, b0);
Vector64<byte> absDiffLower = absDiff.GetLower().AsByte();
Vector64<byte> absDiffUpper = absDiff.GetUpper().AsByte();
Vector128<ushort> prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower);
Vector128<ushort> prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper);
// pair-wise adds and widen.
Vector128<uint> sum1 = AdvSimd.AddPairwiseWidening(prod1);
Vector128<uint> sum2 = AdvSimd.AddPairwiseWidening(prod2);
Vector128<uint> sum = AdvSimd.Add(sum1, sum2);
#if NET7_0_OR_GREATER
return (int)Vector128.Sum(sum);
#else
return Numerics.ReduceSumArm(sum);
#endif
}
// Load all 4x4 pixels into a single Vector128<uint>
[MethodImpl(InliningOptions.ShortMethod)]
private static unsafe Vector128<uint> Load4x4Neon(Span<byte> src)
{
fixed (byte* srcRef = &MemoryMarshal.GetReference(src))
{
Vector128<uint> output = Vector128<uint>.Zero;
output = AdvSimd.LoadAndInsertScalar(output, 0, (uint*)srcRef);
output = AdvSimd.LoadAndInsertScalar(output, 1, (uint*)(srcRef + WebpConstants.Bps));
output = AdvSimd.LoadAndInsertScalar(output, 2, (uint*)(srcRef + (WebpConstants.Bps * 2)));
output = AdvSimd.LoadAndInsertScalar(output, 3, (uint*)(srcRef + (WebpConstants.Bps * 3)));
return output;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private static unsafe Vector128<uint> AccumulateSSE16Neon(byte* a, byte* b, Vector128<uint> sum)
{
Vector128<byte> a0 = AdvSimd.LoadVector128(a);
Vector128<byte> b0 = AdvSimd.LoadVector128(b);
Vector128<byte> absDiff = AdvSimd.AbsoluteDifference(a0, b0);
Vector64<byte> absDiffLower = absDiff.GetLower();
Vector64<byte> absDiffUpper = absDiff.GetUpper();
Vector128<ushort> prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower);
Vector128<ushort> prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper);
// pair-wise adds and widen.
Vector128<uint> sum1 = AdvSimd.AddPairwiseWidening(prod1);
Vector128<uint> sum2 = AdvSimd.AddPairwiseWidening(prod2);
return AdvSimd.Add(sum, AdvSimd.Add(sum1, sum2));
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<int> SubtractAndAccumulate(Vector128<byte> a, Vector128<byte> b)
{

12
src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs

@ -53,7 +53,7 @@ internal static unsafe class QuantEnc
rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode);
// Measure RD-score.
rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst);
rdCur.D = LossyUtils.Vp8_Sse16x16(src, tmpDst);
rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0;
rdCur.H = WebpConstants.Vp8FixedCostsI16[mode];
rdCur.R = it.GetCostLuma16(rdCur, proba, res);
@ -145,7 +145,7 @@ internal static unsafe class QuantEnc
rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode);
// Compute RD-score.
rdTmp.D = LossyUtils.Vp8_Sse4X4(src, tmpDst);
rdTmp.D = LossyUtils.Vp8_Sse4x4(src, tmpDst);
rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0;
rdTmp.H = modeCosts[mode];
@ -235,7 +235,7 @@ internal static unsafe class QuantEnc
rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode);
// Compute RD-score
rdUv.D = LossyUtils.Vp8_Sse16X8(src, tmpDst);
rdUv.D = LossyUtils.Vp8_Sse16x8(src, tmpDst);
rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas.
rdUv.H = WebpConstants.Vp8FixedCostsUv[mode];
rdUv.R = it.GetCostUv(rdUv, proba, res);
@ -389,7 +389,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]);
long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16);
long score = (LossyUtils.Vp8_Sse16x16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16);
if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit)
{
@ -436,7 +436,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumBModes; ++mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4);
long score = (LossyUtils.Vp8_Sse4x4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4);
if (score < bestI4Score)
{
bestI4Mode = mode;
@ -485,7 +485,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]);
long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv);
long score = (LossyUtils.Vp8_Sse16x8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv);
if (score < bestUvScore)
{
bestMode = mode;

41
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@ -19,7 +18,7 @@ internal sealed class ChunkedMemoryStream : Stream
private readonly MemoryAllocator allocator;
// Data
private MemoryChunk memoryChunk;
private MemoryChunk? memoryChunk;
// The total number of allocated chunks
private int chunkCount;
@ -31,13 +30,13 @@ internal sealed class ChunkedMemoryStream : Stream
private bool isDisposed;
// Current chunk to write to
private MemoryChunk writeChunk;
private MemoryChunk? writeChunk;
// Offset into chunk to write to
private int writeOffset;
// Current chunk to read from
private MemoryChunk readChunk;
private MemoryChunk? readChunk;
// Offset into chunk to read from
private int readOffset;
@ -48,8 +47,6 @@ internal sealed class ChunkedMemoryStream : Stream
/// <param name="allocator">The memory allocator.</param>
public ChunkedMemoryStream(MemoryAllocator allocator)
{
Guard.NotNull(allocator, nameof(allocator));
this.allocatorCapacity = allocator.GetBufferCapacityInBytes();
this.allocator = allocator;
}
@ -71,10 +68,10 @@ internal sealed class ChunkedMemoryStream : Stream
this.EnsureNotDisposed();
int length = 0;
MemoryChunk chunk = this.memoryChunk;
MemoryChunk? chunk = this.memoryChunk;
while (chunk != null)
{
MemoryChunk next = chunk.Next;
MemoryChunk? next = chunk.Next;
if (next != null)
{
length += chunk.Length;
@ -104,8 +101,8 @@ internal sealed class ChunkedMemoryStream : Stream
}
int pos = 0;
MemoryChunk chunk = this.memoryChunk;
while (chunk != this.readChunk)
MemoryChunk? chunk = this.memoryChunk;
while (chunk != this.readChunk && chunk is not null)
{
pos += chunk.Length;
chunk = chunk.Next;
@ -126,14 +123,14 @@ internal sealed class ChunkedMemoryStream : Stream
}
// Back up current position in case new position is out of range
MemoryChunk backupReadChunk = this.readChunk;
MemoryChunk? backupReadChunk = this.readChunk;
int backupReadOffset = this.readOffset;
this.readChunk = null;
this.readOffset = 0;
int leftUntilAtPos = (int)value;
MemoryChunk chunk = this.memoryChunk;
MemoryChunk? chunk = this.memoryChunk;
while (chunk != null)
{
if ((leftUntilAtPos < chunk.Length)
@ -365,6 +362,8 @@ internal sealed class ChunkedMemoryStream : Stream
this.writeOffset = 0;
}
Guard.NotNull(this.writeChunk);
Span<byte> chunkBuffer = this.writeChunk.Buffer.GetSpan();
int chunkSize = this.writeChunk.Length;
int count = buffer.Length;
@ -402,6 +401,8 @@ internal sealed class ChunkedMemoryStream : Stream
this.writeOffset = 0;
}
Guard.NotNull(this.writeChunk);
IMemoryOwner<byte> chunkBuffer = this.writeChunk.Buffer;
int chunkSize = this.writeChunk.Length;
@ -426,7 +427,7 @@ internal sealed class ChunkedMemoryStream : Stream
int length = (int)this.Length; // This will throw if stream is closed
byte[] copy = new byte[this.Length];
MemoryChunk backupReadChunk = this.readChunk;
MemoryChunk? backupReadChunk = this.readChunk;
int backupReadOffset = this.readOffset;
this.readChunk = this.memoryChunk;
@ -522,15 +523,14 @@ internal sealed class ChunkedMemoryStream : Stream
// available contiguous buffer size.
IMemoryOwner<byte> buffer = this.allocator.Allocate<byte>(Math.Min(this.allocatorCapacity, GetChunkSize(this.chunkCount++)));
return new MemoryChunk
return new MemoryChunk(buffer)
{
Buffer = buffer,
Next = null,
Length = buffer.Length()
};
}
private static void ReleaseMemoryChunks(MemoryChunk chunk)
private static void ReleaseMemoryChunks(MemoryChunk? chunk)
{
while (chunk != null)
{
@ -555,11 +555,13 @@ internal sealed class ChunkedMemoryStream : Stream
{
private bool isDisposed;
public IMemoryOwner<byte> Buffer { get; set; }
public MemoryChunk(IMemoryOwner<byte> buffer) => this.Buffer = buffer;
public IMemoryOwner<byte> Buffer { get; }
public MemoryChunk Next { get; set; }
public MemoryChunk? Next { get; set; }
public int Length { get; set; }
public int Length { get; init; }
private void Dispose(bool disposing)
{
@ -570,7 +572,6 @@ internal sealed class ChunkedMemoryStream : Stream
this.Buffer.Dispose();
}
this.Buffer = null;
this.isDisposed = true;
}
}

2
src/ImageSharp/Image.Decode.cs

@ -91,7 +91,7 @@ public abstract partial class Image
ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
}
return format!;
return format;
}
/// <summary>

6
src/ImageSharp/Image.cs

@ -28,9 +28,9 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv
/// <param name="pixelType">The pixel type information.</param>
/// <param name="metadata">The image metadata.</param>
/// <param name="size">The size in px units.</param>
protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size)
protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata? metadata, Size size)
: base(pixelType, size, metadata)
=> this.configuration = configuration ?? Configuration.Default;
=> this.configuration = configuration;
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
@ -43,7 +43,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv
internal Image(
Configuration configuration,
PixelTypeInfo pixelType,
ImageMetadata metadata,
ImageMetadata? metadata,
int width,
int height)
: this(configuration, pixelType, metadata, new Size(width, height))

4
src/ImageSharp/ImageInfo.cs

@ -18,7 +18,7 @@ public class ImageInfo
/// <param name="width">The width of the image in px units.</param>
/// <param name="height">The height of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata)
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata? metadata)
: this(pixelType, new(width, height), metadata)
{
}
@ -29,7 +29,7 @@ public class ImageInfo
/// <param name="pixelType">The pixel type information.</param>
/// <param name="size">The size of the image in px units.</param>
/// <param name="metadata">The image metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata metadata)
public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata? metadata)
{
this.PixelType = pixelType;
this.Metadata = metadata ?? new ImageMetadata();

4
src/ImageSharp/Image{TPixel}.cs

@ -77,7 +77,7 @@ public sealed class Image<TPixel> : Image
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</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, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
@ -128,7 +128,7 @@ public sealed class Image<TPixel> : Image
int width,
int height,
TPixel backgroundColor,
ImageMetadata metadata)
ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor);

15
src/ImageSharp/Metadata/ImageFrameMetadata.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -49,22 +48,22 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
/// <summary>
/// Gets or sets the Exif profile.
/// </summary>
public ExifProfile ExifProfile { get; set; }
public ExifProfile? ExifProfile { get; set; }
/// <summary>
/// Gets or sets the XMP profile.
/// </summary>
public XmpProfile XmpProfile { get; set; }
public XmpProfile? XmpProfile { get; set; }
/// <summary>
/// Gets or sets the ICC profile.
/// </summary>
public IccProfile IccProfile { get; set; }
public IccProfile? IccProfile { get; set; }
/// <summary>
/// Gets or sets the iptc profile.
/// </summary>
public IptcProfile IptcProfile { get; set; }
public IptcProfile? IptcProfile { get; set; }
/// <inheritdoc/>
public ImageFrameMetadata DeepClone() => new(this);
@ -83,7 +82,7 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
where TFormatMetadata : class
where TFormatFrameMetadata : class, IDeepCloneable
{
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta))
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta))
{
return (TFormatFrameMetadata)meta;
}
@ -107,11 +106,11 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
/// <returns>
/// <see langword="true"/> if the frame metadata exists for the specified key; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetFormatMetadata<TFormatMetadata, TFormatFrameMetadata>(IImageFormat<TFormatMetadata, TFormatFrameMetadata> key, out TFormatFrameMetadata metadata)
public bool TryGetFormatMetadata<TFormatMetadata, TFormatFrameMetadata>(IImageFormat<TFormatMetadata, TFormatFrameMetadata> key, out TFormatFrameMetadata? metadata)
where TFormatMetadata : class
where TFormatFrameMetadata : class, IDeepCloneable
{
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta))
if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta))
{
metadata = (TFormatFrameMetadata)meta;
return true;

39
src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs

@ -1,9 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using SixLabors.ImageSharp.Metadata.Profiles.IPTC;
@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc;
/// </summary>
public sealed class IptcProfile : IDeepCloneable<IptcProfile>
{
private Collection<IptcValue> values;
private readonly Collection<IptcValue> values = new();
private const byte IptcTagMarkerByte = 0x1c;
@ -30,7 +30,7 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
/// Initializes a new instance of the <see cref="IptcProfile"/> class.
/// </summary>
public IptcProfile()
: this((byte[])null)
: this((byte[]?)null)
{
}
@ -38,7 +38,7 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
/// Initializes a new instance of the <see cref="IptcProfile"/> class.
/// </summary>
/// <param name="data">The byte array to read the iptc profile from.</param>
public IptcProfile(byte[] data)
public IptcProfile(byte[]? data)
{
this.Data = data;
this.Initialize();
@ -53,14 +53,9 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
{
Guard.NotNull(other, nameof(other));
if (other.values != null)
foreach (IptcValue value in other.Values)
{
this.values = new Collection<IptcValue>();
foreach (IptcValue value in other.Values)
{
this.values.Add(value.DeepClone());
}
this.values.Add(value.DeepClone());
}
if (other.Data != null)
@ -78,19 +73,12 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
/// <summary>
/// Gets the byte data of the IPTC profile.
/// </summary>
public byte[] Data { get; private set; }
public byte[]? Data { get; private set; }
/// <summary>
/// Gets the values of this iptc profile.
/// </summary>
public IEnumerable<IptcValue> Values
{
get
{
this.Initialize();
return this.values;
}
}
public IEnumerable<IptcValue> Values => this.values;
/// <inheritdoc/>
public IptcProfile DeepClone() => new(this);
@ -121,8 +109,6 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
/// <returns>True when the value was found and removed.</returns>
public bool RemoveValue(IptcTag tag)
{
this.Initialize();
bool removed = false;
for (int i = this.values.Count - 1; i >= 0; i--)
{
@ -144,8 +130,6 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
/// <returns>True when the value was found and removed.</returns>
public bool RemoveValue(IptcTag tag, string value)
{
this.Initialize();
bool removed = false;
for (int i = this.values.Count - 1; i >= 0; i--)
{
@ -312,13 +296,6 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
private void Initialize()
{
if (this.values != null)
{
return;
}
this.values = new Collection<IptcValue>();
if (this.Data == null || this.Data[0] != IptcTagMarkerByte)
{
return;

10
src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Text;
@ -22,10 +21,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
other.data.AsSpan().CopyTo(this.data);
}
if (other.Encoding != null)
{
this.Encoding = (Encoding)other.Encoding.Clone();
}
this.encoding = (Encoding)other.Encoding.Clone();
this.Tag = other.Tag;
this.Strict = other.Strict;
@ -133,7 +129,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
/// </summary>
/// <param name="obj">The object to compare this <see cref="IptcValue"/> with.</param>
/// <returns>True when the specified object is equal to the current <see cref="IptcValue"/>.</returns>
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj))
{
@ -148,7 +144,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
/// </summary>
/// <param name="other">The iptc value to compare this <see cref="IptcValue"/> with.</param>
/// <returns>True when the specified iptc value is equal to the current <see cref="IptcValue"/>.</returns>
public bool Equals(IptcValue other)
public bool Equals(IptcValue? other)
{
if (other is null)
{

13
src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Diagnostics;
using System.Text;
using System.Xml.Linq;
@ -17,7 +17,7 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// Initializes a new instance of the <see cref="XmpProfile"/> class.
/// </summary>
public XmpProfile()
: this((byte[])null)
: this((byte[]?)null)
{
}
@ -25,7 +25,7 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// Initializes a new instance of the <see cref="XmpProfile"/> class.
/// </summary>
/// <param name="data">The UTF8 encoded byte array to read the XMP profile from.</param>
public XmpProfile(byte[] data) => this.Data = data;
public XmpProfile(byte[]? data) => this.Data = data;
/// <summary>
/// Initializes a new instance of the <see cref="XmpProfile"/> class
@ -42,15 +42,15 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// <summary>
/// Gets the XMP raw data byte array.
/// </summary>
internal byte[] Data { get; private set; }
internal byte[]? Data { get; private set; }
/// <summary>
/// Gets the raw XML document containing the XMP profile.
/// </summary>
/// <returns>The <see cref="XDocument"/></returns>
public XDocument GetDocument()
public XDocument? GetDocument()
{
byte[] byteArray = this.Data;
byte[]? byteArray = this.Data;
if (byteArray is null)
{
return null;
@ -77,6 +77,7 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// <returns>The <see cref="T:Byte[]"/></returns>
public byte[] ToByteArray()
{
Guard.NotNull(this.Data);
byte[] result = new byte[this.Data.Length];
this.Data.AsSpan().CopyTo(result);
return result;

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -140,7 +139,7 @@ public partial struct A8 : IPixel<A8>, IPackedVector<byte>
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override readonly bool Equals(object obj) => obj is A8 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is A8 other && this.Equals(other);
/// <summary>
/// Compares another A8 packed vector with the packed vector.

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -335,7 +334,7 @@ public partial struct Abgr32 : IPixel<Abgr32>, IPackedVector<uint>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Abgr32 abgr32 && this.Equals(abgr32);
public override readonly bool Equals(object? obj) => obj is Abgr32 abgr32 && this.Equals(abgr32);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -334,7 +333,7 @@ public partial struct Argb32 : IPixel<Argb32>, IPackedVector<uint>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Argb32 argb32 && this.Equals(argb32);
public override readonly bool Equals(object? obj) => obj is Argb32 argb32 && this.Equals(argb32);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -232,7 +231,7 @@ public partial struct Bgr24 : IPixel<Bgr24>
public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Bgr24 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Bgr24 other && this.Equals(other);
/// <inheritdoc />
public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})";

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -151,7 +150,7 @@ public partial struct Bgr565 : IPixel<Bgr565>, IPackedVector<ushort>
(this.PackedValue & 0x1F) * (1F / 31F));
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Bgr565 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Bgr565 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -287,7 +286,7 @@ public partial struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Bgra32 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/>
public readonly bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra);

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -143,7 +142,7 @@ public partial struct Bgra4444 : IPixel<Bgra4444>, IPackedVector<ushort>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Bgra4444 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Bgra4444 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -141,7 +140,7 @@ public partial struct Byte4 : IPixel<Byte4>, IPackedVector<uint>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Byte4 byte4 && this.Equals(byte4);
public override readonly bool Equals(object? obj) => obj is Byte4 byte4 && this.Equals(byte4);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -140,7 +139,7 @@ public partial struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
public readonly float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfSingle other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is HalfSingle other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -157,7 +156,7 @@ public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
}
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfVector2 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is HalfVector2 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -149,7 +148,7 @@ public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is HalfVector4 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is HalfVector4 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -153,7 +152,7 @@ public partial struct L16 : IPixel<L16>, IPackedVector<ushort>
public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is L16 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -143,7 +142,7 @@ public partial struct L8 : IPixel<L8>, IPackedVector<byte>
ColorNumerics.DownScaleFrom16BitTo8Bit(source.B));
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is L8 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is L8 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -80,7 +79,7 @@ public partial struct La16 : IPixel<La16>, IPackedVector<ushort>
public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is La16 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other);
/// <inheritdoc />
public override readonly string ToString() => $"La16({this.L}, {this.A})";

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -82,7 +81,7 @@ public partial struct La32 : IPixel<La32>, IPackedVector<uint>
public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is La32 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other);
/// <inheritdoc />
public override readonly string ToString() => $"La32({this.L}, {this.A})";

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -162,7 +161,7 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
(sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedByte2 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is NormalizedByte2 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -154,7 +153,7 @@ public partial struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<u
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedByte4 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is NormalizedByte4 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -163,7 +162,7 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
(short)(this.PackedValue >> 0x10) / MaxPos);
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedShort2 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is NormalizedShort2 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -155,7 +154,7 @@ public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is NormalizedShort4 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is NormalizedShort4 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -146,7 +145,7 @@ public partial struct Rg32 : IPixel<Rg32>, IPackedVector<uint>
public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max;
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rg32 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Rg32 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -241,7 +240,7 @@ public partial struct Rgb24 : IPixel<Rgb24>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Rgb24 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Rgb24 other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -210,7 +209,7 @@ public partial struct Rgb48 : IPixel<Rgb48>
public void FromRgb48(Rgb48 source) => this = source;
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rgb48 rgb48 && this.Equals(rgb48);
public override readonly bool Equals(object? obj) => obj is Rgb48 rgb48 && this.Equals(rgb48);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -141,7 +140,7 @@ public partial struct Rgba1010102 : IPixel<Rgba1010102>, IPackedVector<uint>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rgba1010102 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Rgba1010102 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

7
src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers.Binary;
using System.Globalization;
@ -267,7 +266,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
/// The <see cref="bool"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool TryParseHex(string hex, out Rgba32 result)
public static bool TryParseHex(string? hex, out Rgba32 result)
{
result = default;
if (string.IsNullOrWhiteSpace(hex))
@ -437,7 +436,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is Rgba32 rgba32 && this.Equals(rgba32);
public override readonly bool Equals(object? obj) => obj is Rgba32 rgba32 && this.Equals(rgba32);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
@ -514,7 +513,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
/// <returns>
/// A rrggbbaa hex value.
/// </returns>
private static string ToRgbaHex(string hex)
private static string? ToRgbaHex(string hex)
{
if (hex[0] == '#')
{

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -444,7 +443,7 @@ public partial struct Rgba64 : IPixel<Rgba64>, IPackedVector<ulong>
}
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Rgba64 rgba64 && this.Equals(rgba64);
public override readonly bool Equals(object? obj) => obj is Rgba64 rgba64 && this.Equals(rgba64);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Globalization;
using System.Numerics;
@ -193,7 +192,7 @@ public partial struct RgbaVector : IPixel<RgbaVector>
}
/// <inheritdoc/>
public override readonly bool Equals(object obj) => obj is RgbaVector other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is RgbaVector other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -164,7 +163,7 @@ public partial struct Short2 : IPixel<Short2>, IPackedVector<uint>
public readonly Vector2 ToVector2() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10));
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Short2 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Short2 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

3
src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics;
using System.Runtime.CompilerServices;
@ -164,7 +163,7 @@ public partial struct Short4 : IPixel<Short4>, IPackedVector<ulong>
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc />
public override readonly bool Equals(object obj) => obj is Short4 other && this.Equals(other);
public override readonly bool Equals(object? obj) => obj is Short4 other && this.Equals(other);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

24
src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs

@ -13,61 +13,61 @@ public static class AdaptiveThresholdExtensions
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor());
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit));
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="upper">Upper (white) color for thresholding.</param>
/// <param name="lower">Lower (black) color for thresholding.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower));
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="upper">Upper (white) color for thresholding.</param>
/// <param name="lower">Lower (black) color for thresholding.</param>
/// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit));
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="upper">Upper (white) color for thresholding.</param>
/// <param name="lower">Lower (black) color for thresholding.</param>
/// <param name="rectangle">Rectangle region to apply the processor on.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle);
/// <summary>
/// Applies Bradley Adaptive Threshold to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="upper">Upper (white) color for thresholding.</param>
/// <param name="lower">Lower (black) color for thresholding.</param>
/// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param>
/// <param name="rectangle">Rectangle region to apply the processor on.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit, Rectangle rectangle)
=> source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle);
}

7
src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Collections.Concurrent;
using SixLabors.ImageSharp.PixelFormats;
@ -17,7 +16,7 @@ internal class DefaultImageProcessorContext<TPixel> : IInternalImageProcessingCo
{
private readonly bool mutate;
private readonly Image<TPixel> source;
private Image<TPixel> destination;
private Image<TPixel>? destination;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultImageProcessorContext{TPixel}"/> class.
@ -54,7 +53,7 @@ internal class DefaultImageProcessorContext<TPixel> : IInternalImageProcessingCo
this.destination = this.source.Clone();
}
return this.destination;
return this.destination!;
}
/// <inheritdoc/>
@ -87,7 +86,7 @@ internal class DefaultImageProcessorContext<TPixel> : IInternalImageProcessingCo
}
// Standard processing pipeline.
using (IImageProcessor<TPixel> specificProcessor = processor.CreatePixelSpecificProcessor(this.Configuration, this.destination, rectangle))
using (IImageProcessor<TPixel> specificProcessor = processor.CreatePixelSpecificProcessor(this.Configuration, this.destination!, rectangle))
{
specificProcessor.Execute();
}

16
src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs

@ -14,9 +14,9 @@ public static class BinaryDitherExtensions
/// <summary>
/// Dithers the image reducing it to two colors using ordered dithering.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext
BinaryDither(this IImageProcessingContext source, IDither dither) =>
BinaryDither(source, dither, Color.White, Color.Black);
@ -24,11 +24,11 @@ public static class BinaryDitherExtensions
/// <summary>
/// Dithers the image reducing it to two colors using ordered dithering.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source,
IDither dither,
@ -39,12 +39,12 @@ public static class BinaryDitherExtensions
/// <summary>
/// Dithers the image reducing it to two colors using ordered dithering.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source,
IDither dither,
@ -54,14 +54,14 @@ public static class BinaryDitherExtensions
/// <summary>
/// Dithers the image reducing it to two colors using ordered dithering.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source,
IDither dither,

32
src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs

@ -15,19 +15,19 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance));
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="mode">Selects the value to be compared to threshold.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -38,12 +38,12 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -53,13 +53,13 @@ public static class BinaryThresholdExtensions
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="mode">Selects the value to be compared to threshold.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -71,11 +71,11 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -86,12 +86,12 @@ public static class BinaryThresholdExtensions
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="mode">Selects the value to be compared to threshold.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -104,14 +104,14 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,
@ -123,7 +123,7 @@ public static class BinaryThresholdExtensions
/// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
/// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
@ -131,7 +131,7 @@ public static class BinaryThresholdExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source,
float threshold,

16
src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs

@ -13,44 +13,44 @@ public static class BokehBlurExtensions
/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BokehBlurProcessor());
/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="components">The 'components' value representing the number of kernels to use to approximate the bokeh effect.</param>
/// <param name="gamma">The gamma highlight factor to use to emphasize bright spots in the source image</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma));
/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(), rectangle);
/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="components">The 'components' value representing the number of kernels to use to approximate the bokeh effect.</param>
/// <param name="gamma">The gamma highlight factor to use to emphasize bright spots in the source image</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle);
}

18
src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,36 +14,36 @@ public static class BoxBlurExtensions
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BoxBlurProcessor());
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius)
=> source.ApplyProcessor(new BoxBlurProcessor(radius));
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle)
=> source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle);
/// <summary>
/// Applies a box blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -54,7 +54,7 @@ public static class BoxBlurExtensions
/// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY);

56
src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs

@ -14,8 +14,8 @@ public static class DetectEdgesExtensions
/// Detects any edges within the image.
/// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) =>
DetectEdges(source, KnownEdgeDetectorKernels.Sobel);
@ -23,11 +23,11 @@ public static class DetectEdgesExtensions
/// Detects any edges within the image.
/// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
Rectangle rectangle) =>
@ -36,9 +36,9 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The 2D edge detector kernel.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetector2DKernel kernel) =>
@ -47,12 +47,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The 2D edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetector2DKernel kernel,
@ -66,12 +66,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The 2D edge detector kernel.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetector2DKernel kernel,
@ -81,7 +81,7 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The 2D edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
@ -89,7 +89,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetector2DKernel kernel,
@ -104,9 +104,9 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The edge detector kernel.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorKernel kernel) =>
@ -115,12 +115,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorKernel kernel,
@ -134,12 +134,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The edge detector kernel.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorKernel kernel,
@ -149,7 +149,7 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">The edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
@ -157,7 +157,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorKernel kernel,
@ -172,9 +172,9 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">Thecompass edge detector kernel.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel) =>
@ -183,12 +183,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">Thecompass edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel,
@ -202,12 +202,12 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image operating in grayscale mode.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">Thecompass edge detector kernel.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel,
@ -217,7 +217,7 @@ public static class DetectEdgesExtensions
/// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="kernel">Thecompass edge detector kernel.</param>
/// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection.
@ -225,7 +225,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel,

18
src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,36 +14,36 @@ public static class GaussianBlurExtensions
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new GaussianBlurProcessor());
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma));
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle);
/// <summary>
/// Applies a Gaussian blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -54,7 +54,7 @@ public static class GaussianBlurExtensions
/// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY);

18
src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,29 +14,29 @@ public static class GaussianSharpenExtensions
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) =>
source.ApplyProcessor(new GaussianSharpenProcessor());
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma));
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianSharpen(
this IImageProcessingContext source,
float sigma,
@ -46,7 +46,7 @@ public static class GaussianSharpenExtensions
/// <summary>
/// Applies a Gaussian sharpening filter to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -57,7 +57,7 @@ public static class GaussianSharpenExtensions
/// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY);

10
src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,19 +14,19 @@ public static class MedianBlurExtensions
/// <summary>
/// Applies a median blur on the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The radius of the area to find the median for.</param>
/// <param name="preserveAlpha">
/// Whether the filter is applied to alpha as well as the color channels.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha));
/// <summary>
/// Applies a median blur on the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The radius of the area to find the median for.</param>
/// <param name="preserveAlpha">
/// Whether the filter is applied to alpha as well as the color channels.
@ -34,7 +34,7 @@ public static class MedianBlurExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha, Rectangle rectangle)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha), rectangle);
}

40
src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs

@ -14,17 +14,17 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(this IImageProcessingContext source) =>
Dither(source, KnownDitherings.Bayer8x8);
/// <summary>
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither) =>
@ -33,10 +33,10 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -46,10 +46,10 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="palette">The palette to select substitute colors from.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -59,11 +59,11 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param>
/// <param name="palette">The palette to select substitute colors from.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -74,23 +74,23 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(this IImageProcessingContext source, Rectangle rectangle) =>
Dither(source, KnownDitherings.Bayer8x8, rectangle);
/// <summary>
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -100,13 +100,13 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -117,13 +117,13 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="palette">The palette to select substitute colors from.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,
@ -134,14 +134,14 @@ public static class DitherExtensions
/// <summary>
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param>
/// <param name="palette">The palette to select substitute colors from.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither(
this IImageProcessingContext source,
IDither dither,

18
src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Effects;
@ -15,29 +15,29 @@ public static class OilPaintExtensions
/// Alters the colors of the image recreating an oil painting effect with levels and brushSize
/// set to <value>10</value> and <value>15</value> respectively.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15);
/// <summary>
/// Alters the colors of the image recreating an oil painting effect with levels and brushSize
/// set to <value>10</value> and <value>15</value> respectively.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) =>
OilPaint(source, 10, 15, rectangle);
/// <summary>
/// Alters the colors of the image recreating an oil painting effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="levels">The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image.</param>
/// <param name="brushSize">The number of neighboring pixels used in calculating each individual pixel value.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext
OilPaint(this IImageProcessingContext source, int levels, int brushSize) =>
source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize));
@ -45,13 +45,13 @@ public static class OilPaintExtensions
/// <summary>
/// Alters the colors of the image recreating an oil painting effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="levels">The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image.</param>
/// <param name="brushSize">The number of neighboring pixels used in calculating each individual pixel value.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext OilPaint(
this IImageProcessingContext source,
int levels,

32
src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs

@ -14,88 +14,88 @@ public static class PixelRowDelegateExtensions
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation)
=> ProcessPixelRowsAsVector4(source, rowOperation, PixelConversionModifiers.None);
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers)
=> source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers));
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle)
=> ProcessPixelRowsAsVector4(source, rowOperation, rectangle, PixelConversionModifiers.None);
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers)
=> source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers), rectangle);
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation<Point> rowOperation)
=> ProcessPixelRowsAsVector4(source, rowOperation, PixelConversionModifiers.None);
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation<Point> rowOperation, PixelConversionModifiers modifiers)
=> source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers));
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation<Point> rowOperation, Rectangle rectangle)
=> ProcessPixelRowsAsVector4(source, rowOperation, rectangle, PixelConversionModifiers.None);
/// <summary>
/// Applies a user defined processing delegate to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation<Point> rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers)
=> source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers), rectangle);
}

12
src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs

@ -14,28 +14,28 @@ public static class PixelateExtensions
/// <summary>
/// Pixelates an image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4);
/// <summary>
/// Pixelates an image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="size">The size of the pixels.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) =>
source.ApplyProcessor(new PixelateProcessor(size));
/// <summary>
/// Pixelates an image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Pixelate(
this IImageProcessingContext source,
int size,

10
src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class BlackWhiteExtensions
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BlackWhite(this IImageProcessingContext source)
=> source.ApplyProcessor(new BlackWhiteProcessor());
/// <summary>
/// Applies black and white toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BlackWhiteProcessor(), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -18,9 +18,9 @@ public static class BrightnessExtensions
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new BrightnessProcessor(amount));
@ -31,12 +31,12 @@ public static class BrightnessExtensions
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new BrightnessProcessor(amount), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors;
@ -15,21 +15,21 @@ public static class ColorBlindnessExtensions
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness)
=> source.ApplyProcessor(GetProcessor(colorBlindness));
/// <summary>
/// Applies the given colorblindness simulator to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="colorBlindnessMode">The type of color blindness simulator to apply.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle)
=> source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle);

10
src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -18,9 +18,9 @@ public static class ContrastExtensions
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new ContrastProcessor(amount));
@ -31,12 +31,12 @@ public static class ContrastExtensions
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new ContrastProcessor(amount), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class FilterExtensions
/// <summary>
/// Filters an image by the given color matrix
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="matrix">The filter color matrix</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix)
=> source.ApplyProcessor(new FilterProcessor(matrix));
/// <summary>
/// Filters an image by the given color matrix
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="matrix">The filter color matrix</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle)
=> source.ApplyProcessor(new FilterProcessor(matrix), rectangle);
}

16
src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs

@ -15,7 +15,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source)
=> Grayscale(source, GrayscaleMode.Bt709);
@ -23,7 +23,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount)
@ -32,7 +32,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/>.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode)
@ -41,7 +41,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/> using the given amount.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
@ -58,7 +58,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
@ -69,7 +69,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -81,7 +81,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies grayscale toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -93,7 +93,7 @@ public static class GrayscaleExtensions
/// <summary>
/// Applies grayscale toning to the image using the given amount.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">

10
src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class HueExtensions
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="degrees">The rotation angle in degrees to adjust the hue.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees)
=> source.ApplyProcessor(new HueProcessor(degrees));
/// <summary>
/// Alters the hue component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="degrees">The rotation angle in degrees to adjust the hue.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle)
=> source.ApplyProcessor(new HueProcessor(degrees), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class InvertExtensions
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Invert(this IImageProcessingContext source)
=> source.ApplyProcessor(new InvertProcessor(1F));
/// <summary>
/// Inverts the colors of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new InvertProcessor(1F), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class KodachromeExtensions
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Kodachrome(this IImageProcessingContext source)
=> source.ApplyProcessor(new KodachromeProcessor());
/// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new KodachromeProcessor(), rectangle);
}

8
src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs

@ -18,9 +18,9 @@ public static class LightnessExtensions
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new LightnessProcessor(amount));
@ -31,12 +31,12 @@ public static class LightnessExtensions
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results.
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new LightnessProcessor(amount), rectangle);
}

8
src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs

@ -14,19 +14,19 @@ public static class LomographExtensions
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Lomograph(this IImageProcessingContext source)
=> source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()));
/// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class OpacityExtensions
/// <summary>
/// Multiplies the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new OpacityProcessor(amount));
/// <summary>
/// Multiplies the alpha component of the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new OpacityProcessor(amount), rectangle);
}

8
src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs

@ -14,19 +14,19 @@ public static class PolaroidExtensions
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Polaroid(this IImageProcessingContext source)
=> source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()));
/// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -18,9 +18,9 @@ public static class SaturateExtensions
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new SaturateProcessor(amount));
@ -31,12 +31,12 @@ public static class SaturateExtensions
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new SaturateProcessor(amount), rectangle);
}

18
src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,40 +14,40 @@ public static class SepiaExtensions
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Sepia(this IImageProcessingContext source)
=> Sepia(source, 1F);
/// <summary>
/// Applies sepia toning to the image using the given amount.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new SepiaProcessor(amount));
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle)
=> Sepia(source, 1F, rectangle);
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new SepiaProcessor(amount), rectangle);
}

10
src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Normalization;
@ -13,17 +13,17 @@ public static class HistogramEqualizationExtensions
/// <summary>
/// Equalizes the histogram of an image to increases the contrast.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) =>
HistogramEqualization(source, new HistogramEqualizationOptions());
/// <summary>
/// Equalizes the histogram of an image to increases the contrast.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The histogram equalization options to use.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext HistogramEqualization(
this IImageProcessingContext source,
HistogramEqualizationOptions options) =>

16
src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs

@ -14,21 +14,21 @@ public static class BackgroundColorExtensions
/// <summary>
/// Replaces the background color of image with the given one.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the background.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) =>
BackgroundColor(source, source.GetGraphicsOptions(), color);
/// <summary>
/// Replaces the background color of image with the given one.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the background.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BackgroundColor(
this IImageProcessingContext source,
Color color,
@ -38,10 +38,10 @@ public static class BackgroundColorExtensions
/// <summary>
/// Replaces the background color of image with the given one.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="color">The color to set as the background.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BackgroundColor(
this IImageProcessingContext source,
GraphicsOptions options,
@ -51,13 +51,13 @@ public static class BackgroundColorExtensions
/// <summary>
/// Replaces the background color of image with the given one.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="color">The color to set as the background.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BackgroundColor(
this IImageProcessingContext source,
GraphicsOptions options,

48
src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs

@ -14,17 +14,17 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source) =>
Glow(source, source.GetGraphicsOptions());
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the glow.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color)
{
return Glow(source, source.GetGraphicsOptions(), color);
@ -33,33 +33,33 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radius">The the radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) =>
Glow(source, source.GetGraphicsOptions(), radius);
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) =>
source.Glow(source.GetGraphicsOptions(), rectangle);
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="radius">The the radius.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(
this IImageProcessingContext source,
Color color,
@ -70,19 +70,19 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) =>
source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f));
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="color">The color to set as the glow.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,
@ -92,10 +92,10 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="radius">The the radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,
@ -105,12 +105,12 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,
@ -120,14 +120,14 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="radius">The the radius.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,
@ -139,14 +139,14 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="radius">The the radius.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
private static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,
@ -158,11 +158,11 @@ public static class GlowExtensions
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="radius">The the radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
private static IImageProcessingContext Glow(
this IImageProcessingContext source,
GraphicsOptions options,

40
src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs

@ -14,27 +14,27 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <param name="source">The current image processing context.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source) =>
Vignette(source, source.GetGraphicsOptions());
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the vignette.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) =>
Vignette(source, source.GetGraphicsOptions(), color);
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="radiusX">The the x-radius.</param>
/// <param name="radiusY">The the y-radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
float radiusX,
@ -44,25 +44,25 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) =>
Vignette(source, source.GetGraphicsOptions(), rectangle);
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="color">The color to set as the vignette.</param>
/// <param name="radiusX">The the x-radius.</param>
/// <param name="radiusY">The the y-radius.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
Color color,
@ -74,9 +74,9 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) =>
source.VignetteInternal(
options,
@ -87,10 +87,10 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="color">The color to set as the vignette.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
GraphicsOptions options,
@ -104,11 +104,11 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="radiusX">The the x-radius.</param>
/// <param name="radiusY">The the y-radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
GraphicsOptions options,
@ -119,12 +119,12 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
GraphicsOptions options,
@ -139,7 +139,7 @@ public static class VignetteExtensions
/// <summary>
/// Applies a radial vignette effect to an image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The current image processing context.</param>
/// <param name="options">The options effecting pixel blending.</param>
/// <param name="color">The color to set as the vignette.</param>
/// <param name="radiusX">The the x-radius.</param>
@ -147,7 +147,7 @@ public static class VignetteExtensions
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Vignette(
this IImageProcessingContext source,
GraphicsOptions options,

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

Loading…
Cancel
Save