Browse Source

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

pull/2363/head
Ildar Khayrutdinov 4 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: pull_request:
branches: branches:
- main - main
types: [ labeled, opened, synchronize, reopened ]
jobs: jobs:
Build: Build:
strategy: strategy:
matrix: matrix:
isARM:
- ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }}
options: options:
- os: ubuntu-latest - os: ubuntu-latest
framework: net7.0 framework: net7.0
@ -32,6 +35,12 @@ jobs:
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false 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 - os: ubuntu-latest
framework: net6.0 framework: net6.0
sdk: 6.0.x sdk: 6.0.x
@ -47,10 +56,23 @@ jobs:
sdk: 6.0.x sdk: 6.0.x
runtime: -x64 runtime: -x64
codecov: false 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}} runs-on: ${{matrix.options.os}}
steps: 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 - name: Git Config
shell: bash shell: bash
run: | run: |
@ -92,7 +114,6 @@ jobs:
if: ${{ matrix.options.sdk-preview != true }} if: ${{ matrix.options.sdk-preview != true }}
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
include-prerelease: true
dotnet-version: | dotnet-version: |
6.0.x 6.0.x
@ -100,7 +121,6 @@ jobs:
if: ${{ matrix.options.sdk-preview == true }} if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
include-prerelease: true
dotnet-version: | dotnet-version: |
7.0.x 7.0.x

2
ci-pack.ps1

@ -3,4 +3,4 @@ dotnet clean -c Release
$repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" $repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY"
# Building for packing and publishing. # 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 { 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. /// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam> /// <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="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param> /// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
#pragma warning disable RCS1163 // Unused parameter. public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
public static void ToPixel<TPixel>(Configuration configuration, ReadOnlySpan<Color> source, Span<TPixel> destination)
#pragma warning restore RCS1163 // Unused parameter.
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// TODO: Investigate bulk operations utilizing configuration parameter here. // 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp; namespace SixLabors.ImageSharp;
@ -808,6 +809,25 @@ internal static class Numerics
return Sse2.ConvertToInt32(vsum); 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> /// <summary>
/// Reduces even elements of the vector into one sum. /// Reduces even elements of the vector into one sum.
/// </summary> /// </summary>

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

@ -4,6 +4,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#pragma warning disable IDE0007 // Use implicit type #pragma warning disable IDE0007 // Use implicit type
@ -70,6 +71,11 @@ internal static class Adler32
return CalculateSse(adler, buffer); return CalculateSse(adler, buffer);
} }
if (AdvSimd.IsSupported)
{
return CalculateArm(adler, buffer);
}
return CalculateScalar(adler, buffer); return CalculateScalar(adler, buffer);
} }
@ -95,7 +101,7 @@ internal static class Adler32
Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr); Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
Vector128<sbyte> tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); Vector128<sbyte> tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
Vector128<byte> zero = Vector128<byte>.Zero; Vector128<byte> zero = Vector128<byte>.Zero;
var ones = Vector128.Create((short)1); Vector128<short> ones = Vector128.Create((short)1);
while (blocks > 0) while (blocks > 0)
{ {
@ -179,13 +185,13 @@ internal static class Adler32
byte* localBufferPtr = bufferPtr; byte* localBufferPtr = bufferPtr;
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
var dot3v = Vector256.Create((short)1); Vector256<short> 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<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 // Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE. // processed before s2 must be reduced modulo BASE.
var vs1 = Vector256.CreateScalar(s1); Vector256<uint> vs1 = Vector256.CreateScalar(s1);
var vs2 = Vector256.CreateScalar(s2); Vector256<uint> vs2 = Vector256.CreateScalar(s2);
while (length >= 32) 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) private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
{ {
if (length >= 16) if (length >= 16)
@ -286,7 +386,6 @@ internal static class Adler32
{ {
uint s1 = adler & 0xFFFF; uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF;
uint k;
fixed (byte* bufferPtr = buffer) fixed (byte* bufferPtr = buffer)
{ {
@ -295,7 +394,7 @@ internal static class Adler32
while (length > 0) while (length > 0)
{ {
k = length < NMAX ? length : NMAX; uint k = length < NMAX ? length : NMAX;
length -= k; length -= k;
while (k >= 16) 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.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using ArmCrc32 = System.Runtime.Intrinsics.Arm.Crc32;
namespace SixLabors.ImageSharp.Compression.Zlib; namespace SixLabors.ImageSharp.Compression.Zlib;
@ -60,6 +61,16 @@ internal static partial class Crc32
return ~CalculateSse(~crc, buffer); return ~CalculateSse(~crc, buffer);
} }
if (ArmCrc32.Arm64.IsSupported)
{
return ~CalculateArm64(~crc, buffer);
}
if (ArmCrc32.IsSupported)
{
return ~CalculateArm(~crc, buffer);
}
return ~CalculateScalar(~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)] [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static uint CalculateScalar(uint crc, ReadOnlySpan<byte> buffer) private static uint CalculateScalar(uint crc, ReadOnlySpan<byte> buffer)
{ {

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

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

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
@ -69,11 +68,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary> /// <summary>
/// The color depth, in number of bits per pixel. /// The color depth, in number of bits per pixel.
/// </summary> /// </summary>
@ -124,7 +118,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration(); Configuration configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata; ImageMetadata metadata = image.Metadata;
BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); BmpMetadata bmpMetadata = metadata.GetBmpMetadata();
this.bitsPerPixel ??= bmpMetadata.BitsPerPixel; this.bitsPerPixel ??= bmpMetadata.BitsPerPixel;
@ -142,7 +136,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
_ => 0 _ => 0
}; };
byte[] iccProfileData = null; byte[]? iccProfileData = null;
int iccProfileSize = 0; int iccProfileSize = 0;
if (metadata.IccProfile != null) if (metadata.IccProfile != null)
{ {
@ -165,7 +159,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
this.WriteImage(stream, image); this.WriteImage(configuration, stream, image);
WriteColorProfile(stream, iccProfileData, buffer); WriteColorProfile(stream, iccProfileData, buffer);
stream.Flush(); stream.Flush();
@ -182,7 +176,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// <param name="metadata">The metadata.</param> /// <param name="metadata">The metadata.</param>
/// <param name="iccProfileData">The icc profile data.</param> /// <param name="iccProfileData">The icc profile data.</param>
/// <returns>The bitmap information header.</returns> /// <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 hResolution = 0;
int vResolution = 0; int vResolution = 0;
@ -234,7 +228,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
infoHeader.Compression = BmpCompression.BitFields; 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.ProfileSize = iccProfileData.Length;
infoHeader.CsType = BmpColorSpace.PROFILE_EMBEDDED; 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="stream">The stream to write to.</param>
/// <param name="iccProfileData">The color profile data.</param> /// <param name="iccProfileData">The color profile data.</param>
/// <param name="buffer">The buffer.</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) if (iccProfileData != null)
{ {
@ -313,42 +307,43 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes the pixel data to the binary stream. /// Writes the pixel data to the binary stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> /// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data. /// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param> /// </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> where TPixel : unmanaged, IPixel<TPixel>
{ {
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer; Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer;
switch (this.bitsPerPixel) switch (this.bitsPerPixel)
{ {
case BmpBitsPerPixel.Pixel32: case BmpBitsPerPixel.Pixel32:
this.Write32BitPixelData(stream, pixels); this.Write32BitPixelData(configuration, stream, pixels);
break; break;
case BmpBitsPerPixel.Pixel24: case BmpBitsPerPixel.Pixel24:
this.Write24BitPixelData(stream, pixels); this.Write24BitPixelData(configuration, stream, pixels);
break; break;
case BmpBitsPerPixel.Pixel16: case BmpBitsPerPixel.Pixel16:
this.Write16BitPixelData(stream, pixels); this.Write16BitPixelData(configuration, stream, pixels);
break; break;
case BmpBitsPerPixel.Pixel8: case BmpBitsPerPixel.Pixel8:
this.Write8BitPixelData(stream, image); this.Write8BitPixelData(configuration, stream, image);
break; break;
case BmpBitsPerPixel.Pixel4: case BmpBitsPerPixel.Pixel4:
this.Write4BitPixelData(stream, image); this.Write4BitPixelData(configuration, stream, image);
break; break;
case BmpBitsPerPixel.Pixel2: case BmpBitsPerPixel.Pixel2:
this.Write2BitPixelData(stream, image); this.Write2BitPixelData(configuration, stream, image);
break; break;
case BmpBitsPerPixel.Pixel1: case BmpBitsPerPixel.Pixel1:
this.Write1BitPixelData(stream, image); this.Write1BitPixelData(configuration, stream, image);
break; break;
} }
} }
@ -360,9 +355,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 32-bit data with a color palette to the stream. /// Writes 32-bit data with a color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4); using IMemoryOwner<byte> row = this.AllocateRow(pixels.Width, 4);
@ -372,7 +368,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
{ {
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes( PixelOperations<TPixel>.Instance.ToBgra32Bytes(
this.configuration, configuration,
pixelSpan, pixelSpan,
rowSpan, rowSpan,
pixelSpan.Length); pixelSpan.Length);
@ -384,9 +380,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 24-bit pixel data with a color palette to the stream. /// Writes 24-bit pixel data with a color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int width = pixels.Width; int width = pixels.Width;
@ -398,7 +395,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
{ {
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes( PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration, configuration,
pixelSpan, pixelSpan,
row.Slice(0, rowBytesWithoutPadding), row.Slice(0, rowBytesWithoutPadding),
width); width);
@ -410,10 +407,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
/// Writes 16-bit pixel data with a color palette to the stream. /// Writes 16-bit pixel data with a color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write16BitPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels) private void Write16BitPixelData<TPixel>(Configuration configuration, Stream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int width = pixels.Width; int width = pixels.Width;
int rowBytesWithoutPadding = width * 2; int rowBytesWithoutPadding = width * 2;
@ -425,7 +423,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra5551Bytes( PixelOperations<TPixel>.Instance.ToBgra5551Bytes(
this.configuration, configuration,
pixelSpan, pixelSpan,
row.Slice(0, rowBytesWithoutPadding), row.Slice(0, rowBytesWithoutPadding),
pixelSpan.Length); 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. /// Writes 8 bit pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool isL8 = typeof(TPixel) == typeof(L8); bool isL8 = typeof(TPixel) == typeof(L8);
@ -453,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
} }
else 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. /// Writes an 8 bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="Image{TPixel}"/> containing pixel data.</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> /// <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> 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); frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds); using IndexedImageFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds);
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; 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--) 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. /// Writes 4 bit pixel data with a color palette. The color palette has 16 entry's with 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</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> 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 MaxColors = 16
}); });
@ -546,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0); ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; 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. /// Writes 2 bit pixel data with a color palette. The color palette has 4 entry's with 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</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> 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 MaxColors = 4
}); });
@ -593,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0); ReadOnlySpan<byte> pixelRowSpan = quantized.DangerousGetRowSpan(0);
int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; 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. /// Writes 1 bit pixel data with a color palette. The color palette has 2 entry's with 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</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> 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 MaxColors = 2
}); });
@ -649,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
Span<byte> colorPalette = colorPaletteBuffer.GetSpan(); Span<byte> colorPalette = colorPaletteBuffer.GetSpan();
ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span; ReadOnlySpan<TPixel> quantizedColorPalette = quantized.Palette.Span;
this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette);
ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0); ReadOnlySpan<byte> quantizedPixelRow = quantized.DangerousGetRowSpan(0);
int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; 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. /// Writes the color palette to the stream. The color palette has 4 bytes for each entry.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <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="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="quantizedColorPalette">The color palette from the quantized image.</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> /// <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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int quantizedColorBytes = quantizedColorPalette.Length * 4; 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); Span<uint> colorPaletteAsUInt = MemoryMarshal.Cast<byte, uint>(colorPalette);
for (int i = 0; i < colorPaletteAsUInt.Length; i++) 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; 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> /// <summary>
/// Gets the shared default general decoder options instance. /// Gets the shared default general decoder options instance.
/// Used internally to reduce allocations for default decoding operations.
/// </summary> /// </summary>
internal static DecoderOptions Default { get; } = LazyOptions.Value; internal static DecoderOptions Default { get; } = LazyOptions.Value;
/// <summary> /// <summary>
/// Gets a custom configuration instance to be used by the image processing pipeline. /// Gets a custom configuration instance to be used by the image processing pipeline.
/// </summary> /// </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> /// <summary>
/// Gets the target size to decode the image into. Scaling should use an operation equivalent to <see cref="ResizeMode.Max"/>. /// 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. /// Gets the maximum number of image frames to decode, inclusive.
/// </summary> /// </summary>
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -24,15 +24,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[16]; private readonly byte[] buffer = new byte[16];
/// <summary>
/// The currently loaded stream.
/// </summary>
private BufferedReadStream stream;
/// <summary> /// <summary>
/// The global color table. /// The global color table.
/// </summary> /// </summary>
private IMemoryOwner<byte> globalColorTable; private IMemoryOwner<byte>? globalColorTable;
/// <summary> /// <summary>
/// The area to restore. /// The area to restore.
@ -77,12 +72,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// The abstract metadata. /// The abstract metadata.
/// </summary> /// </summary>
private ImageMetadata metadata; private ImageMetadata? metadata;
/// <summary> /// <summary>
/// The gif specific metadata. /// The gif specific metadata.
/// </summary> /// </summary>
private GifMetadata gifMetadata; private GifMetadata? gifMetadata;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore"/> class. /// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
@ -108,8 +103,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint frameCount = 0; uint frameCount = 0;
Image<TPixel> image = null; Image<TPixel>? image = null;
ImageFrame<TPixel> previousFrame = null; ImageFrame<TPixel>? previousFrame = null;
try try
{ {
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
@ -125,7 +120,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
break; break;
} }
this.ReadFrame(ref image, ref previousFrame); this.ReadFrame(stream, ref image, ref previousFrame);
// Reset per-frame state. // Reset per-frame state.
this.imageDescriptor = default; this.imageDescriptor = default;
@ -136,16 +131,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
switch (stream.ReadByte()) switch (stream.ReadByte())
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension(); this.ReadGraphicalControlExtension(stream);
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments(stream);
break; break;
case GifConstants.ApplicationExtensionLabel: case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension(); this.ReadApplicationExtension(stream);
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder. SkipBlock(stream); // Not supported by any known decoder.
break; break;
} }
} }
@ -187,23 +182,23 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
{ {
if (nextFlag == GifConstants.ImageLabel) if (nextFlag == GifConstants.ImageLabel)
{ {
this.ReadImageDescriptor(); this.ReadImageDescriptor(stream);
} }
else if (nextFlag == GifConstants.ExtensionIntroducer) else if (nextFlag == GifConstants.ExtensionIntroducer)
{ {
switch (stream.ReadByte()) switch (stream.ReadByte())
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.SkipBlock(); // Skip graphic control extension block SkipBlock(stream); // Skip graphic control extension block
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments(stream);
break; break;
case GifConstants.ApplicationExtensionLabel: case GifConstants.ApplicationExtensionLabel:
this.ReadApplicationExtension(); this.ReadApplicationExtension(stream);
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
this.SkipBlock(); // Not supported by any known decoder. SkipBlock(stream); // Not supported by any known decoder.
break; break;
} }
} }
@ -239,9 +234,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the graphic control extension. /// Reads the graphic control extension.
/// </summary> /// </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) if (bytesRead != 6)
{ {
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension"); GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
@ -253,9 +249,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the image descriptor. /// Reads the image descriptor.
/// </summary> /// </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) if (bytesRead != 9)
{ {
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor"); GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
@ -271,9 +268,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the logical screen descriptor. /// Reads the logical screen descriptor.
/// </summary> /// </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) if (bytesRead != 7)
{ {
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor"); 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 /// Reads the application extension block parsing any animation or XMP information
/// if present. /// if present.
/// </summary> /// </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 // 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. // 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) 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); bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
if (isXmp && !this.skipMetadata) if (isXmp && !this.skipMetadata)
{ {
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator); GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator);
if (extension.Data.Length > 0) if (extension.Data.Length > 0)
{ {
this.metadata.XmpProfile = new XmpProfile(extension.Data); this.metadata!.XmpProfile = new XmpProfile(extension.Data);
} }
else else
{ {
// Reset the stream position and continue. // Reset the stream position and continue.
this.stream.Position = position; stream.Position = position;
this.SkipBlock(appLength); SkipBlock(stream, appLength);
} }
return; return;
} }
int subBlockSize = this.stream.ReadByte(); int subBlockSize = stream.ReadByte();
// TODO: There's also a NETSCAPE buffer extension. // TODO: There's also a NETSCAPE buffer extension.
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension // http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize) if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{ {
this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize); stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
this.stream.Skip(1); // Skip the terminator. stream.Skip(1); // Skip the terminator.
return; return;
} }
// Could be something else not supported yet. // Could be something else not supported yet.
// Skip the subblock and terminator. // Skip the subblock and terminator.
this.SkipBlock(subBlockSize); SkipBlock(stream, subBlockSize);
return; return;
} }
this.SkipBlock(appLength); // Not supported by any known decoder. SkipBlock(stream, appLength); // Not supported by any known decoder.
} }
/// <summary> /// <summary>
/// Skips over a block or reads its terminator. /// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// </summary> /// </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) if (blockSize > 0)
{ {
this.stream.Skip(blockSize); stream.Skip(blockSize);
} }
int flag; int flag;
while ((flag = this.stream.ReadByte()) > 0) while ((flag = stream.ReadByte()) > 0)
{ {
this.stream.Skip(flag); stream.Skip(flag);
} }
} }
/// <summary> /// <summary>
/// Reads the gif comments. /// Reads the gif comments.
/// </summary> /// </summary>
private void ReadComments() /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
private void ReadComments(BufferedReadStream stream)
{ {
int length; int length;
var stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new();
while ((length = this.stream.ReadByte()) != 0) while ((length = stream.ReadByte()) != 0)
{ {
if (length > GifConstants.MaxCommentSubBlockLength) if (length > GifConstants.MaxCommentSubBlockLength)
{ {
@ -372,21 +373,21 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
if (this.skipMetadata) if (this.skipMetadata)
{ {
this.stream.Seek(length, SeekOrigin.Current); stream.Seek(length, SeekOrigin.Current);
continue; continue;
} }
using IMemoryOwner<byte> commentsBuffer = this.memoryAllocator.Allocate<byte>(length); using IMemoryOwner<byte> commentsBuffer = this.memoryAllocator.Allocate<byte>(length);
Span<byte> commentsSpan = commentsBuffer.GetSpan(); Span<byte> commentsSpan = commentsBuffer.GetSpan();
this.stream.Read(commentsSpan); stream.Read(commentsSpan);
string commentPart = GifConstants.Encoding.GetString(commentsSpan); string commentPart = GifConstants.Encoding.GetString(commentsSpan);
stringBuilder.Append(commentPart); stringBuilder.Append(commentPart);
} }
if (stringBuilder.Length > 0) 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. /// Reads an individual gif frame.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
this.ReadImageDescriptor(); this.ReadImageDescriptor(stream);
IMemoryOwner<byte> localColorTable = null; IMemoryOwner<byte>? localColorTable = null;
Buffer2D<byte> indices = null; Buffer2D<byte>? indices = null;
try try
{ {
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. // 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; int length = this.imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean); 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); 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; Span<byte> rawColorTable = default;
if (localColorTable != null) if (localColorTable != null)
@ -430,7 +432,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
// Skip any remaining blocks // Skip any remaining blocks
this.SkipBlock(); SkipBlock(stream);
} }
finally finally
{ {
@ -442,12 +444,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Reads the frame indices marking the color to use for each pixel. /// Reads the frame indices marking the color to use for each pixel.
/// </summary> /// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="indices">The 2D pixel buffer to write to.</param> /// <param name="indices">The 2D pixel buffer to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(Buffer2D<byte> indices) private void ReadFrameIndices(BufferedReadStream stream, Buffer2D<byte> indices)
{ {
int minCodeSize = this.stream.ReadByte(); int minCodeSize = stream.ReadByte();
using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream);
lzwDecoder.DecodePixels(minCodeSize, indices); lzwDecoder.DecodePixels(minCodeSize, indices);
} }
@ -460,15 +463,15 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// <param name="indices">The indexed pixels.</param> /// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int imageWidth = this.logicalScreenDescriptor.Width; int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.logicalScreenDescriptor.Height;
bool transFlag = this.graphicsControlExtension.TransparencyFlag; bool transFlag = this.graphicsControlExtension.TransparencyFlag;
ImageFrame<TPixel> prevFrame = null; ImageFrame<TPixel>? prevFrame = null;
ImageFrame<TPixel> currentFrame = null; ImageFrame<TPixel>? currentFrame = null;
ImageFrame<TPixel> imageFrame; ImageFrame<TPixel> imageFrame;
if (previousFrame is null) if (previousFrame is null)
@ -494,7 +497,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
prevFrame = previousFrame; prevFrame = previousFrame;
} }
currentFrame = image.Frames.CreateFrame(); currentFrame = image!.Frames.CreateFrame();
this.SetFrameMetadata(currentFrame.Metadata, false); this.SetFrameMetadata(currentFrame.Metadata, false);
@ -661,13 +664,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// Reads the logical screen descriptor and global color table blocks /// Reads the logical screen descriptor and global color table blocks
/// </summary> /// </summary>
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(gifMetadata))]
private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream) private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream)
{ {
this.stream = stream;
// Skip the identifier // Skip the identifier
this.stream.Skip(6); stream.Skip(6);
this.ReadLogicalScreenDescriptor(); this.ReadLogicalScreenDescriptor(stream);
ImageMetadata meta = new(); ImageMetadata meta = new();

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

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

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -37,5 +38,5 @@ public static partial class MetadataExtensions
/// <returns> /// <returns>
/// <see langword="true"/> if the gif frame metadata exists; otherwise, <see langword="false"/>. /// <see langword="true"/> if the gif frame metadata exists; otherwise, <see langword="false"/>.
/// </returns> /// </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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -45,7 +44,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// The <see cref="ImageMetadata"/> decoded by this decoder instance. /// The <see cref="ImageMetadata"/> decoded by this decoder instance.
/// </summary> /// </summary>
private ImageMetadata metadata; private ImageMetadata? metadata;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PbmDecoderCore" /> class. /// 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters; namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -35,6 +36,10 @@ internal static class AverageFilter
{ {
DecodeSse2(scanline, previousScanline); DecodeSse2(scanline, previousScanline);
} }
else if (AdvSimd.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline, previousScanline);
}
else else
{ {
DecodeScalar(scanline, previousScanline, bytesPerPixel); DecodeScalar(scanline, previousScanline, bytesPerPixel);
@ -48,7 +53,7 @@ internal static class AverageFilter
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
Vector128<byte> d = Vector128<byte>.Zero; Vector128<byte> d = Vector128<byte>.Zero;
var ones = Vector128.Create((byte)1); Vector128<byte> ones = Vector128.Create((byte)1);
int rb = scanline.Length; int rb = scanline.Length;
nint offset = 1; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel) 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters; namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -38,6 +39,10 @@ internal static class PaethFilter
{ {
DecodeSse41(scanline, previousScanline); DecodeSse41(scanline, previousScanline);
} }
else if (AdvSimd.Arm64.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline, previousScanline);
}
else else
{ {
DecodeScalar(scanline, previousScanline, bytesPerPixel); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel) 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters; namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -29,6 +30,10 @@ internal static class SubFilter
{ {
DecodeSse2(scanline); DecodeSse2(scanline);
} }
else if (AdvSimd.IsSupported && bytesPerPixel is 4)
{
DecodeArm(scanline);
}
else else
{ {
DecodeScalar(scanline, bytesPerPixel); 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) private static void DecodeScalar(Span<byte> scanline, int bytesPerPixel)
{ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Png.Filters; namespace SixLabors.ImageSharp.Formats.Png.Filters;
@ -34,6 +35,10 @@ internal static class UpFilter
{ {
DecodeSse2(scanline, previousScanline); DecodeSse2(scanline, previousScanline);
} }
else if (AdvSimd.IsSupported)
{
DecodeArm(scanline, previousScanline);
}
else else
{ {
DecodeScalar(scanline, previousScanline); DecodeScalar(scanline, previousScanline);
@ -51,11 +56,10 @@ internal static class UpFilter
while (rb >= Vector256<byte>.Count) while (rb >= Vector256<byte>.Count)
{ {
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); 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> 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) = Avx2.Add(up, prior);
Unsafe.As<byte, Vector256<byte>>(ref scanRef) = sum;
offset += Vector256<byte>.Count; offset += Vector256<byte>.Count;
rb -= Vector256<byte>.Count; rb -= Vector256<byte>.Count;
@ -82,11 +86,10 @@ internal static class UpFilter
while (rb >= Vector128<byte>.Count) while (rb >= Vector128<byte>.Count)
{ {
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); 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> 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) = Sse2.Add(up, prior);
Unsafe.As<byte, Vector128<byte>>(ref scanRef) = sum;
offset += Vector128<byte>.Count; offset += Vector128<byte>.Count;
rb -= 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline) 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers; using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -29,12 +29,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// The metadata. /// The metadata.
/// </summary> /// </summary>
private ImageMetadata metadata; private ImageMetadata? metadata;
/// <summary> /// <summary>
/// The tga specific metadata. /// The tga specific metadata.
/// </summary> /// </summary>
private TgaMetadata tgaMetadata; private TgaMetadata? tgaMetadata;
/// <summary> /// <summary>
/// The file header containing general information about the image. /// The file header containing general information about the image.
@ -46,11 +46,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The stream to decode from.
/// </summary>
private BufferedReadStream currentStream;
/// <summary> /// <summary>
/// Indicates whether there is a alpha channel present. /// Indicates whether there is a alpha channel present.
/// </summary> /// </summary>
@ -80,7 +75,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
try try
{ {
TgaImageOrigin origin = this.ReadFileHeader(stream); TgaImageOrigin origin = this.ReadFileHeader(stream);
this.currentStream.Skip(this.fileHeader.IdLength); stream.Skip(this.fileHeader.IdLength);
// Parse the color map, if present. // Parse the color map, if present.
if (this.fileHeader.ColorMapType is not 0 and not 1) 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"); 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(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1) 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)) using (IMemoryOwner<byte> palette = this.memoryAllocator.Allocate<byte>(colorMapSizeInBytes, AllocationOptions.Clean))
{ {
Span<byte> paletteSpan = palette.GetSpan(); 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) if (bytesRead != colorMapSizeInBytes)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map"); 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) if (this.fileHeader.ImageType == TgaImageType.RleColorMapped)
{ {
this.ReadPalettedRle( this.ReadPalettedRle(
stream,
this.fileHeader.Width, this.fileHeader.Width,
this.fileHeader.Height, this.fileHeader.Height,
pixels, pixels,
@ -132,6 +128,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
else else
{ {
this.ReadPaletted( this.ReadPaletted(
stream,
this.fileHeader.Width, this.fileHeader.Width,
this.fileHeader.Height, this.fileHeader.Height,
pixels, pixels,
@ -148,7 +145,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.CMapLength > 0) if (this.fileHeader.CMapLength > 0)
{ {
int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8; int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes); stream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
} }
switch (this.fileHeader.PixelDepth) switch (this.fileHeader.PixelDepth)
@ -156,11 +153,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 8: case 8:
if (this.fileHeader.ImageType.IsRunLengthEncoded()) 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 else
{ {
this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); this.ReadMonoChrome(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
} }
break; break;
@ -169,11 +166,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 16: case 16:
if (this.fileHeader.ImageType.IsRunLengthEncoded()) 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 else
{ {
this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); this.ReadBgra16(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
} }
break; break;
@ -181,11 +178,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 24: case 24:
if (this.fileHeader.ImageType.IsRunLengthEncoded()) 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 else
{ {
this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); this.ReadBgr24(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
} }
break; break;
@ -193,11 +190,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 32: case 32:
if (this.fileHeader.ImageType.IsRunLengthEncoded()) 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 else
{ {
this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); this.ReadBgra32(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
} }
break; break;
@ -219,13 +216,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image with a palette. /// Reads a uncompressed TGA image with a palette.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="palette">The color palette.</param> /// <param name="palette">The color palette.</param>
/// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param> /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -243,14 +241,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) 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--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) 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--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) 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. /// Reads a run length encoded TGA image with a palette.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="palette">The color palette.</param> /// <param name="palette">The color palette.</param>
/// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param> /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean)) using IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height, AllocationOptions.Clean);
{ TPixel color = default;
TPixel color = default; Span<byte> bufferSpan = buffer.GetSpan();
Span<byte> bufferSpan = buffer.GetSpan(); this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1);
this.UncompressRle(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); int idx = rowStartIdx + x;
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY); switch (colorMapPixelSizeInBytes)
int rowStartIdx = y * width;
for (int x = 0; x < width; x++)
{ {
int idx = rowStartIdx + x; case 1:
switch (colorMapPixelSizeInBytes) color.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
{ break;
case 1: case 2:
color.FromL8(Unsafe.As<byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
break; break;
case 2: case 3:
this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break; break;
case 3: case 4:
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break; break;
case 4:
color.FromBgra32(Unsafe.As<byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
}
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
} }
int newX = InvertX(x, width, origin);
pixelRow[newX] = color;
} }
} }
} }
@ -349,11 +346,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed monochrome TGA image. /// Reads a uncompressed monochrome TGA image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">the image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
@ -366,7 +364,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
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--) for (int y = height - 1; y >= 0; y--)
{ {
this.ReadL8Row(width, pixels, rowSpan, y); this.ReadL8Row(stream, width, pixels, rowSpan, y);
} }
} }
else else
{ {
for (int y = 0; y < height; y++) 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. /// Reads a uncompressed TGA image where each pixels has 16 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -417,7 +416,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) 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) if (bytesRead != 2)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -442,7 +441,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
else else
{ {
int bytesRead = this.currentStream.Read(rowSpan); int bytesRead = stream.Read(rowSpan);
if (bytesRead != rowSpan.Length) if (bytesRead != rowSpan.Length)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); 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. /// Reads a uncompressed TGA image where each pixels has 24 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
@ -490,7 +490,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
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--) for (int y = height - 1; y >= 0; y--)
{ {
this.ReadBgr24Row(width, pixels, rowSpan, y); this.ReadBgr24Row(stream, width, pixels, rowSpan, y);
} }
} }
else else
{ {
for (int y = 0; y < height; y++) 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. /// Reads a uncompressed TGA image where each pixels has 32 bit.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
Guard.NotNull(this.tgaMetadata);
if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX)
{ {
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0); 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--) for (int y = height - 1; y >= 0; y--)
{ {
this.ReadBgra32Row(width, pixels, rowSpan, y); this.ReadBgra32Row(stream, width, pixels, rowSpan, y);
} }
} }
else else
{ {
for (int y = 0; y < height; y++) 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--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadBgra32Pixel(x, color, pixelRow); this.ReadBgra32Pixel(stream, x, color, pixelRow);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) 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. /// Reads a run length encoded TGA image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <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="width">The width of the image.</param>
/// <param name="height">The height 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="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="origin">The image origin.</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> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
Guard.NotNull(this.tgaMetadata);
byte alphaBits = this.tgaMetadata.AlphaChannelBits; 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(); int newY = InvertY(y, height, origin);
this.UncompressRle(width, height, bufferSpan, bytesPerPixel); Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY);
for (int y = 0; y < height; y++) int rowStartIdx = y * width * bytesPerPixel;
for (int x = 0; x < width; x++)
{ {
int newY = InvertY(y, height, origin); int idx = rowStartIdx + (x * bytesPerPixel);
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(newY); switch (bytesPerPixel)
int rowStartIdx = y * width * bytesPerPixel;
for (int x = 0; x < width; x++)
{ {
int idx = rowStartIdx + (x * bytesPerPixel); case 1:
switch (bytesPerPixel) color.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx]));
{ break;
case 1: case 2:
color.FromL8(Unsafe.As<byte, L8>(ref bufferSpan[idx])); if (!this.hasAlpha)
break; {
case 2: // Set alpha value to 1, to treat it as opaque for Bgra5551.
if (!this.hasAlpha) bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
{ }
// 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]));
if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) }
{ else
color.FromLa16(Unsafe.As<byte, La16>(ref bufferSpan[idx])); {
} color.FromBgra5551(Unsafe.As<byte, Bgra5551>(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;
}
int newX = InvertX(x, width, origin); break;
pixelRow[newX] = color; 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)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = this.currentStream.Read(row); int bytesRead = stream.Read(row);
if (bytesRead != row.Length) if (bytesRead != row.Length)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -672,19 +678,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte pixelValue = (byte)this.currentStream.ReadByte(); byte pixelValue = (byte)stream.ReadByte();
color.FromL8(Unsafe.As<byte, L8>(ref pixelValue)); color.FromL8(Unsafe.As<byte, L8>(ref pixelValue));
pixelSpan[x] = color; pixelSpan[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> 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) if (bytesRead != 3)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel");
@ -695,10 +701,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = this.currentStream.Read(row); int bytesRead = stream.Read(row);
if (bytesRead != row.Length) if (bytesRead != row.Length)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -709,25 +715,27 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> 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) if (bytesRead != 4)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); 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]; 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)); color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha));
pixelRow[x] = color; pixelRow[x] = color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = this.currentStream.Read(row); int bytesRead = stream.Read(row);
if (bytesRead != row.Length) if (bytesRead != row.Length)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -738,10 +746,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = this.currentStream.ReadByte(); int colorIndex = stream.ReadByte();
if (colorIndex == -1) if (colorIndex == -1)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -768,10 +776,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = this.currentStream.ReadByte(); int colorIndex = stream.ReadByte();
if (colorIndex == -1) if (colorIndex == -1)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -782,10 +790,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorIndex = this.currentStream.ReadByte(); int colorIndex = stream.ReadByte();
if (colorIndex == -1) if (colorIndex == -1)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@ -798,25 +806,26 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// <summary> /// <summary>
/// Produce uncompressed tga data from a run length encoded stream. /// Produce uncompressed tga data from a run length encoded stream.
/// </summary> /// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <param name="width">The width of the image.</param> /// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param> /// <param name="height">The height of the image.</param>
/// <param name="buffer">Buffer for uncompressed data.</param> /// <param name="buffer">Buffer for uncompressed data.</param>
/// <param name="bytesPerPixel">The bytes used per pixel.</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; int uncompressedPixels = 0;
Span<byte> pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel); Span<byte> pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel);
int totalPixels = width * height; int totalPixels = width * height;
while (uncompressedPixels < totalPixels) 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. // The high bit of a run length packet is set to 1.
int highBit = runLengthByte >> 7; int highBit = runLengthByte >> 7;
if (highBit == 1) if (highBit == 1)
{ {
int runLength = runLengthByte & 127; int runLength = runLengthByte & 127;
int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel); int bytesRead = stream.Read(pixel, 0, bytesPerPixel);
if (bytesRead != bytesPerPixel) if (bytesRead != bytesPerPixel)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); 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; int bufferIdx = uncompressedPixels * bytesPerPixel;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) 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) if (bytesRead != bytesPerPixel)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@ -917,13 +926,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// </summary> /// </summary>
/// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param> /// <param name="stream">The <see cref="BufferedReadStream"/> containing image data.</param>
/// <returns>The image origin.</returns> /// <returns>The image origin.</returns>
[MemberNotNull(nameof(metadata))]
[MemberNotNull(nameof(tgaMetadata))]
private TgaImageOrigin ReadFileHeader(BufferedReadStream stream) private TgaImageOrigin ReadFileHeader(BufferedReadStream stream)
{ {
this.currentStream = stream;
Span<byte> buffer = stackalloc byte[TgaFileHeader.Size]; 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.fileHeader = TgaFileHeader.Parse(buffer);
this.metadata = new ImageMetadata(); this.metadata = new ImageMetadata();
this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata();
@ -939,7 +948,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
this.hasAlpha = alphaBits > 0; this.hasAlpha = alphaBits > 0;
// Bits 4 and 5 describe the image origin. // Bits 4 and 5 describe the image origin.
var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
return origin;
} }
} }

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

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

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

@ -67,6 +67,11 @@ internal static class HorizontalPredictor
{ {
ApplyHorizontalPrediction8Bit(rows, width); 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) else if (bitsPerPixel == 24)
{ {
ApplyHorizontalPrediction24Bit(rows, width); 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> /// <summary>
/// Applies a horizontal predictor to a gray pixel row. /// Applies a horizontal predictor to a gray pixel row.
/// </summary> /// </summary>

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

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

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

@ -54,7 +54,7 @@ public enum TiffBitsPerPixel
/// <summary> /// <summary>
/// 16 bits per pixel, for gray images. /// 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> /// </summary>
Bit16 = 16, Bit16 = 16,

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

@ -376,11 +376,14 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
case TiffBitsPerPixel.Bit8: case TiffBitsPerPixel.Bit8:
this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break; break;
case TiffBitsPerPixel.Bit16:
// Assume desire to encode as L16 grayscale
this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break;
case TiffBitsPerPixel.Bit6: case TiffBitsPerPixel.Bit6:
case TiffBitsPerPixel.Bit10: case TiffBitsPerPixel.Bit10:
case TiffBitsPerPixel.Bit12: case TiffBitsPerPixel.Bit12:
case TiffBitsPerPixel.Bit14: case TiffBitsPerPixel.Bit14:
case TiffBitsPerPixel.Bit16:
case TiffBitsPerPixel.Bit30: case TiffBitsPerPixel.Bit30:
case TiffBitsPerPixel.Bit36: case TiffBitsPerPixel.Bit36:
case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit42:
@ -413,13 +416,20 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return; 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) if (inputBitsPerPixel == 8)
{ {
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
return; 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); this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor);
return; return;
} }
@ -434,6 +444,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return; return;
} }
if (inputBitsPerPixel == 16)
{
this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor);
return;
}
this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor);
return; return;

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

@ -343,20 +343,20 @@ internal class TiffEncoderEntriesCollector
return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); return TiffConstants.BitsPerSampleRgb8Bit.ToArray();
case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.WhiteIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) return encoder.BitsPerPixel switch
{ {
return TiffConstants.BitsPerSample1Bit.ToArray(); TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
} TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
return TiffConstants.BitsPerSample8Bit.ToArray(); };
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) return encoder.BitsPerPixel switch
{ {
return TiffConstants.BitsPerSample1Bit.ToArray(); TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(),
} TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(),
_ => TiffConstants.BitsPerSample8Bit.ToArray()
return TiffConstants.BitsPerSample8Bit.ToArray(); };
default: default:
return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); 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); return new TiffPaletteWriter<TPixel>(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero: 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: default:
return new TiffRgbWriter<TPixel>(image, memoryAllocator, configuration, entriesCollector); 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
@ -14,7 +15,7 @@ internal static class LossyUtils
{ {
// Note: method name in libwebp reference implementation is called VP8SSE16x16. // Note: method name in libwebp reference implementation is called VP8SSE16x16.
[MethodImpl(InliningOptions.ShortMethod)] [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) if (Avx2.IsSupported)
{ {
@ -26,12 +27,17 @@ internal static class LossyUtils
return Vp8_Sse16xN_Sse2(a, b, 8); return Vp8_Sse16xN_Sse2(a, b, 8);
} }
if (AdvSimd.IsSupported)
{
return Vp8_Sse16x16_Neon(a, b);
}
return Vp8_SseNxN(a, b, 16, 16); return Vp8_SseNxN(a, b, 16, 16);
} }
// Note: method name in libwebp reference implementation is called VP8SSE16x8. // Note: method name in libwebp reference implementation is called VP8SSE16x8.
[MethodImpl(InliningOptions.ShortMethod)] [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) if (Avx2.IsSupported)
{ {
@ -43,12 +49,17 @@ internal static class LossyUtils
return Vp8_Sse16xN_Sse2(a, b, 4); return Vp8_Sse16xN_Sse2(a, b, 4);
} }
if (AdvSimd.IsSupported)
{
return Vp8_Sse16x8_Neon(a, b);
}
return Vp8_SseNxN(a, b, 16, 8); return Vp8_SseNxN(a, b, 16, 8);
} }
// Note: method name in libwebp reference implementation is called VP8SSE4x4. // Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)] [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) if (Avx2.IsSupported)
{ {
@ -77,8 +88,8 @@ internal static class LossyUtils
Vector256<byte> b01s = Avx2.UnpackLow(b01.AsByte(), Vector256<byte>.Zero); Vector256<byte> b01s = Avx2.UnpackLow(b01.AsByte(), Vector256<byte>.Zero);
// subtract, square and accumulate. // subtract, square and accumulate.
Vector256<byte> d0 = Avx2.SubtractSaturate(a01s, b01s); Vector256<short> d0 = Avx2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16());
Vector256<int> e0 = Avx2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); Vector256<int> e0 = Avx2.MultiplyAddAdjacent(d0, d0);
return Numerics.ReduceSum(e0); return Numerics.ReduceSum(e0);
} }
@ -110,15 +121,20 @@ internal static class LossyUtils
Vector128<byte> b23s = Sse2.UnpackLow(b23.AsByte(), Vector128<byte>.Zero); Vector128<byte> b23s = Sse2.UnpackLow(b23.AsByte(), Vector128<byte>.Zero);
// subtract, square and accumulate. // subtract, square and accumulate.
Vector128<byte> d0 = Sse2.SubtractSaturate(a01s, b01s); Vector128<short> d0 = Sse2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16());
Vector128<byte> d1 = Sse2.SubtractSaturate(a23s, b23s); Vector128<short> d1 = Sse2.SubtractSaturate(a23s.AsInt16(), b23s.AsInt16());
Vector128<int> e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); Vector128<int> e0 = Sse2.MultiplyAddAdjacent(d0, d0);
Vector128<int> e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16()); Vector128<int> e1 = Sse2.MultiplyAddAdjacent(d1, d1);
Vector128<int> sum = Sse2.Add(e0, e1); Vector128<int> sum = Sse2.Add(e0, e1);
return Numerics.ReduceSum(sum); return Numerics.ReduceSum(sum);
} }
if (AdvSimd.IsSupported)
{
return Vp8_Sse4x4_Neon(a, b);
}
return Vp8_SseNxN(a, b, 4, 4); 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) public static int Vp8_SseNxN(Span<byte> a, Span<byte> b, int w, int h)
{ {
int count = 0; int count = 0;
int aOffset = 0; int offset = 0;
int bOffset = 0;
for (int y = 0; y < h; y++) for (int y = 0; y < h; y++)
{ {
for (int x = 0; x < w; x++) 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; count += diff * diff;
} }
aOffset += WebpConstants.Bps; offset += WebpConstants.Bps;
bOffset += WebpConstants.Bps;
} }
return count; return count;
@ -201,6 +215,106 @@ internal static class LossyUtils
return Numerics.ReduceSum(sum); 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)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<int> SubtractAndAccumulate(Vector128<byte> a, Vector128<byte> b) 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); rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode);
// Measure RD-score. // 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.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0;
rdCur.H = WebpConstants.Vp8FixedCostsI16[mode]; rdCur.H = WebpConstants.Vp8FixedCostsI16[mode];
rdCur.R = it.GetCostLuma16(rdCur, proba, res); 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); rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode);
// Compute RD-score. // 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.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0;
rdTmp.H = modeCosts[mode]; rdTmp.H = modeCosts[mode];
@ -235,7 +235,7 @@ internal static unsafe class QuantEnc
rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode); rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode);
// Compute RD-score // 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.SD = 0; // not calling TDisto here: it tends to flatten areas.
rdUv.H = WebpConstants.Vp8FixedCostsUv[mode]; rdUv.H = WebpConstants.Vp8FixedCostsUv[mode];
rdUv.R = it.GetCostUv(rdUv, proba, res); rdUv.R = it.GetCostUv(rdUv, proba, res);
@ -389,7 +389,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[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) if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit)
{ {
@ -436,7 +436,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumBModes; ++mode) for (mode = 0; mode < WebpConstants.NumBModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[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) if (score < bestI4Score)
{ {
bestI4Mode = mode; bestI4Mode = mode;
@ -485,7 +485,7 @@ internal static unsafe class QuantEnc
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[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) if (score < bestUvScore)
{ {
bestMode = mode; bestMode = mode;

41
src/ImageSharp/IO/ChunkedMemoryStream.cs

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

2
src/ImageSharp/Image.Decode.cs

@ -91,7 +91,7 @@ public abstract partial class Image
ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager); ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
} }
return format!; return format;
} }
/// <summary> /// <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="pixelType">The pixel type information.</param>
/// <param name="metadata">The image metadata.</param> /// <param name="metadata">The image metadata.</param>
/// <param name="size">The size in px units.</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) : base(pixelType, size, metadata)
=> this.configuration = configuration ?? Configuration.Default; => this.configuration = configuration;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Image"/> class. /// Initializes a new instance of the <see cref="Image"/> class.
@ -43,7 +43,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv
internal Image( internal Image(
Configuration configuration, Configuration configuration,
PixelTypeInfo pixelType, PixelTypeInfo pixelType,
ImageMetadata metadata, ImageMetadata? metadata,
int width, int width,
int height) int height)
: this(configuration, pixelType, metadata, new Size(width, 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="width">The width of the image in px units.</param>
/// <param name="height">The height 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> /// <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) : this(pixelType, new(width, height), metadata)
{ {
} }
@ -29,7 +29,7 @@ public class ImageInfo
/// <param name="pixelType">The pixel type information.</param> /// <param name="pixelType">The pixel type information.</param>
/// <param name="size">The size of the image in px units.</param> /// <param name="size">The size of the image in px units.</param>
/// <param name="metadata">The image metadata.</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.PixelType = pixelType;
this.Metadata = metadata ?? new ImageMetadata(); 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="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param> /// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height) : base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel)); => this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
@ -128,7 +128,7 @@ public sealed class Image<TPixel> : Image
int width, int width,
int height, int height,
TPixel backgroundColor, TPixel backgroundColor,
ImageMetadata metadata) ImageMetadata? metadata)
: base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height) : base(configuration, PixelTypeInfo.Create<TPixel>(), metadata, width, height)
=> this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor); => this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor);

15
src/ImageSharp/Metadata/ImageFrameMetadata.cs

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

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

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

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Text; using System.Text;
@ -22,10 +21,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
other.data.AsSpan().CopyTo(this.data); 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.Tag = other.Tag;
this.Strict = other.Strict; this.Strict = other.Strict;
@ -133,7 +129,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
/// </summary> /// </summary>
/// <param name="obj">The object to compare this <see cref="IptcValue"/> with.</param> /// <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> /// <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)) if (ReferenceEquals(this, obj))
{ {
@ -148,7 +144,7 @@ public sealed class IptcValue : IDeepCloneable<IptcValue>
/// </summary> /// </summary>
/// <param name="other">The iptc value to compare this <see cref="IptcValue"/> with.</param> /// <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> /// <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) if (other is null)
{ {

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

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

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

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

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -335,7 +334,7 @@ public partial struct Abgr32 : IPixel<Abgr32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -334,7 +333,7 @@ public partial struct Argb32 : IPixel<Argb32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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); public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
/// <inheritdoc/> /// <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 /> /// <inheritdoc />
public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})"; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -151,7 +150,7 @@ public partial struct Bgr565 : IPixel<Bgr565>, IPackedVector<ushort>
(this.PackedValue & 0x1F) * (1F / 31F)); (this.PackedValue & 0x1F) * (1F / 31F));
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -287,7 +286,7 @@ public partial struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
} }
/// <inheritdoc/> /// <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/> /// <inheritdoc/>
public readonly bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra); 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

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

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -157,7 +156,7 @@ public partial struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
} }
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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); public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B);
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -143,7 +142,7 @@ public partial struct L8 : IPixel<L8>, IPackedVector<byte>
ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); ColorNumerics.DownScaleFrom16BitTo8Bit(source.B));
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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); public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
public override readonly string ToString() => $"La16({this.L}, {this.A})"; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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); public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue);
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
public override readonly string ToString() => $"La32({this.L}, {this.A})"; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -162,7 +161,7 @@ public partial struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<u
(sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos); (sbyte)((this.PackedValue >> 8) & 0xFF) / MaxPos);
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -163,7 +162,7 @@ public partial struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector
(short)(this.PackedValue >> 0x10) / MaxPos); (short)(this.PackedValue >> 0x10) / MaxPos);
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -155,7 +154,7 @@ public partial struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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; public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max;
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -241,7 +240,7 @@ public partial struct Rgb24 : IPixel<Rgb24>
} }
/// <inheritdoc/> /// <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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -210,7 +209,7 @@ public partial struct Rgb48 : IPixel<Rgb48>
public void FromRgb48(Rgb48 source) => this = source; public void FromRgb48(Rgb48 source) => this = source;
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

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

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -444,7 +443,7 @@ public partial struct Rgba64 : IPixel<Rgba64>, IPackedVector<ulong>
} }
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Globalization; using System.Globalization;
using System.Numerics; using System.Numerics;
@ -193,7 +192,7 @@ public partial struct RgbaVector : IPixel<RgbaVector>
} }
/// <inheritdoc/> /// <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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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)); public readonly Vector2 ToVector2() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10));
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; 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()); public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
/// <inheritdoc /> /// <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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

24
src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs

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

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

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

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

@ -14,9 +14,9 @@ public static class BinaryDitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to two colors using ordered dithering. /// Dithers the image reducing it to two colors using ordered dithering.
/// </summary> /// </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="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 public static IImageProcessingContext
BinaryDither(this IImageProcessingContext source, IDither dither) => BinaryDither(this IImageProcessingContext source, IDither dither) =>
BinaryDither(source, dither, Color.White, Color.Black); BinaryDither(source, dither, Color.White, Color.Black);
@ -24,11 +24,11 @@ public static class BinaryDitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to two colors using ordered dithering. /// Dithers the image reducing it to two colors using ordered dithering.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</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="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( public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -39,12 +39,12 @@ public static class BinaryDitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to two colors using ordered dithering. /// Dithers the image reducing it to two colors using ordered dithering.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryDither( public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -54,14 +54,14 @@ public static class BinaryDitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to two colors using ordered dithering. /// Dithers the image reducing it to two colors using ordered dithering.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="upperColor">The color to use for pixels that are above the threshold.</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="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryDither( public static IImageProcessingContext BinaryDither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, 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 /// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold. /// Luminance as the color component to be compared to threshold.
/// </summary> /// </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="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) public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold)
=> source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance)); => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance));
/// <summary> /// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold. /// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary> /// </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="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="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( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -38,12 +38,12 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with /// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold. /// Luminance as the color component to be compared to threshold.
/// </summary> /// </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="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -53,13 +53,13 @@ public static class BinaryThresholdExtensions
/// <summary> /// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold. /// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary> /// </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="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="mode">Selects the value to be compared to threshold.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -71,11 +71,11 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with /// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold. /// Luminance as the color component to be compared to threshold.
/// </summary> /// </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="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="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="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( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -86,12 +86,12 @@ public static class BinaryThresholdExtensions
/// <summary> /// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold. /// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary> /// </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="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="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="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> /// <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( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -104,14 +104,14 @@ public static class BinaryThresholdExtensions
/// Applies binarization to the image splitting the pixels at the given threshold with /// Applies binarization to the image splitting the pixels at the given threshold with
/// Luminance as the color component to be compared to threshold. /// Luminance as the color component to be compared to threshold.
/// </summary> /// </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="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="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="lowerColor">The color to use for pixels that are below the threshold</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,
@ -123,7 +123,7 @@ public static class BinaryThresholdExtensions
/// <summary> /// <summary>
/// Applies binarization to the image splitting the pixels at the given threshold. /// Applies binarization to the image splitting the pixels at the given threshold.
/// </summary> /// </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="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="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="lowerColor">The color to use for pixels that are below the threshold</param>
@ -131,7 +131,7 @@ public static class BinaryThresholdExtensions
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext BinaryThreshold( public static IImageProcessingContext BinaryThreshold(
this IImageProcessingContext source, this IImageProcessingContext source,
float threshold, float threshold,

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

@ -13,44 +13,44 @@ public static class BokehBlurExtensions
/// <summary> /// <summary>
/// Applies a bokeh blur to the image. /// Applies a bokeh blur to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext BokehBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BokehBlurProcessor()); => source.ApplyProcessor(new BokehBlurProcessor());
/// <summary> /// <summary>
/// Applies a bokeh blur to the image. /// Applies a bokeh blur to the image.
/// </summary> /// </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="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="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="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) public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma));
/// <summary> /// <summary>
/// Applies a bokeh blur to the image. /// Applies a bokeh blur to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(), rectangle); => source.ApplyProcessor(new BokehBlurProcessor(), rectangle);
/// <summary> /// <summary>
/// Applies a bokeh blur to the image. /// Applies a bokeh blur to the image.
/// </summary> /// </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="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="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="gamma">The gamma highlight factor to use to emphasize bright spots in the source image</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,36 +14,36 @@ public static class BoxBlurExtensions
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext BoxBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BoxBlurProcessor()); => source.ApplyProcessor(new BoxBlurProcessor());
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </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="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) public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius)
=> source.ApplyProcessor(new BoxBlurProcessor(radius)); => source.ApplyProcessor(new BoxBlurProcessor(radius));
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </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="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle)
=> source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle);
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </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="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// 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"> /// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction. /// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param> /// </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) public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{ {
var processor = new BoxBlurProcessor(radius, borderWrapModeX, 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. /// Detects any edges within the image.
/// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode. /// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) =>
DetectEdges(source, KnownEdgeDetectorKernels.Sobel); DetectEdges(source, KnownEdgeDetectorKernels.Sobel);
@ -23,11 +23,11 @@ public static class DetectEdgesExtensions
/// Detects any edges within the image. /// Detects any edges within the image.
/// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode. /// Uses the <see cref="KnownEdgeDetectorKernels.Sobel"/> kernel operating in grayscale mode.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
Rectangle rectangle) => Rectangle rectangle) =>
@ -36,9 +36,9 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="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( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetector2DKernel kernel) => EdgeDetector2DKernel kernel) =>
@ -47,12 +47,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>.
/// </summary> /// </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="kernel">The 2D edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetector2DKernel kernel, EdgeDetector2DKernel kernel,
@ -66,12 +66,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="kernel">The 2D edge detector kernel.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetector2DKernel kernel, EdgeDetector2DKernel kernel,
@ -81,7 +81,7 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetector2DKernel"/>.
/// </summary> /// </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="kernel">The 2D edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
@ -89,7 +89,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetector2DKernel kernel, EdgeDetector2DKernel kernel,
@ -104,9 +104,9 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="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( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorKernel kernel) => EdgeDetectorKernel kernel) =>
@ -115,12 +115,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>.
/// </summary> /// </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="kernel">The edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorKernel kernel, EdgeDetectorKernel kernel,
@ -134,12 +134,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="kernel">The edge detector kernel.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorKernel kernel, EdgeDetectorKernel kernel,
@ -149,7 +149,7 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetectorKernel"/>.
/// </summary> /// </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="kernel">The edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
@ -157,7 +157,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorKernel kernel, EdgeDetectorKernel kernel,
@ -172,9 +172,9 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="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( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel) => EdgeDetectorCompassKernel kernel) =>
@ -183,12 +183,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>.
/// </summary> /// </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="kernel">Thecompass edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel, EdgeDetectorCompassKernel kernel,
@ -202,12 +202,12 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image operating in grayscale mode. /// Detects any edges within the image operating in grayscale mode.
/// </summary> /// </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="kernel">Thecompass edge detector kernel.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel, EdgeDetectorCompassKernel kernel,
@ -217,7 +217,7 @@ public static class DetectEdgesExtensions
/// <summary> /// <summary>
/// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>. /// Detects any edges within the image using a <see cref="EdgeDetectorCompassKernel"/>.
/// </summary> /// </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="kernel">Thecompass edge detector kernel.</param>
/// <param name="grayscale"> /// <param name="grayscale">
/// Whether to convert the image to grayscale before performing edge detection. /// Whether to convert the image to grayscale before performing edge detection.
@ -225,7 +225,7 @@ public static class DetectEdgesExtensions
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DetectEdges( public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source, this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel, 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,36 +14,36 @@ public static class GaussianBlurExtensions
/// <summary> /// <summary>
/// Applies a Gaussian blur to the image. /// Applies a Gaussian blur to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new GaussianBlurProcessor()); => source.ApplyProcessor(new GaussianBlurProcessor());
/// <summary> /// <summary>
/// Applies a Gaussian blur to the image. /// Applies a Gaussian blur to the image.
/// </summary> /// </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="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) public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma)); => source.ApplyProcessor(new GaussianBlurProcessor(sigma));
/// <summary> /// <summary>
/// Applies a Gaussian blur to the image. /// Applies a Gaussian blur to the image.
/// </summary> /// </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="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle);
/// <summary> /// <summary>
/// Applies a Gaussian blur to the image. /// Applies a Gaussian blur to the image.
/// </summary> /// </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="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// 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"> /// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction. /// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param> /// </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) public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{ {
var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,29 +14,29 @@ public static class GaussianSharpenExtensions
/// <summary> /// <summary>
/// Applies a Gaussian sharpening filter to the image. /// Applies a Gaussian sharpening filter to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) =>
source.ApplyProcessor(new GaussianSharpenProcessor()); source.ApplyProcessor(new GaussianSharpenProcessor());
/// <summary> /// <summary>
/// Applies a Gaussian sharpening filter to the image. /// Applies a Gaussian sharpening filter to the image.
/// </summary> /// </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="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) => public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); source.ApplyProcessor(new GaussianSharpenProcessor(sigma));
/// <summary> /// <summary>
/// Applies a Gaussian sharpening filter to the image. /// Applies a Gaussian sharpening filter to the image.
/// </summary> /// </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="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext GaussianSharpen( public static IImageProcessingContext GaussianSharpen(
this IImageProcessingContext source, this IImageProcessingContext source,
float sigma, float sigma,
@ -46,7 +46,7 @@ public static class GaussianSharpenExtensions
/// <summary> /// <summary>
/// Applies a Gaussian sharpening filter to the image. /// Applies a Gaussian sharpening filter to the image.
/// </summary> /// </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="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// 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"> /// <param name="borderWrapModeY">
/// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction. /// The <see cref="BorderWrappingMode"/> to use when mapping the pixels outside of the border, in Y direction.
/// </param> /// </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) public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{ {
var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Convolution;
@ -14,19 +14,19 @@ public static class MedianBlurExtensions
/// <summary> /// <summary>
/// Applies a median blur on the image. /// Applies a median blur on the image.
/// </summary> /// </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="radius">The radius of the area to find the median for.</param>
/// <param name="preserveAlpha"> /// <param name="preserveAlpha">
/// Whether the filter is applied to alpha as well as the color channels. /// Whether the filter is applied to alpha as well as the color channels.
/// </param> /// </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) public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha)); => source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha));
/// <summary> /// <summary>
/// Applies a median blur on the image. /// Applies a median blur on the image.
/// </summary> /// </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="radius">The radius of the area to find the median for.</param>
/// <param name="preserveAlpha"> /// <param name="preserveAlpha">
/// Whether the filter is applied to alpha as well as the color channels. /// Whether the filter is applied to alpha as well as the color channels.
@ -34,7 +34,7 @@ public static class MedianBlurExtensions
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha, Rectangle rectangle)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha), 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> /// <summary>
/// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>. /// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => public static IImageProcessingContext Dither(this IImageProcessingContext source) =>
Dither(source, KnownDitherings.Bayer8x8); Dither(source, KnownDitherings.Bayer8x8);
/// <summary> /// <summary>
/// Dithers the image reducing it to a web-safe palette. /// Dithers the image reducing it to a web-safe palette.
/// </summary> /// </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="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( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither) => IDither dither) =>
@ -33,10 +33,10 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to a web-safe palette. /// Dithers the image reducing it to a web-safe palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</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( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -46,10 +46,10 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to the given palette. /// Dithers the image reducing it to the given palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="palette">The palette to select substitute colors from.</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( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -59,11 +59,11 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to the given palette. /// Dithers the image reducing it to the given palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</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="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( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -74,23 +74,23 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>. /// Dithers the image reducing it to a web-safe palette using <see cref="KnownDitherings.Bayer8x8"/>.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) => public static IImageProcessingContext Dither(this IImageProcessingContext source, Rectangle rectangle) =>
Dither(source, KnownDitherings.Bayer8x8, rectangle); Dither(source, KnownDitherings.Bayer8x8, rectangle);
/// <summary> /// <summary>
/// Dithers the image reducing it to a web-safe palette. /// Dithers the image reducing it to a web-safe palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -100,13 +100,13 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to a web-safe palette. /// Dithers the image reducing it to a web-safe palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param> /// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -117,13 +117,13 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to the given palette. /// Dithers the image reducing it to the given palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="palette">The palette to select substitute colors from.</param> /// <param name="palette">The palette to select substitute colors from.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, IDither dither,
@ -134,14 +134,14 @@ public static class DitherExtensions
/// <summary> /// <summary>
/// Dithers the image reducing it to the given palette. /// Dithers the image reducing it to the given palette.
/// </summary> /// </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="dither">The ordered ditherer.</param>
/// <param name="ditherScale">The dithering scale used to adjust the amount of dither.</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="palette">The palette to select substitute colors from.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Dither( public static IImageProcessingContext Dither(
this IImageProcessingContext source, this IImageProcessingContext source,
IDither dither, 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Effects; 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 /// 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. /// set to <value>10</value> and <value>15</value> respectively.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => OilPaint(source, 10, 15); public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15);
/// <summary> /// <summary>
/// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// 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. /// set to <value>10</value> and <value>15</value> respectively.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) => public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) =>
OilPaint(source, 10, 15, rectangle); OilPaint(source, 10, 15, rectangle);
/// <summary> /// <summary>
/// Alters the colors of the image recreating an oil painting effect. /// Alters the colors of the image recreating an oil painting effect.
/// </summary> /// </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="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="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 public static IImageProcessingContext
OilPaint(this IImageProcessingContext source, int levels, int brushSize) => OilPaint(this IImageProcessingContext source, int levels, int brushSize) =>
source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize));
@ -45,13 +45,13 @@ public static class OilPaintExtensions
/// <summary> /// <summary>
/// Alters the colors of the image recreating an oil painting effect. /// Alters the colors of the image recreating an oil painting effect.
/// </summary> /// </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="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="brushSize">The number of neighboring pixels used in calculating each individual pixel value.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext OilPaint( public static IImageProcessingContext OilPaint(
this IImageProcessingContext source, this IImageProcessingContext source,
int levels, int levels,

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

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

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

@ -14,28 +14,28 @@ public static class PixelateExtensions
/// <summary> /// <summary>
/// Pixelates an image with the given pixel size. /// Pixelates an image with the given pixel size.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => Pixelate(source, 4); public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4);
/// <summary> /// <summary>
/// Pixelates an image with the given pixel size. /// Pixelates an image with the given pixel size.
/// </summary> /// </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="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) => public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) =>
source.ApplyProcessor(new PixelateProcessor(size)); source.ApplyProcessor(new PixelateProcessor(size));
/// <summary> /// <summary>
/// Pixelates an image with the given pixel size. /// Pixelates an image with the given pixel size.
/// </summary> /// </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="size">The size of the pixels.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Pixelate( public static IImageProcessingContext Pixelate(
this IImageProcessingContext source, this IImageProcessingContext source,
int size, 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class BlackWhiteExtensions
/// <summary> /// <summary>
/// Applies black and white toning to the image. /// Applies black and white toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext BlackWhite(this IImageProcessingContext source)
=> source.ApplyProcessor(new BlackWhiteProcessor()); => source.ApplyProcessor(new BlackWhiteProcessor());
/// <summary> /// <summary>
/// Applies black and white toning to the image. /// Applies black and white toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BlackWhiteProcessor(), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; 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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks> /// </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="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) public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new BrightnessProcessor(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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks> /// </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="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new BrightnessProcessor(amount), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
@ -15,21 +15,21 @@ public static class ColorBlindnessExtensions
/// <summary> /// <summary>
/// Applies the given colorblindness simulator to the image. /// Applies the given colorblindness simulator to the image.
/// </summary> /// </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> /// <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) public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness)
=> source.ApplyProcessor(GetProcessor(colorBlindness)); => source.ApplyProcessor(GetProcessor(colorBlindness));
/// <summary> /// <summary>
/// Applies the given colorblindness simulator to the image. /// Applies the given colorblindness simulator to the image.
/// </summary> /// </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="colorBlindnessMode">The type of color blindness simulator to apply.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle)
=> source.ApplyProcessor(GetProcessor(colorBlindnessMode), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; 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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks> /// </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="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) public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new ContrastProcessor(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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks> /// </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="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new ContrastProcessor(amount), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class FilterExtensions
/// <summary> /// <summary>
/// Filters an image by the given color matrix /// Filters an image by the given color matrix
/// </summary> /// </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="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) public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix)
=> source.ApplyProcessor(new FilterProcessor(matrix)); => source.ApplyProcessor(new FilterProcessor(matrix));
/// <summary> /// <summary>
/// Filters an image by the given color matrix /// Filters an image by the given color matrix
/// </summary> /// </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="matrix">The filter color matrix</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle)
=> source.ApplyProcessor(new FilterProcessor(matrix), rectangle); => source.ApplyProcessor(new FilterProcessor(matrix), rectangle);
} }

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

@ -15,7 +15,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image. /// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image.
/// </summary> /// </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> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source) public static IImageProcessingContext Grayscale(this IImageProcessingContext source)
=> Grayscale(source, GrayscaleMode.Bt709); => Grayscale(source, GrayscaleMode.Bt709);
@ -23,7 +23,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount. /// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount.
/// </summary> /// </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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount)
@ -32,7 +32,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/>. /// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/>.
/// </summary> /// </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="mode">The formula to apply to perform the operation.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode)
@ -41,7 +41,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/> using the given amount. /// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/> using the given amount.
/// </summary> /// </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="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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
@ -58,7 +58,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image. /// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
@ -69,7 +69,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount. /// Applies <see cref="GrayscaleMode.Bt709"/> grayscale toning to the image using the given amount.
/// </summary> /// </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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -81,7 +81,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies grayscale toning to the image. /// Applies grayscale toning to the image.
/// </summary> /// </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="mode">The formula to apply to perform the operation.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
@ -93,7 +93,7 @@ public static class GrayscaleExtensions
/// <summary> /// <summary>
/// Applies grayscale toning to the image using the given amount. /// Applies grayscale toning to the image using the given amount.
/// </summary> /// </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="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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class HueExtensions
/// <summary> /// <summary>
/// Alters the hue component of the image. /// Alters the hue component of the image.
/// </summary> /// </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="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) public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees)
=> source.ApplyProcessor(new HueProcessor(degrees)); => source.ApplyProcessor(new HueProcessor(degrees));
/// <summary> /// <summary>
/// Alters the hue component of the image. /// Alters the hue component of the image.
/// </summary> /// </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="degrees">The rotation angle in degrees to adjust the hue.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle)
=> source.ApplyProcessor(new HueProcessor(degrees), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class InvertExtensions
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext Invert(this IImageProcessingContext source)
=> source.ApplyProcessor(new InvertProcessor(1F)); => source.ApplyProcessor(new InvertProcessor(1F));
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new InvertProcessor(1F), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,19 +14,19 @@ public static class KodachromeExtensions
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect. /// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext Kodachrome(this IImageProcessingContext source)
=> source.ApplyProcessor(new KodachromeProcessor()); => source.ApplyProcessor(new KodachromeProcessor());
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect. /// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new KodachromeProcessor(), 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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results.
/// </remarks> /// </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="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) public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new LightnessProcessor(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. /// 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. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results.
/// </remarks> /// </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="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new LightnessProcessor(amount), rectangle); => source.ApplyProcessor(new LightnessProcessor(amount), rectangle);
} }

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

@ -14,19 +14,19 @@ public static class LomographExtensions
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect. /// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext Lomograph(this IImageProcessingContext source)
=> source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions())); => source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()));
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect. /// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,21 +14,21 @@ public static class OpacityExtensions
/// <summary> /// <summary>
/// Multiplies the alpha component of the image. /// Multiplies the alpha component of the image.
/// </summary> /// </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="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) public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new OpacityProcessor(amount)); => source.ApplyProcessor(new OpacityProcessor(amount));
/// <summary> /// <summary>
/// Multiplies the alpha component of the image. /// Multiplies the alpha component of the image.
/// </summary> /// </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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new OpacityProcessor(amount), rectangle); => source.ApplyProcessor(new OpacityProcessor(amount), rectangle);
} }

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

@ -14,19 +14,19 @@ public static class PolaroidExtensions
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect. /// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext Polaroid(this IImageProcessingContext source)
=> source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions())); => source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()));
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect. /// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; 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. /// 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 /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks> /// </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="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) public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new SaturateProcessor(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. /// 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 /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks> /// </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="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new SaturateProcessor(amount), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;
@ -14,40 +14,40 @@ public static class SepiaExtensions
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) public static IImageProcessingContext Sepia(this IImageProcessingContext source)
=> Sepia(source, 1F); => Sepia(source, 1F);
/// <summary> /// <summary>
/// Applies sepia toning to the image using the given amount. /// Applies sepia toning to the image using the given amount.
/// </summary> /// </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="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) public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount)
=> source.ApplyProcessor(new SepiaProcessor(amount)); => source.ApplyProcessor(new SepiaProcessor(amount));
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle)
=> Sepia(source, 1F, rectangle); => Sepia(source, 1F, rectangle);
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </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="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </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) public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle)
=> source.ApplyProcessor(new SepiaProcessor(amount), 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. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Normalization; using SixLabors.ImageSharp.Processing.Processors.Normalization;
@ -13,17 +13,17 @@ public static class HistogramEqualizationExtensions
/// <summary> /// <summary>
/// Equalizes the histogram of an image to increases the contrast. /// Equalizes the histogram of an image to increases the contrast.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The current image processing context.</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) => public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) =>
HistogramEqualization(source, new HistogramEqualizationOptions()); HistogramEqualization(source, new HistogramEqualizationOptions());
/// <summary> /// <summary>
/// Equalizes the histogram of an image to increases the contrast. /// Equalizes the histogram of an image to increases the contrast.
/// </summary> /// </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> /// <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( public static IImageProcessingContext HistogramEqualization(
this IImageProcessingContext source, this IImageProcessingContext source,
HistogramEqualizationOptions options) => HistogramEqualizationOptions options) =>

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

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

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

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

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

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

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

Loading…
Cancel
Save