diff --git a/.editorconfig b/.editorconfig
index c28089d720..af1e5b44c1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -104,8 +104,8 @@ dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:war
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_operators = always_for_clarity:suggestion
# Expression-level preferences
-dotnet_style_object_initializer = true:warning
-dotnet_style_collection_initializer = true:warning
+dotnet_style_object_initializer = true:error
+dotnet_style_collection_initializer = true:error
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
@@ -135,9 +135,9 @@ csharp_style_prefer_null_check_over_type_check = true:warning
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules
[*.{cs,csx,cake}]
# 'var' preferences
-csharp_style_var_for_built_in_types = false:warning
-csharp_style_var_when_type_is_apparent = false:warning
-csharp_style_var_elsewhere = false:warning
+csharp_style_var_for_built_in_types = false:error
+csharp_style_var_when_type_is_apparent = false:error
+csharp_style_var_elsewhere = false:error
# Expression-bodied members
csharp_style_expression_bodied_methods = true:warning
csharp_style_expression_bodied_constructors = true:warning
@@ -160,7 +160,7 @@ csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_style_deconstructed_variable_declaration = true:warning
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_range_operator = true:warning
-csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
+csharp_style_implicit_object_creation_when_type_is_apparent = true:error
# "Null" checking preferences
csharp_style_throw_expression = true:warning
csharp_style_conditional_delegate_call = true:warning
diff --git a/.gitattributes b/.gitattributes
index b5f742ab47..f7bd4d061e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -136,3 +136,10 @@
*.ico filter=lfs diff=lfs merge=lfs -text
*.cur filter=lfs diff=lfs merge=lfs -text
*.ani filter=lfs diff=lfs merge=lfs -text
+*.heic filter=lfs diff=lfs merge=lfs -text
+*.hif filter=lfs diff=lfs merge=lfs -text
+*.avif filter=lfs diff=lfs merge=lfs -text
+###############################################################################
+# Handle ICC files by git lfs
+###############################################################################
+*.icc filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index a450aebf43..cb90793e04 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -19,6 +19,31 @@ jobs:
isARM:
- ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }}
options:
+ - os: ubuntu-latest
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: buildjet-4vcpu-ubuntu-2204-arm
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+
- os: ubuntu-latest
framework: net8.0
sdk: 8.0.x
@@ -48,8 +73,10 @@ jobs:
steps:
- name: Install libgdi+, which is required for tests running on ubuntu
- if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }}
- run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev
+ if: ${{ contains(matrix.options.os, 'ubuntu') }}
+ run: |
+ sudo apt-get update
+ sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev
- name: Git Config
shell: bash
@@ -100,7 +127,7 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 8.0.x
+ 9.0.x
- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index cd22fe5e58..b4965795c3 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -17,6 +17,13 @@ jobs:
runs-on: ${{matrix.options.os}}
steps:
+
+ - name: Install libgdi+, which is required for tests running on ubuntu
+ if: ${{ contains(matrix.options.os, 'ubuntu') }}
+ run: |
+ sudo apt-get update
+ sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev
+
- name: Git Config
shell: bash
run: |
diff --git a/Directory.Build.props b/Directory.Build.props
index 26b3cc5afc..755cbe3b30 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -21,9 +21,8 @@
-
-
- preview
+
+ 12.0
@@ -29,14 +30,12 @@
- net8.0
- true
+ net8.0;net9.0
net8.0
- true
@@ -52,20 +51,15 @@
-
+
True
True
- ImageMetadataExtensions.tt
+ InlineArray.tt
-
- True
- True
- Block8x8F.Generated.tt
-
-
+
True
True
- Block8x8F.Generated.tt
+ ImageMetadataExtensions.tt
True
@@ -155,17 +149,13 @@
-
- ImageMetadataExtensions.cs
+
TextTemplatingFileGenerator
+ InlineArray.cs
-
- TextTemplatingFileGenerator
- Block8x8F.Generated.cs
-
-
+
+ ImageMetadataExtensions.cs
TextTemplatingFileGenerator
- Block8x8F.Generated.cs
TextTemplatingFileGenerator
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 02403923d2..7ec7918381 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -160,7 +160,7 @@ public sealed class Image : Image
///
/// Gets the root frame.
///
- private IPixelSource PixelSourceUnsafe => this.frames.RootFrameUnsafe;
+ private ImageFrame PixelSourceUnsafe => this.frames.RootFrameUnsafe;
///
/// Gets or sets the pixel at the specified position.
@@ -324,7 +324,7 @@ public sealed class Image : Image
}
///
- /// Clones the current image
+ /// Clones the current image.
///
/// Returns a new image with all the same metadata as the original.
public Image Clone() => this.Clone(this.Configuration);
diff --git a/src/ImageSharp/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/IndexedImageFrame{TPixel}.cs
index 6807e77ad2..a88cdb524e 100644
--- a/src/ImageSharp/IndexedImageFrame{TPixel}.cs
+++ b/src/ImageSharp/IndexedImageFrame{TPixel}.cs
@@ -25,12 +25,12 @@ public sealed class IndexedImageFrame : IPixelSource, IDisposable
/// Initializes a new instance of the class.
///
///
- /// The configuration which allows altering default behaviour or extending the library.
+ /// The configuration which allows altering default behavior or extending the library.
///
/// The frame width.
/// The frame height.
/// The color palette.
- internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory palette)
+ public IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory palette)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeLessThanOrEqualTo(palette.Length, QuantizerConstants.MaxColors, nameof(palette));
@@ -42,14 +42,14 @@ public sealed class IndexedImageFrame : IPixelSource, IDisposable
this.Height = height;
this.pixelBuffer = configuration.MemoryAllocator.Allocate2D(width, height);
- // Copy the palette over. We want the lifetime of this frame to be independant of any palette source.
+ // Copy the palette over. We want the lifetime of this frame to be independent of any palette source.
this.paletteOwner = configuration.MemoryAllocator.Allocate(palette.Length);
palette.Span.CopyTo(this.paletteOwner.GetSpan());
this.Palette = this.paletteOwner.Memory[..palette.Length];
}
///
- /// Gets the configuration which allows altering default behaviour or extending the library.
+ /// Gets the configuration which allows altering default behavior or extending the library.
///
public Configuration Configuration { get; }
diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs
index 2eb05ea935..f0fa1438dd 100644
--- a/src/ImageSharp/Memory/Buffer2DExtensions.cs
+++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs
@@ -25,27 +25,65 @@ public static class Buffer2DExtensions
return buffer.FastMemoryGroup.View;
}
+ ///
+ /// Performs a deep clone of the buffer covering the specified .
+ ///
+ /// The element type.
+ /// The source buffer.
+ /// The configuration.
+ /// The rectangle to clone.
+ /// The .
+ internal static Buffer2D CloneRegion(this Buffer2D source, Configuration configuration, Rectangle rectangle)
+ where T : unmanaged
+ {
+ Buffer2D buffer = configuration.MemoryAllocator.Allocate2D(
+ rectangle.Width,
+ rectangle.Height,
+ configuration.PreferContiguousImageBuffers);
+
+ // Optimization for when the size of the area is the same as the buffer size.
+ Buffer2DRegion sourceRegion = source.GetRegion(rectangle);
+ if (sourceRegion.IsFullBufferArea)
+ {
+ sourceRegion.Buffer.FastMemoryGroup.CopyTo(buffer.FastMemoryGroup);
+ }
+ else
+ {
+ for (int y = 0; y < rectangle.Height; y++)
+ {
+ sourceRegion.DangerousGetRowSpan(y).CopyTo(buffer.DangerousGetRowSpan(y));
+ }
+ }
+
+ return buffer;
+ }
+
///
/// TODO: Does not work with multi-buffer groups, should be specific to Resize.
- /// Copy columns of inplace,
- /// from positions starting at to positions at .
+ /// Copy columns of in-place,
+ /// from positions starting at to positions at .
///
+ /// The element type.
+ /// The .
+ /// The source column index.
+ /// The destination column index.
+ /// The number of columns to copy.
internal static unsafe void DangerousCopyColumns(
this Buffer2D buffer,
int sourceIndex,
- int destIndex,
+ int destinationIndex,
int columnCount)
where T : struct
{
DebugGuard.NotNull(buffer, nameof(buffer));
DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex));
- DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, 0, nameof(sourceIndex));
- CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destIndex, columnCount);
+ DebugGuard.MustBeGreaterThanOrEqualTo(destinationIndex, 0, nameof(sourceIndex));
+ CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destinationIndex, columnCount);
int elementSize = Unsafe.SizeOf();
int width = buffer.Width * elementSize;
int sOffset = sourceIndex * elementSize;
- int dOffset = destIndex * elementSize;
+ int dOffset = destinationIndex * elementSize;
long count = columnCount * elementSize;
Span span = MemoryMarshal.AsBytes(buffer.DangerousGetSingleMemory().Span);
@@ -73,9 +111,7 @@ public static class Buffer2DExtensions
/// The
internal static Rectangle FullRectangle(this Buffer2D buffer)
where T : struct
- {
- return new Rectangle(0, 0, buffer.Width, buffer.Height);
- }
+ => new(0, 0, buffer.Width, buffer.Height);
///
/// Return a to the subregion represented by 'rectangle'
@@ -86,11 +122,11 @@ public static class Buffer2DExtensions
/// The
internal static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle)
where T : unmanaged =>
- new Buffer2DRegion(buffer, rectangle);
+ new(buffer, rectangle);
internal static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height)
where T : unmanaged =>
- new Buffer2DRegion(buffer, new Rectangle(x, y, width, height));
+ new(buffer, new Rectangle(x, y, width, height));
///
/// Return a to the whole area of 'buffer'
@@ -100,7 +136,7 @@ public static class Buffer2DExtensions
/// The
internal static Buffer2DRegion GetRegion(this Buffer2D buffer)
where T : unmanaged =>
- new Buffer2DRegion(buffer);
+ new(buffer);
///
/// Returns the size of the buffer.
@@ -115,6 +151,8 @@ public static class Buffer2DExtensions
///
/// Gets the bounds of the buffer.
///
+ /// The element type
+ /// The
/// The
internal static Rectangle Bounds(this Buffer2D buffer)
where T : struct =>
diff --git a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
index 033b0a25a6..f4b257b587 100644
--- a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
+++ b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
@@ -107,7 +107,7 @@ public readonly struct Buffer2DRegion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Buffer2DRegion GetSubRegion(int x, int y, int width, int height)
{
- var rectangle = new Rectangle(x, y, width, height);
+ Rectangle rectangle = new(x, y, width, height);
return this.GetSubRegion(rectangle);
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
index b4b1ffc6f4..e2e933f3cc 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
@@ -36,8 +36,13 @@ internal static class MemoryGroupExtensions
///
/// Returns a slice that is expected to be within the bounds of a single buffer.
- /// Otherwise is thrown.
///
+ /// The type of element.
+ /// The group.
+ /// The start index of the slice.
+ /// The length of the slice.
+ /// Slice is out of bounds.
+ /// The slice.
internal static Memory GetBoundedMemorySlice(this IMemoryGroup group, long start, int length)
where T : struct
{
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
index a4fcd9275b..3af4eb3c39 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
@@ -7,6 +7,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Memory;
@@ -187,11 +188,21 @@ internal abstract class BaseExifReader
protected void ReadSubIfd(List values)
{
- if (this.subIfds is not null)
+ if (this.subIfds != null)
{
- foreach (ulong subIfdOffset in this.subIfds)
+ const int maxSubIfds = 8;
+ const int maxNestingLevel = 8;
+ Span buf = stackalloc ulong[maxSubIfds];
+ for (int i = 0; i < maxNestingLevel && this.subIfds.Count > 0; i++)
{
- this.ReadValues(values, (uint)subIfdOffset);
+ int sz = Math.Min(this.subIfds.Count, maxSubIfds);
+ CollectionsMarshal.AsSpan(this.subIfds)[..sz].CopyTo(buf);
+
+ this.subIfds.Clear();
+ foreach (ulong subIfdOffset in buf[..sz])
+ {
+ this.ReadValues(values, (uint)subIfdOffset);
+ }
}
}
}
@@ -447,6 +458,7 @@ internal abstract class BaseExifReader
ExifTagValue.TileByteCounts => new ExifLong8Array(ExifTagValue.TileByteCounts),
_ => ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents),
};
+
if (exifValue is null)
{
this.AddInvalidTag(new UnkownExifTag(tag));
@@ -481,8 +493,9 @@ internal abstract class BaseExifReader
foreach (IExifValue val in values)
{
- // Sometimes duplicates appear, can compare val.Tag == exif.Tag
- if (val == exif)
+ // to skip duplicates must be used Equals method,
+ // == operator not defined for ExifValue and IExifValue
+ if (exif.Equals(val))
{
Debug.WriteLine($"Duplicate Exif tag: tag={exif.Tag}, dataType={exif.DataType}");
return;
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index 1d2dca8700..cf4a421b44 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -241,7 +241,7 @@ internal sealed class ExifWriter
return true;
}
- private static uint GetLength(IList values)
+ private static uint GetLength(List values)
{
if (values.Count == 0)
{
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs
index e88dd8d9e1..f3ce6cd79c 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs
@@ -4,24 +4,21 @@
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
///
-/// Provides methods to read ICC data types
+/// Provides methods to read ICC data types.
///
internal sealed partial class IccDataReader
{
///
- /// Reads an 8bit lookup table
+ /// Reads an 8bit lookup table.
///
- /// The read LUT
- public IccLut ReadLut8()
- {
- return new IccLut(this.ReadBytes(256));
- }
+ /// The read LUT.
+ public IccLut ReadLut8() => new(this.ReadBytes(256));
///
- /// Reads a 16bit lookup table
+ /// Reads a 16bit lookup table.
///
- /// The number of entries
- /// The read LUT
+ /// The number of entries.
+ /// The read LUT.
public IccLut ReadLut16(int count)
{
var values = new ushort[count];
@@ -34,16 +31,16 @@ internal sealed partial class IccDataReader
}
///
- /// Reads a CLUT depending on type
+ /// Reads a CLUT depending on type.
///
- /// Input channel count
- /// Output channel count
+ /// Input channel count.
+ /// Output channel count.
/// If true, it's read as CLUTf32,
- /// else read as either CLUT8 or CLUT16 depending on embedded information
- /// The read CLUT
+ /// else read as either CLUT8 or CLUT16 depending on embedded information.
+ /// The read CLUT.
public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat)
{
- // Grid-points are always 16 bytes long but only 0-inChCount are used
+ // Grid-points are always 16 bytes long but only 0-inChCount are used.
var gridPointCount = new byte[inChannelCount];
Buffer.BlockCopy(this.data, this.AddIndex(16), gridPointCount, 0, inChannelCount);
@@ -67,15 +64,14 @@ internal sealed partial class IccDataReader
}
///
- /// Reads an 8 bit CLUT
+ /// Reads an 8 bit CLUT.
///
- /// Input channel count
- /// Output channel count
- /// Grid point count for each CLUT channel
- /// The read CLUT8
+ /// Input channel count.
+ /// Output channel count.
+ /// Grid point count for each CLUT channel.
+ /// The read CLUT8.
public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount)
{
- int start = this.currentIndex;
int length = 0;
for (int i = 0; i < inChannelCount; i++)
{
@@ -86,27 +82,26 @@ internal sealed partial class IccDataReader
const float Max = byte.MaxValue;
- var values = new float[length][];
+ float[] values = new float[length * outChannelCount];
+ int offset = 0;
for (int i = 0; i < length; i++)
{
- values[i] = new float[outChannelCount];
for (int j = 0; j < outChannelCount; j++)
{
- values[i][j] = this.data[this.currentIndex++] / Max;
+ values[offset++] = this.data[this.currentIndex++] / Max;
}
}
- this.currentIndex = start + (length * outChannelCount);
- return new IccClut(values, gridPointCount, IccClutDataType.UInt8);
+ return new IccClut(values, gridPointCount, IccClutDataType.UInt8, outChannelCount);
}
///
- /// Reads a 16 bit CLUT
+ /// Reads a 16 bit CLUT.
///
- /// Input channel count
- /// Output channel count
- /// Grid point count for each CLUT channel
- /// The read CLUT16
+ /// Input channel count.
+ /// Output channel count.
+ /// Grid point count for each CLUT channel.
+ /// The read CLUT16.
public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount)
{
int start = this.currentIndex;
@@ -120,27 +115,27 @@ internal sealed partial class IccDataReader
const float Max = ushort.MaxValue;
- var values = new float[length][];
+ float[] values = new float[length * outChannelCount];
+ int offset = 0;
for (int i = 0; i < length; i++)
{
- values[i] = new float[outChannelCount];
for (int j = 0; j < outChannelCount; j++)
{
- values[i][j] = this.ReadUInt16() / Max;
+ values[offset++] = this.ReadUInt16() / Max;
}
}
this.currentIndex = start + (length * outChannelCount * 2);
- return new IccClut(values, gridPointCount, IccClutDataType.UInt16);
+ return new IccClut(values, gridPointCount, IccClutDataType.UInt16, outChannelCount);
}
///
- /// Reads a 32bit floating point CLUT
+ /// Reads a 32bit floating point CLUT.
///
- /// Input channel count
- /// Output channel count
- /// Grid point count for each CLUT channel
- /// The read CLUTf32
+ /// Input channel count.
+ /// Output channel count.
+ /// Grid point count for each CLUT channel.
+ /// The read CLUTf32.
public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount)
{
int start = this.currentIndex;
@@ -152,17 +147,17 @@ internal sealed partial class IccDataReader
length /= inChCount;
- var values = new float[length][];
+ float[] values = new float[length * outChCount];
+ int offset = 0;
for (int i = 0; i < length; i++)
{
- values[i] = new float[outChCount];
for (int j = 0; j < outChCount; j++)
{
- values[i][j] = this.ReadSingle();
+ values[offset++] = this.ReadSingle();
}
}
this.currentIndex = start + (length * outChCount * 4);
- return new IccClut(values, gridPointCount, IccClutDataType.Float);
+ return new IccClut(values, gridPointCount, IccClutDataType.Float, outChCount);
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs
index 61ecda4aab..ecc9bfbffb 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs
@@ -17,16 +17,23 @@ internal sealed partial class IccDataReader
/// The read matrix
public float[,] ReadMatrix(int xCount, int yCount, bool isSingle)
{
- var matrix = new float[xCount, yCount];
- for (int y = 0; y < yCount; y++)
+ float[,] matrix = new float[xCount, yCount];
+
+ if (isSingle)
{
- for (int x = 0; x < xCount; x++)
+ for (int y = 0; y < yCount; y++)
{
- if (isSingle)
+ for (int x = 0; x < xCount; x++)
{
matrix[x, y] = this.ReadSingle();
}
- else
+ }
+ }
+ else
+ {
+ for (int y = 0; y < yCount; y++)
+ {
+ for (int x = 0; x < xCount; x++)
{
matrix[x, y] = this.ReadFix16();
}
@@ -44,14 +51,17 @@ internal sealed partial class IccDataReader
/// The read matrix
public float[] ReadMatrix(int yCount, bool isSingle)
{
- var matrix = new float[yCount];
- for (int i = 0; i < yCount; i++)
+ float[] matrix = new float[yCount];
+ if (isSingle)
{
- if (isSingle)
+ for (int i = 0; i < yCount; i++)
{
matrix[i] = this.ReadSingle();
}
- else
+ }
+ else
+ {
+ for (int i = 0; i < yCount; i++)
{
matrix[i] = this.ReadFix16();
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
index 47d946d443..7a526ef1af 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
@@ -16,55 +16,37 @@ internal sealed partial class IccDataReader
/// Reads an ushort
///
/// the value
- public ushort ReadUInt16()
- {
- return BinaryPrimitives.ReadUInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
- }
+ public ushort ReadUInt16() => BinaryPrimitives.ReadUInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
///
/// Reads a short
///
/// the value
- public short ReadInt16()
- {
- return BinaryPrimitives.ReadInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
- }
+ public short ReadInt16() => BinaryPrimitives.ReadInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
///
/// Reads an uint
///
/// the value
- public uint ReadUInt32()
- {
- return BinaryPrimitives.ReadUInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
- }
+ public uint ReadUInt32() => BinaryPrimitives.ReadUInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
///
/// Reads an int
///
/// the value
- public int ReadInt32()
- {
- return BinaryPrimitives.ReadInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
- }
+ public int ReadInt32() => BinaryPrimitives.ReadInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
///
/// Reads an ulong
///
/// the value
- public ulong ReadUInt64()
- {
- return BinaryPrimitives.ReadUInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
- }
+ public ulong ReadUInt64() => BinaryPrimitives.ReadUInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
///
/// Reads a long
///
/// the value
- public long ReadInt64()
- {
- return BinaryPrimitives.ReadInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
- }
+ public long ReadInt64() => BinaryPrimitives.ReadInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
///
/// Reads a float.
@@ -152,10 +134,7 @@ internal sealed partial class IccDataReader
/// Reads an unsigned 16bit number with 8 value bits and 8 fractional bits.
///
/// The number as double
- public float ReadUFix8()
- {
- return this.ReadUInt16() / 256f;
- }
+ public float ReadUFix8() => this.ReadUInt16() / 256f;
///
/// Reads a number of bytes and advances the index.
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
index 0d50b98095..c1b22e82bf 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
@@ -8,21 +8,19 @@ using System.Numerics;
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
///
-/// Provides methods to read ICC data types
+/// Provides methods to read ICC data types.
///
internal sealed partial class IccDataReader
{
///
- /// Reads a tag data entry
+ /// Reads a tag data entry.
///
- /// The table entry with reading information
- /// the tag data entry
+ /// The table entry with reading information.
+ /// The tag data entry.
public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info)
{
this.currentIndex = (int)info.Offset;
- IccTypeSignature type = this.ReadTagDataEntryHeader();
-
- switch (type)
+ switch (this.ReadTagDataEntryHeader())
{
case IccTypeSignature.Chromaticity:
return this.ReadChromaticityTagDataEntry();
@@ -103,10 +101,10 @@ internal sealed partial class IccDataReader
///
/// Reads the header of a
///
- /// The read signature
+ /// The read signature.
public IccTypeSignature ReadTagDataEntryHeader()
{
- var type = (IccTypeSignature)this.ReadUInt32();
+ IccTypeSignature type = (IccTypeSignature)this.ReadUInt32();
this.AddIndex(4); // 4 bytes are not used
return type;
}
@@ -114,7 +112,7 @@ internal sealed partial class IccDataReader
///
/// Reads the header of a and checks if it's the expected value
///
- /// expected value to check against
+ /// The expected value to check against.
public void ReadCheckTagDataEntryHeader(IccTypeSignature expected)
{
IccTypeSignature type = this.ReadTagDataEntryHeader();
@@ -127,8 +125,8 @@ internal sealed partial class IccDataReader
///
/// Reads a with an unknown
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size)
{
int count = (int)size - 8; // 8 is the tag header size
@@ -138,13 +136,13 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry()
{
ushort channelCount = this.ReadUInt16();
var colorant = (IccColorantEncoding)this.ReadUInt16();
- if (Enum.IsDefined(typeof(IccColorantEncoding), colorant) && colorant != IccColorantEncoding.Unknown)
+ if (Enum.IsDefined(colorant) && colorant != IccColorantEncoding.Unknown)
{
// The type is known and so are the values (they are constant)
// channelCount should always be 3 but it doesn't really matter if it's not
@@ -152,7 +150,7 @@ internal sealed partial class IccDataReader
}
else
{
- // The type is not know, so the values need be read
+ // The type is not know, so the values need be read.
double[][] values = new double[channelCount][];
for (int i = 0; i < channelCount; i++)
{
@@ -166,7 +164,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry()
{
uint colorantCount = this.ReadUInt32();
@@ -177,7 +175,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry()
{
uint colorantCount = this.ReadUInt32();
@@ -193,7 +191,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccCurveTagDataEntry ReadCurveTagDataEntry()
{
uint pointCount = this.ReadUInt32();
@@ -222,7 +220,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
+ /// The size of the entry in bytes.
/// The read entry
public IccDataTagDataEntry ReadDataTagDataEntry(uint size)
{
@@ -240,16 +238,13 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
- public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry()
- {
- return new IccDateTimeTagDataEntry(this.ReadDateTime());
- }
+ /// The read entry.
+ public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() => new IccDateTimeTagDataEntry(this.ReadDateTime());
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccLut16TagDataEntry ReadLut16TagDataEntry()
{
byte inChCount = this.data[this.AddIndex(1)];
@@ -287,7 +282,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccLut8TagDataEntry ReadLut8TagDataEntry()
{
byte inChCount = this.data[this.AddIndex(1)];
@@ -322,7 +317,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry()
{
int start = this.currentIndex - 8; // 8 is the tag header size
@@ -381,7 +376,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccLutBToATagDataEntry ReadLutBtoATagDataEntry()
{
int start = this.currentIndex - 8; // 8 is the tag header size
@@ -440,21 +435,18 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
- public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry()
- {
- return new IccMeasurementTagDataEntry(
+ /// The read entry.
+ public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() => new(
observer: (IccStandardObserver)this.ReadUInt32(),
xyzBacking: this.ReadXyzNumber(),
geometry: (IccMeasurementGeometry)this.ReadUInt32(),
flare: this.ReadUFix16(),
illuminant: (IccStandardIlluminant)this.ReadUInt32());
- }
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry()
{
int start = this.currentIndex - 8; // 8 is the tag header size
@@ -519,7 +511,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry()
{
int start = this.currentIndex - 8;
@@ -547,7 +539,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry()
{
int vendorFlag = this.ReadInt32();
@@ -569,15 +561,12 @@ internal sealed partial class IccDataReader
/// Reads a
///
/// The read entry
- public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry()
- {
- return new IccParametricCurveTagDataEntry(this.ReadParametricCurve());
- }
+ public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() => new(this.ReadParametricCurve());
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry()
{
uint count = this.ReadUInt32();
@@ -593,7 +582,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry()
{
int start = this.currentIndex - 8; // 8 is the tag header size
@@ -620,7 +609,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry()
{
int start = this.currentIndex - 8; // 8 is the tag header size
@@ -646,8 +635,8 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size)
{
uint count = (size - 8) / 4;
@@ -663,27 +652,21 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
- public IccSignatureTagDataEntry ReadSignatureTagDataEntry()
- {
- return new IccSignatureTagDataEntry(this.ReadAsciiString(4));
- }
+ /// The read entry.
+ public IccSignatureTagDataEntry ReadSignatureTagDataEntry() => new(this.ReadAsciiString(4));
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
- public IccTextTagDataEntry ReadTextTagDataEntry(uint size)
- {
- return new IccTextTagDataEntry(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size
- }
+ /// The size of the entry in bytes.
+ /// The read entry.
+ public IccTextTagDataEntry ReadTextTagDataEntry(uint size) => new(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size)
{
uint count = (size - 8) / 4;
@@ -699,8 +682,8 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size)
{
uint count = (size - 8) / 2;
@@ -716,8 +699,8 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size)
{
uint count = (size - 8) / 4;
@@ -733,8 +716,8 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size)
{
uint count = (size - 8) / 8;
@@ -750,8 +733,8 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size)
{
int count = (int)size - 8; // 8 is the tag header size
@@ -763,20 +746,17 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
- public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry()
- {
- return new IccViewingConditionsTagDataEntry(
+ /// The read entry.
+ public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() => new(
illuminantXyz: this.ReadXyzNumber(),
surroundXyz: this.ReadXyzNumber(),
illuminant: (IccStandardIlluminant)this.ReadUInt32());
- }
///
/// Reads a
///
- /// The size of the entry in bytes
- /// The read entry
+ /// The size of the entry in bytes.
+ /// The read entry.
public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size)
{
uint count = (size - 8) / 12;
@@ -792,7 +772,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry()
{
string unicodeValue, scriptcodeValue;
@@ -832,7 +812,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry()
{
uint productNameCount = this.ReadUInt32();
@@ -856,7 +836,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The read entry
+ /// The read entry.
public IccScreeningTagDataEntry ReadScreeningTagDataEntry()
{
var flags = (IccScreeningFlag)this.ReadInt32();
@@ -873,7 +853,7 @@ internal sealed partial class IccDataReader
///
/// Reads a
///
- /// The size of the entry in bytes
+ /// The size of the entry in bytes.
/// The read entry
public IccUcrBgTagDataEntry ReadUcrBgTagDataEntry(uint size)
{
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs
index 703a3896bb..29394c0820 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs
@@ -4,15 +4,15 @@
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
///
-/// Provides methods to write ICC data types
+/// Provides methods to write ICC data types.
///
internal sealed partial class IccDataWriter
{
///
- /// Writes an 8bit lookup table
+ /// Writes an 8bit lookup table.
///
- /// The LUT to write
- /// The number of bytes written
+ /// The LUT to write.
+ /// The number of bytes written.
public int WriteLut8(IccLut value)
{
foreach (float item in value.Values)
@@ -24,10 +24,10 @@ internal sealed partial class IccDataWriter
}
///
- /// Writes an 16bit lookup table
+ /// Writes an 16bit lookup table.
///
- /// The LUT to write
- /// The number of bytes written
+ /// The LUT to write.
+ /// The number of bytes written.
public int WriteLut16(IccLut value)
{
foreach (float item in value.Values)
@@ -39,10 +39,10 @@ internal sealed partial class IccDataWriter
}
///
- /// Writes an color lookup table
+ /// Writes an color lookup table.
///
- /// The CLUT to write
- /// The number of bytes written
+ /// The CLUT to write.
+ /// The number of bytes written.
public int WriteClut(IccClut value)
{
int count = this.WriteArray(value.GridPointCount);
@@ -67,57 +67,48 @@ internal sealed partial class IccDataWriter
}
///
- /// Writes a 8bit color lookup table
+ /// Writes a 8bit color lookup table.
///
- /// The CLUT to write
- /// The number of bytes written
+ /// The CLUT to write.
+ /// The number of bytes written.
public int WriteClut8(IccClut value)
{
int count = 0;
- foreach (float[] inArray in value.Values)
+ foreach (float item in value.Values)
{
- foreach (float item in inArray)
- {
- count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue));
- }
+ count += this.WriteByte((byte)Numerics.Clamp((item * byte.MaxValue) + 0.5F, 0, byte.MaxValue));
}
return count;
}
///
- /// Writes a 16bit color lookup table
+ /// Writes a 16bit color lookup table.
///
- /// The CLUT to write
- /// The number of bytes written
+ /// The CLUT to write.
+ /// The number of bytes written.
public int WriteClut16(IccClut value)
{
int count = 0;
- foreach (float[] inArray in value.Values)
+ foreach (float item in value.Values)
{
- foreach (float item in inArray)
- {
- count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue));
- }
+ count += this.WriteUInt16((ushort)Numerics.Clamp((item * ushort.MaxValue) + 0.5F, 0, ushort.MaxValue));
}
return count;
}
///
- /// Writes a 32bit float color lookup table
+ /// Writes a 32bit float color lookup table.
///
- /// The CLUT to write
- /// The number of bytes written
+ /// The CLUT to write.
+ /// The number of bytes written.
public int WriteClutF32(IccClut value)
{
int count = 0;
- foreach (float[] inArray in value.Values)
+ foreach (float item in value.Values)
{
- foreach (float item in inArray)
- {
- count += this.WriteSingle(item);
- }
+ count += this.WriteSingle(item);
}
return count;
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
index 1e5f359e09..636cc90a57 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
@@ -61,15 +61,21 @@ internal sealed partial class IccDataWriter
public int WriteMatrix(in DenseMatrix value, bool isSingle)
{
int count = 0;
- for (int y = 0; y < value.Rows; y++)
+ if (isSingle)
{
- for (int x = 0; x < value.Columns; x++)
+ for (int y = 0; y < value.Rows; y++)
{
- if (isSingle)
+ for (int x = 0; x < value.Columns; x++)
{
count += this.WriteSingle(value[x, y]);
}
- else
+ }
+ }
+ else
+ {
+ for (int y = 0; y < value.Rows; y++)
+ {
+ for (int x = 0; x < value.Columns; x++)
{
count += this.WriteFix16(value[x, y]);
}
@@ -88,15 +94,22 @@ internal sealed partial class IccDataWriter
public int WriteMatrix(float[,] value, bool isSingle)
{
int count = 0;
- for (int y = 0; y < value.GetLength(1); y++)
+
+ if (isSingle)
{
- for (int x = 0; x < value.GetLength(0); x++)
+ for (int y = 0; y < value.GetLength(1); y++)
{
- if (isSingle)
+ for (int x = 0; x < value.GetLength(0); x++)
{
count += this.WriteSingle(value[x, y]);
}
- else
+ }
+ }
+ else
+ {
+ for (int y = 0; y < value.GetLength(1); y++)
+ {
+ for (int x = 0; x < value.GetLength(0); x++)
{
count += this.WriteFix16(value[x, y]);
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs
index 1f9c101fb5..6019a0bff7 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs
@@ -231,7 +231,7 @@ internal sealed partial class IccDataWriter
{
int count = this.WriteByte((byte)value.InputChannelCount);
count += this.WriteByte((byte)value.OutputChannelCount);
- count += this.WriteByte((byte)value.ClutValues.Values[0].Length);
+ count += this.WriteByte((byte)value.ClutValues.OutputChannelCount);
count += this.WriteEmpty(1);
count += this.WriteMatrix(value.Matrix, false);
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs
index e0c6f4c962..27af2a91f7 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs
@@ -14,7 +14,7 @@ internal enum IccFormulaCurveType : ushort
Type1 = 0,
///
- /// Type 1: Y = a * log10 (b * X^γ + c) + d
+ /// Type 2: Y = a * log10 (b * X^γ + c) + d
///
Type2 = 1,
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs
index c1cb3f10f0..c6b4b65773 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
index be7350bc44..392ccb3062 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
@@ -155,9 +155,9 @@ public sealed class IccProfile : IDeepCloneable
}
return arrayValid &&
- Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
- Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) &&
- Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) &&
+ Enum.IsDefined(this.Header.DataColorSpace) &&
+ Enum.IsDefined(this.Header.ProfileConnectionSpace) &&
+ Enum.IsDefined(this.Header.RenderingIntent) &&
this.Header.Size is >= minSize and < maxSize;
}
@@ -190,7 +190,6 @@ public sealed class IccProfile : IDeepCloneable
return;
}
- IccReader reader = new();
this.header = IccReader.ReadHeader(this.data);
}
@@ -203,11 +202,10 @@ public sealed class IccProfile : IDeepCloneable
if (this.data is null)
{
- this.entries = Array.Empty();
+ this.entries = [];
return;
}
- IccReader reader = new();
this.entries = IccReader.ReadTagData(this.data);
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs
index 4da5a6aa56..b50885d025 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
@@ -11,6 +11,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
///
public sealed class IccProfileHeader
{
+ private static readonly Vector3 TruncatedD50 = new(0.9642029F, 1F, 0.8249054F);
+
+ // sRGB v2 Preference
+ private static readonly IccProfileId StandardRgbV2 = new(0x3D0EB2DE, 0xAE9397BE, 0x9B6726CE, 0x8C0A43CE);
+
+ // sRGB v4 Preference
+ private static readonly IccProfileId StandardRgbV4 = new(0x34562ABF, 0x994CCD06, 0x6D2C5721, 0xD0D68C5D);
+
+ // sRGB v4 Appearance
+ private static readonly IccProfileId StandardRgbV4A = new(0xDF1132A1, 0x746E97B0, 0xAD85719, 0xBE711E08);
+
///
/// Gets or sets the profile size in bytes (will be ignored when writing a profile).
///
@@ -97,4 +108,31 @@ public sealed class IccProfileHeader
/// Gets or sets the profile ID (hash).
///
public IccProfileId Id { get; set; }
+
+ internal static bool IsLikelySrgb(IccProfileHeader header)
+ {
+ // Reject known perceptual-appearance profile
+ // This profile employs perceptual rendering intents to maintain color appearance across different
+ // devices and media, which can lead to variations from standard sRGB representations.
+ if (header.Id == StandardRgbV4A)
+ {
+ return false;
+ }
+
+ // Accept known sRGB profile IDs
+ if (header.Id == StandardRgbV2 || header.Id == StandardRgbV4)
+ {
+ return true;
+ }
+
+ // Fallback: best-guess heuristic
+ return
+ header.FileSignature == "acsp" &&
+ header.DataColorSpace == IccColorSpaceType.Rgb &&
+ (header.ProfileConnectionSpace == IccColorSpaceType.CieXyz || header.ProfileConnectionSpace == IccColorSpaceType.CieLab) &&
+ (header.Class == IccProfileClass.DisplayDevice || header.Class == IccProfileClass.ColorSpace) &&
+ header.PcsIlluminant == TruncatedD50 &&
+ (header.Version.Major == 2 || header.Version.Major == 4) &&
+ !string.Equals(header.CmmType, "ADBE", StringComparison.Ordinal);
+ }
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs
index 45074c9a6e..074712d302 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs
@@ -83,28 +83,19 @@ internal sealed class IccReader
{
IccTagTableEntry[] tagTable = ReadTagTable(reader);
List entries = new(tagTable.Length);
- Dictionary store = new();
foreach (IccTagTableEntry tag in tagTable)
{
IccTagDataEntry entry;
- if (store.TryGetValue(tag.Offset, out IccTagDataEntry? value))
+
+ try
{
- entry = value;
+ entry = reader.ReadTagDataEntry(tag);
}
- else
+ catch
{
- try
- {
- entry = reader.ReadTagDataEntry(tag);
- }
- catch
- {
- // Ignore tags that could not be read
- continue;
- }
-
- store.Add(tag.Offset, entry);
+ // Ignore tags that could not be read
+ continue;
}
entry.TagSignature = tag.Signature;
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
index 12228f3f58..f7a99645bb 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -41,9 +41,7 @@ public abstract class IccTagDataEntry : IEquatable
///
public override bool Equals(object? obj)
- {
- return obj is IccTagDataEntry entry && this.Equals(entry);
- }
+ => obj is IccTagDataEntry entry && this.Equals(entry);
///
public virtual bool Equals(IccTagDataEntry? other)
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs
index 26a882810e..bbec7ce43e 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs
@@ -1,20 +1,21 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
///
-/// Color Lookup Table
+/// Color Lookup Table.
///
internal sealed class IccClut : IEquatable
{
///
/// Initializes a new instance of the class.
///
- /// The CLUT values
- /// The gridpoint count
- /// The data type of this CLUT
- public IccClut(float[][] values, byte[] gridPointCount, IccClutDataType type)
+ /// The CLUT values.
+ /// The gridpoint count.
+ /// The data type of this CLUT.
+ /// The output channels count.
+ public IccClut(float[] values, byte[] gridPointCount, IccClutDataType type, int outputChannelCount)
{
Guard.NotNull(values, nameof(values));
Guard.NotNull(gridPointCount, nameof(gridPointCount));
@@ -22,91 +23,33 @@ internal sealed class IccClut : IEquatable
this.Values = values;
this.DataType = type;
this.InputChannelCount = gridPointCount.Length;
- this.OutputChannelCount = values[0].Length;
+ this.OutputChannelCount = outputChannelCount;
this.GridPointCount = gridPointCount;
this.CheckValues();
}
///
- /// Initializes a new instance of the class.
- ///
- /// The CLUT values
- /// The gridpoint count
- public IccClut(ushort[][] values, byte[] gridPointCount)
- {
- Guard.NotNull(values, nameof(values));
- Guard.NotNull(gridPointCount, nameof(gridPointCount));
-
- const float Max = ushort.MaxValue;
-
- this.Values = new float[values.Length][];
- for (int i = 0; i < values.Length; i++)
- {
- this.Values[i] = new float[values[i].Length];
- for (int j = 0; j < values[i].Length; j++)
- {
- this.Values[i][j] = values[i][j] / Max;
- }
- }
-
- this.DataType = IccClutDataType.UInt16;
- this.InputChannelCount = gridPointCount.Length;
- this.OutputChannelCount = values[0].Length;
- this.GridPointCount = gridPointCount;
- this.CheckValues();
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The CLUT values
- /// The gridpoint count
- public IccClut(byte[][] values, byte[] gridPointCount)
- {
- Guard.NotNull(values, nameof(values));
- Guard.NotNull(gridPointCount, nameof(gridPointCount));
-
- const float Max = byte.MaxValue;
-
- this.Values = new float[values.Length][];
- for (int i = 0; i < values.Length; i++)
- {
- this.Values[i] = new float[values[i].Length];
- for (int j = 0; j < values[i].Length; j++)
- {
- this.Values[i][j] = values[i][j] / Max;
- }
- }
-
- this.DataType = IccClutDataType.UInt8;
- this.InputChannelCount = gridPointCount.Length;
- this.OutputChannelCount = values[0].Length;
- this.GridPointCount = gridPointCount;
- this.CheckValues();
- }
-
- ///
- /// Gets the values that make up this table
+ /// Gets the values that make up this table.
///
- public float[][] Values { get; }
+ public float[] Values { get; }
///
- /// Gets the CLUT data type (important when writing a profile)
+ /// Gets the CLUT data type (important when writing a profile).
///
public IccClutDataType DataType { get; }
///
- /// Gets the number of input channels
+ /// Gets the number of input channels.
///
public int InputChannelCount { get; }
///
- /// Gets the number of output channels
+ /// Gets the number of output channels.
///
public int OutputChannelCount { get; }
///
- /// Gets the number of grid points per input channel
+ /// Gets the number of grid points per input channel.
///
public byte[] GridPointCount { get; }
@@ -134,15 +77,12 @@ internal sealed class IccClut : IEquatable
public override bool Equals(object? obj) => obj is IccClut other && this.Equals(other);
///
- public override int GetHashCode()
- {
- return HashCode.Combine(
+ public override int GetHashCode() => HashCode.Combine(
this.Values,
this.DataType,
this.InputChannelCount,
this.OutputChannelCount,
this.GridPointCount);
- }
private bool EqualsValuesArray(IccClut other)
{
@@ -153,7 +93,7 @@ internal sealed class IccClut : IEquatable
for (int i = 0; i < this.Values.Length; i++)
{
- if (!this.Values[i].AsSpan().SequenceEqual(other.Values[i]))
+ if (!this.Values.SequenceEqual(other.Values))
{
return false;
}
@@ -167,17 +107,13 @@ internal sealed class IccClut : IEquatable
Guard.MustBeBetweenOrEqualTo(this.InputChannelCount, 1, 15, nameof(this.InputChannelCount));
Guard.MustBeBetweenOrEqualTo(this.OutputChannelCount, 1, 15, nameof(this.OutputChannelCount));
- bool isLengthDifferent = this.Values.Any(t => t.Length != this.OutputChannelCount);
- Guard.IsFalse(isLengthDifferent, nameof(this.Values), "The number of output values varies");
-
int length = 0;
for (int i = 0; i < this.InputChannelCount; i++)
{
length += (int)Math.Pow(this.GridPointCount[i], this.InputChannelCount);
}
- length /= this.InputChannelCount;
-
- Guard.IsTrue(this.Values.Length == length, nameof(this.Values), "Length of values array does not match the grid points");
+ // TODO: Disabled this check, not sure if this check is correct.
+ // Guard.IsTrue(this.Values.Length == length, nameof(this.Values), "Length of values array does not match the grid points");
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs
index e7d7461d5d..a71cbfaf5a 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -49,9 +49,7 @@ internal readonly struct IccTagTableEntry : IEquatable
/// True if the parameter is equal to the parameter; otherwise, false.
///
public static bool operator ==(IccTagTableEntry left, IccTagTableEntry right)
- {
- return left.Equals(right);
- }
+ => left.Equals(right);
///
/// Compares two objects for equality.
@@ -62,9 +60,7 @@ internal readonly struct IccTagTableEntry : IEquatable
/// True if the parameter is not equal to the parameter; otherwise, false.
///
public static bool operator !=(IccTagTableEntry left, IccTagTableEntry right)
- {
- return !left.Equals(right);
- }
+ => !left.Equals(right);
///
public override bool Equals(object? obj) => obj is IccTagTableEntry other && this.Equals(other);
diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs
index adf386614d..528b3e76d4 100644
--- a/src/ImageSharp/PixelFormats/IPixel.cs
+++ b/src/ImageSharp/PixelFormats/IPixel.cs
@@ -23,7 +23,8 @@ public interface IPixel : IPixel, IEquatable
static abstract PixelOperations CreatePixelOperations();
///
- /// Initializes the pixel instance from a generic scaled .
+ /// Initializes the pixel instance from a generic a generic ("scaled") representation
+ /// with values scaled and clamped between 0 and 1
///
/// The vector to load the pixel from.
/// The .
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
index 0aa7bad237..b03a54c585 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
@@ -61,7 +61,8 @@ public partial struct Rgb24 : IPixel
/// The instance of to convert.
/// An instance of .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static implicit operator Rgb24(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f));
+ public static implicit operator Rgb24(Rgb color)
+ => FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F));
///
/// Compares two objects for equality.
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs
index 0491553430..507d6d70b6 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs
@@ -187,7 +187,7 @@ public partial struct Rgba32 : IPixel, IPackedVector
/// The instance of to convert.
/// An instance of .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F));
+ public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F));
///
/// Compares two objects for equality.
diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index 4ac9546f39..6d1e8aaa55 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing;
///
public class AffineTransformBuilder
{
- private readonly List> transformMatrixFactories = new();
+ private readonly List> transformMatrixFactories = [];
///
/// Initializes a new instance of the class.
@@ -301,7 +301,8 @@ public class AffineTransformBuilder
///
/// The source image size.
/// The .
- public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
+ public Matrix3x2 BuildMatrix(Size sourceSize)
+ => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
///
/// Returns the combined transform matrix for a given source rectangle.
@@ -345,18 +346,8 @@ public class AffineTransformBuilder
/// The .
public Size GetTransformedSize(Rectangle sourceRectangle)
{
- Size size = sourceRectangle.Size;
-
- // Translate the origin matrix to cater for source rectangle offsets.
- Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location);
-
- foreach (Func factory in this.transformMatrixFactories)
- {
- matrix *= factory(size);
- CheckDegenerate(matrix);
- }
-
- return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
+ Matrix3x2 matrix = this.BuildMatrix(sourceRectangle);
+ return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix3x2 matrix)
diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
index 4d95e060dc..63c4895080 100644
--- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
+++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
@@ -61,9 +61,7 @@ internal class DefaultImageProcessorContext : IInternalImageProcessingCo
///
public IImageProcessingContext ApplyProcessor(IImageProcessor processor)
- {
- return this.ApplyProcessor(processor, this.GetCurrentBounds());
- }
+ => this.ApplyProcessor(processor, this.GetCurrentBounds());
///
public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
@@ -74,11 +72,9 @@ internal class DefaultImageProcessorContext : IInternalImageProcessingCo
// interim clone if the first processor in the pipeline is a cloning processor.
if (processor is ICloningImageProcessor cloningImageProcessor)
{
- using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.Configuration, this.source, rectangle))
- {
- this.destination = pixelProcessor.CloneAndExecute();
- return this;
- }
+ using ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.Configuration, this.source, rectangle);
+ this.destination = pixelProcessor.CloneAndExecute();
+ return this;
}
// Not a cloning processor? We need to create a clone to operate on.
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs
index e3e6f13ed6..71252e0bb0 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs
@@ -44,13 +44,13 @@ public static class BokehBlurExtensions
/// Applies a bokeh blur to the image.
///
/// The current image processing context.
- /// The 'radius' value representing the size of the area to sample.
- /// The 'components' value representing the number of kernels to use to approximate the bokeh effect.
- /// The gamma highlight factor to use to emphasize bright spots in the source image
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'radius' value representing the size of the area to sample.
+ /// The 'components' value representing the number of kernels to use to approximate the bokeh effect.
+ /// The gamma highlight factor to use to emphasize bright spots in the source image
/// The .
- public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle)
+ public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, int radius, int components, float gamma)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
index 6611af742b..73e40b57aa 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
@@ -44,10 +44,10 @@ public static class BoxBlurExtensions
/// Applies a box blur to the image.
///
/// The current image processing context.
- /// The 'radius' value representing the size of the area to sample.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'radius' value representing the size of the area to sample.
///
/// The to use when mapping the pixels outside of the border, in X direction.
///
@@ -55,9 +55,11 @@ public static class BoxBlurExtensions
/// The to use when mapping the pixels outside of the border, in Y direction.
///
/// The .
- public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
- {
- var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY);
- return source.ApplyProcessor(processor, rectangle);
- }
+ public static IImageProcessingContext BoxBlur(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ int radius,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs
new file mode 100644
index 0000000000..2980ff44f0
--- /dev/null
+++ b/src/ImageSharp/Processing/Extensions/Convolution/ConvolutionExtensions.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Processing.Processors.Convolution;
+
+namespace SixLabors.ImageSharp.Processing.Extensions.Convolution;
+
+///
+/// Defines general convolution extensions to apply on an
+/// using Mutate/Clone.
+///
+public static class ConvolutionExtensions
+{
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY)
+ => Convolve(source, kernelXY, false);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, DenseMatrix kernelXY, bool preserveAlpha)
+ => Convolve(source, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ /// The .
+ public static IImageProcessingContext Convolve(
+ this IImageProcessingContext source,
+ DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY));
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY)
+ => Convolve(source, rectangle, kernelXY, false);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The .
+ public static IImageProcessingContext Convolve(this IImageProcessingContext source, Rectangle rectangle, DenseMatrix kernelXY, bool preserveAlpha)
+ => Convolve(source, rectangle, kernelXY, preserveAlpha, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
+
+ ///
+ /// Applies a convolution filter to the image.
+ ///
+ /// The current image processing context.
+ /// The rectangle structure that specifies the portion of the image object to alter.
+ /// The convolution kernel to apply.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ /// The .
+ public static IImageProcessingContext Convolve(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new ConvolutionProcessor(kernelXY, preserveAlpha, borderWrapModeX, borderWrapModeY), rectangle);
+}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs
index b044c3966f..c8fb230559 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs
@@ -16,8 +16,8 @@ public static class DetectEdgesExtensions
///
/// The current image processing context.
/// The .
- public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) =>
- DetectEdges(source, KnownEdgeDetectorKernels.Sobel);
+ public static IImageProcessingContext DetectEdges(this IImageProcessingContext source)
+ => DetectEdges(source, KnownEdgeDetectorKernels.Sobel);
///
/// Detects any edges within the image.
@@ -28,10 +28,8 @@ public static class DetectEdgesExtensions
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext DetectEdges(
- this IImageProcessingContext source,
- Rectangle rectangle) =>
- DetectEdges(source, KnownEdgeDetectorKernels.Sobel, rectangle);
+ public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle)
+ => DetectEdges(source, rectangle, KnownEdgeDetectorKernels.Sobel);
///
/// Detects any edges within the image operating in grayscale mode.
@@ -39,10 +37,8 @@ public static class DetectEdgesExtensions
/// The current image processing context.
/// The 2D edge detector kernel.
/// The .
- public static IImageProcessingContext DetectEdges(
- this IImageProcessingContext source,
- EdgeDetector2DKernel kernel) =>
- DetectEdges(source, kernel, true);
+ public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel)
+ => DetectEdges(source, kernel, true);
///
/// Detects any edges within the image using a .
@@ -57,49 +53,41 @@ public static class DetectEdgesExtensions
this IImageProcessingContext source,
EdgeDetector2DKernel kernel,
bool grayscale)
- {
- var processor = new EdgeDetector2DProcessor(kernel, grayscale);
- source.ApplyProcessor(processor);
- return source;
- }
+ => source.ApplyProcessor(new EdgeDetector2DProcessor(kernel, grayscale));
///
/// Detects any edges within the image operating in grayscale mode.
///
/// The current image processing context.
- /// The 2D edge detector kernel.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 2D edge detector kernel.
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
- EdgeDetector2DKernel kernel,
- Rectangle rectangle) =>
- DetectEdges(source, kernel, true, rectangle);
+ Rectangle rectangle,
+ EdgeDetector2DKernel kernel)
+ => DetectEdges(source, rectangle, kernel, true);
///
/// Detects any edges within the image using a .
///
/// The current image processing context.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
/// The 2D edge detector kernel.
///
/// Whether to convert the image to grayscale before performing edge detection.
///
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
+ Rectangle rectangle,
EdgeDetector2DKernel kernel,
- bool grayscale,
- Rectangle rectangle)
- {
- var processor = new EdgeDetector2DProcessor(kernel, grayscale);
- source.ApplyProcessor(processor, rectangle);
- return source;
- }
+ bool grayscale)
+ => source.ApplyProcessor(new EdgeDetector2DProcessor(kernel, grayscale), rectangle);
///
/// Detects any edges within the image operating in grayscale mode.
@@ -107,10 +95,8 @@ public static class DetectEdgesExtensions
/// The current image processing context.
/// The edge detector kernel.
/// The .
- public static IImageProcessingContext DetectEdges(
- this IImageProcessingContext source,
- EdgeDetectorKernel kernel) =>
- DetectEdges(source, kernel, true);
+ public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel)
+ => DetectEdges(source, kernel, true);
///
/// Detects any edges within the image using a .
@@ -125,66 +111,56 @@ public static class DetectEdgesExtensions
this IImageProcessingContext source,
EdgeDetectorKernel kernel,
bool grayscale)
- {
- var processor = new EdgeDetectorProcessor(kernel, grayscale);
- source.ApplyProcessor(processor);
- return source;
- }
+ => source.ApplyProcessor(new EdgeDetectorProcessor(kernel, grayscale));
///
/// Detects any edges within the image operating in grayscale mode.
///
/// The current image processing context.
- /// The edge detector kernel.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The edge detector kernel.
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
- EdgeDetectorKernel kernel,
- Rectangle rectangle) =>
- DetectEdges(source, kernel, true, rectangle);
+ Rectangle rectangle,
+ EdgeDetectorKernel kernel)
+ => DetectEdges(source, rectangle, kernel, true);
///
/// Detects any edges within the image using a .
///
/// The current image processing context.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
/// The edge detector kernel.
///
/// Whether to convert the image to grayscale before performing edge detection.
///
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
+ Rectangle rectangle,
EdgeDetectorKernel kernel,
- bool grayscale,
- Rectangle rectangle)
- {
- var processor = new EdgeDetectorProcessor(kernel, grayscale);
- source.ApplyProcessor(processor, rectangle);
- return source;
- }
+ bool grayscale)
+ => source.ApplyProcessor(new EdgeDetectorProcessor(kernel, grayscale), rectangle);
///
/// Detects any edges within the image operating in grayscale mode.
///
/// The current image processing context.
- /// Thecompass edge detector kernel.
+ /// The compass edge detector kernel.
/// The .
- public static IImageProcessingContext DetectEdges(
- this IImageProcessingContext source,
- EdgeDetectorCompassKernel kernel) =>
- DetectEdges(source, kernel, true);
+ public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel)
+ => DetectEdges(source, kernel, true);
///
/// Detects any edges within the image using a .
///
/// The current image processing context.
- /// Thecompass edge detector kernel.
+ /// The compass edge detector kernel.
///
/// Whether to convert the image to grayscale before performing edge detection.
///
@@ -193,47 +169,39 @@ public static class DetectEdgesExtensions
this IImageProcessingContext source,
EdgeDetectorCompassKernel kernel,
bool grayscale)
- {
- var processor = new EdgeDetectorCompassProcessor(kernel, grayscale);
- source.ApplyProcessor(processor);
- return source;
- }
+ => source.ApplyProcessor(new EdgeDetectorCompassProcessor(kernel, grayscale));
///
/// Detects any edges within the image operating in grayscale mode.
///
/// The current image processing context.
- /// Thecompass edge detector kernel.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The compass edge detector kernel.
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
- EdgeDetectorCompassKernel kernel,
- Rectangle rectangle) =>
- DetectEdges(source, kernel, true, rectangle);
+ Rectangle rectangle,
+ EdgeDetectorCompassKernel kernel)
+ => DetectEdges(source, rectangle, kernel, true);
///
/// Detects any edges within the image using a .
///
/// The current image processing context.
- /// Thecompass edge detector kernel.
- ///
- /// Whether to convert the image to grayscale before performing edge detection.
- ///
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The compass edge detector kernel.
+ ///
+ /// Whether to convert the image to grayscale before performing edge detection.
+ ///
/// The .
public static IImageProcessingContext DetectEdges(
this IImageProcessingContext source,
+ Rectangle rectangle,
EdgeDetectorCompassKernel kernel,
- bool grayscale,
- Rectangle rectangle)
- {
- var processor = new EdgeDetectorCompassProcessor(kernel, grayscale);
- source.ApplyProcessor(processor, rectangle);
- return source;
- }
+ bool grayscale)
+ => source.ApplyProcessor(new EdgeDetectorCompassProcessor(kernel, grayscale), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
index b851482008..d406bf8d10 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
@@ -32,22 +32,25 @@ public static class GaussianBlurExtensions
/// Applies a Gaussian blur to the image.
///
/// The current image processing context.
- /// The 'sigma' value representing the weight of the blur.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'sigma' value representing the weight of the blur.
/// The .
- public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle)
+ public static IImageProcessingContext GaussianBlur(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ float sigma)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle);
///
/// Applies a Gaussian blur to the image.
///
/// The current image processing context.
- /// The 'sigma' value representing the weight of the blur.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'sigma' value representing the weight of the blur.
///
/// The to use when mapping the pixels outside of the border, in X direction.
///
@@ -55,9 +58,11 @@ public static class GaussianBlurExtensions
/// The to use when mapping the pixels outside of the border, in Y direction.
///
/// The .
- public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
- {
- var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY);
- return source.ApplyProcessor(processor, rectangle);
- }
+ public static IImageProcessingContext GaussianBlur(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ float sigma,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
index 4a94df0963..9470cdbdc0 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
@@ -16,8 +16,8 @@ public static class GaussianSharpenExtensions
///
/// The current image processing context.
/// The .
- public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) =>
- source.ApplyProcessor(new GaussianSharpenProcessor());
+ public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source)
+ => source.ApplyProcessor(new GaussianSharpenProcessor());
///
/// Applies a Gaussian sharpening filter to the image.
@@ -25,32 +25,32 @@ public static class GaussianSharpenExtensions
/// The current image processing context.
/// The 'sigma' value representing the weight of the blur.
/// The .
- public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) =>
- source.ApplyProcessor(new GaussianSharpenProcessor(sigma));
+ public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma)
+ => source.ApplyProcessor(new GaussianSharpenProcessor(sigma));
///
/// Applies a Gaussian sharpening filter to the image.
///
/// The current image processing context.
- /// The 'sigma' value representing the weight of the blur.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'sigma' value representing the weight of the blur.
/// The .
public static IImageProcessingContext GaussianSharpen(
this IImageProcessingContext source,
- float sigma,
- Rectangle rectangle) =>
+ Rectangle rectangle,
+ float sigma) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle);
///
/// Applies a Gaussian sharpening filter to the image.
///
/// The current image processing context.
- /// The 'sigma' value representing the weight of the blur.
///
/// The structure that specifies the portion of the image object to alter.
///
+ /// The 'sigma' value representing the weight of the blur.
///
/// The to use when mapping the pixels outside of the border, in X direction.
///
@@ -58,9 +58,11 @@ public static class GaussianSharpenExtensions
/// The to use when mapping the pixels outside of the border, in Y direction.
///
/// The .
- public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
- {
- var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY);
- return source.ApplyProcessor(processor, rectangle);
- }
+ public static IImageProcessingContext GaussianSharpen(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ float sigma,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ => source.ApplyProcessor(new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs
index a08a398b75..bc6fef62a6 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs
@@ -20,21 +20,28 @@ public static class MedianBlurExtensions
/// Whether the filter is applied to alpha as well as the color channels.
///
/// The .
- public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha)
+ public static IImageProcessingContext MedianBlur(
+ this IImageProcessingContext source,
+ int radius,
+ bool preserveAlpha)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha));
///
/// Applies a median blur on the image.
///
/// The current image processing context.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
/// The radius of the area to find the median for.
///
/// Whether the filter is applied to alpha as well as the color channels.
///
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
/// The .
- public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha, Rectangle rectangle)
+ public static IImageProcessingContext MedianBlur(
+ this IImageProcessingContext source,
+ Rectangle rectangle,
+ int radius,
+ bool preserveAlpha)
=> source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha), rectangle);
}
diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
index 713d4d5b77..676acee0f4 100644
--- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
+++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
-using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -42,7 +41,7 @@ public static partial class ProcessingExtensions
/// The containing all the sums.
public static Buffer2D CalculateIntegralImage(this ImageFrame source)
where TPixel : unmanaged, IPixel
- => source.CalculateIntegralImage(source.Bounds());
+ => source.CalculateIntegralImage(source.Bounds);
///
/// Apply an image integral.
@@ -56,7 +55,7 @@ public static partial class ProcessingExtensions
{
Configuration configuration = source.Configuration;
- var interest = Rectangle.Intersect(bounds, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(bounds, source.Bounds);
int startY = interest.Y;
int startX = interest.X;
int endY = interest.Height;
diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
index 73c7c3302d..e17de49d74 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
@@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization;
///
/// Performs Bradley Adaptive Threshold filter against an image.
///
+/// The pixel format.
internal class AdaptiveThresholdProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
@@ -30,7 +31,7 @@ internal class AdaptiveThresholdProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
Configuration configuration = this.Configuration;
TPixel upper = this.definition.Upper.ToPixel();
@@ -97,19 +98,23 @@ internal class AdaptiveThresholdProcessor : ImageProcessor
Span rowSpan = this.source.DangerousGetRowSpan(y).Slice(this.startX, span.Length);
PixelOperations.Instance.ToL8(this.configuration, rowSpan, span);
+ int startY = this.startY;
int maxX = this.bounds.Width - 1;
int maxY = this.bounds.Height - 1;
+ int clusterSize = this.clusterSize;
+ float thresholdLimit = this.thresholdLimit;
+ Buffer2D image = this.intImage;
for (int x = 0; x < rowSpan.Length; x++)
{
- int x1 = Math.Clamp(x - this.clusterSize + 1, 0, maxX);
- int x2 = Math.Min(x + this.clusterSize + 1, maxX);
- int y1 = Math.Clamp(y - this.startY - this.clusterSize + 1, 0, maxY);
- int y2 = Math.Min(y - this.startY + this.clusterSize + 1, maxY);
+ int x1 = Math.Clamp(x - clusterSize + 1, 0, maxX);
+ int x2 = Math.Min(x + clusterSize + 1, maxX);
+ int y1 = Math.Clamp(y - startY - clusterSize + 1, 0, maxY);
+ int y2 = Math.Min(y - startY + clusterSize + 1, maxY);
uint count = (uint)((x2 - x1) * (y2 - y1));
- ulong sum = Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], ulong.MaxValue);
+ ulong sum = Math.Min(image[x2, y2] - image[x1, y2] - image[x2, y1] + image[x1, y1], ulong.MaxValue);
- if (span[x].PackedValue * count <= sum * this.thresholdLimit)
+ if (span[x].PackedValue * count <= sum * thresholdLimit)
{
rowSpan[x] = this.lower;
}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
index 1c76ea6a45..ad87f36c1c 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
@@ -38,8 +38,8 @@ internal class BinaryThresholdProcessor : ImageProcessor
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
- var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
- var operation = new RowOperation(
+ Rectangle interest = Rectangle.Intersect(sourceRectangle, source.Bounds);
+ RowOperation operation = new(
interest.X,
source.PixelBuffer,
upper,
@@ -169,10 +169,8 @@ internal class BinaryThresholdProcessor : ImageProcessor
{
return chroma / (max + min);
}
- else
- {
- return chroma / (2F - max - min);
- }
+
+ return chroma / (2F - max - min);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs
index abe32e3882..bc34f759a0 100644
--- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs
@@ -132,16 +132,14 @@ public abstract class CloningImageProcessor : ICloningImageProcessorThe source image. Cannot be null.
/// The cloned/destination image. Cannot be null.
protected virtual void AfterFrameApply(ImageFrame source, ImageFrame destination)
- {
- }
+ => destination.Metadata.AfterFrameApply(source, destination);
///
/// This method is called after the process is applied to prepare the processor.
///
/// The cloned/destination image. Cannot be null.
protected virtual void AfterImageApply(Image destination)
- {
- }
+ => destination.Metadata.AfterImageApply(destination);
///
/// Disposes the object and frees resources for the Garbage Collector.
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index 5931b7c402..a96fa1993e 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -75,12 +75,12 @@ internal class BokehBlurProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- var sourceRectangle = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle sourceRectangle = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
// Preliminary gamma highlight pass
if (this.gamma == 3F)
{
- var gammaOperation = new ApplyGamma3ExposureRowOperation(sourceRectangle, source.PixelBuffer, this.Configuration);
+ ApplyGamma3ExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration);
ParallelRowIterator.IterateRows(
this.Configuration,
sourceRectangle,
@@ -88,7 +88,7 @@ internal class BokehBlurProcessor : ImageProcessor
}
else
{
- var gammaOperation = new ApplyGammaExposureRowOperation(sourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
+ ApplyGammaExposureRowOperation gammaOperation = new(sourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
ParallelRowIterator.IterateRows(
this.Configuration,
sourceRectangle,
@@ -104,7 +104,7 @@ internal class BokehBlurProcessor : ImageProcessor
// Apply the inverse gamma exposure pass, and write the final pixel data
if (this.gamma == 3F)
{
- var operation = new ApplyInverseGamma3ExposureRowOperation(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration);
+ ApplyInverseGamma3ExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration);
ParallelRowIterator.IterateRows(
this.Configuration,
sourceRectangle,
@@ -112,7 +112,7 @@ internal class BokehBlurProcessor : ImageProcessor
}
else
{
- var operation = new ApplyInverseGammaExposureRowOperation(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, 1 / this.gamma);
+ ApplyInverseGammaExposureRowOperation operation = new(sourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, 1 / this.gamma);
ParallelRowIterator.IterateRows(
this.Configuration,
sourceRectangle,
@@ -146,7 +146,7 @@ internal class BokehBlurProcessor : ImageProcessor
// doing two 1D convolutions with the same kernel, we can use a single kernel sampling map as if
// we were using a 2D kernel with each dimension being the same as the length of our kernel, and
// use the two sampling offset spans resulting from this same map. This saves some extra work.
- using var mapXY = new KernelSamplingMap(configuration.MemoryAllocator);
+ using KernelSamplingMap mapXY = new(configuration.MemoryAllocator);
mapXY.BuildSamplingOffsetMap(this.kernelSize, this.kernelSize, sourceRectangle);
@@ -161,7 +161,7 @@ internal class BokehBlurProcessor : ImageProcessor
Vector4 parameters = Unsafe.Add(ref paramsRef, (uint)i);
// Horizontal convolution
- var horizontalOperation = new FirstPassConvolutionRowOperation(
+ FirstPassConvolutionRowOperation horizontalOperation = new(
sourceRectangle,
firstPassBuffer,
source.PixelBuffer,
@@ -175,7 +175,7 @@ internal class BokehBlurProcessor : ImageProcessor
in horizontalOperation);
// Vertical 1D convolutions to accumulate the partial results on the target buffer
- var verticalOperation = new BokehBlurProcessor.SecondPassConvolutionRowOperation(
+ BokehBlurProcessor.SecondPassConvolutionRowOperation verticalOperation = new(
sourceRectangle,
processingBuffer,
firstPassBuffer,
@@ -342,9 +342,7 @@ internal class BokehBlurProcessor : ImageProcessor
///
[MethodImpl(InliningOptions.ShortMethod)]
public int GetRequiredBufferLength(Rectangle bounds)
- {
- return bounds.Width;
- }
+ => bounds.Width;
///
[MethodImpl(InliningOptions.ShortMethod)]
@@ -391,7 +389,7 @@ internal class BokehBlurProcessor : ImageProcessor
public void Invoke(int y)
{
Vector4 low = Vector4.Zero;
- var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
+ Vector4 high = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
Span targetPixelSpan = this.targetPixels.DangerousGetRowSpan(y)[this.bounds.X..];
Span sourceRowSpan = this.sourceValues.DangerousGetRowSpan(y)[this.bounds.X..];
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
index 8a7c424815..02e06db494 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
@@ -62,14 +62,14 @@ internal class Convolution2DProcessor : ImageProcessor
source.CopyTo(targetPixels);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
- using (var map = new KernelSamplingMap(allocator))
+ using (KernelSamplingMap map = new(allocator))
{
// Since the kernel sizes are identical we can use a single map.
map.BuildSamplingOffsetMap(this.KernelY, interest);
- var operation = new Convolution2DRowOperation(
+ Convolution2DRowOperation operation = new(
interest,
targetPixels,
source.PixelBuffer,
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index 10780a21e2..1bbbdb3501 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -35,18 +35,48 @@ internal class Convolution2PassProcessor : ImageProcessor
Rectangle sourceRectangle,
BorderWrappingMode borderWrapModeX,
BorderWrappingMode borderWrapModeY)
+ : this(configuration, kernel, kernel, preserveAlpha, source, sourceRectangle, borderWrapModeX, borderWrapModeY)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The 1D convolution kernel. X Direction
+ /// The 1D convolution kernel. Y Direction
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public Convolution2PassProcessor(
+ Configuration configuration,
+ float[] kernelX,
+ float[] kernelY,
+ bool preserveAlpha,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
- this.Kernel = kernel;
+ this.KernelX = kernelX;
+ this.KernelY = kernelY;
this.PreserveAlpha = preserveAlpha;
this.BorderWrapModeX = borderWrapModeX;
this.BorderWrapModeY = borderWrapModeY;
}
///
- /// Gets the convolution kernel.
+ /// Gets the convolution kernel. X direction.
+ ///
+ public float[] KernelX { get; }
+
+ ///
+ /// Gets the convolution kernel. Y direction.
///
- public float[] Kernel { get; }
+ public float[] KernelY { get; }
///
/// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
@@ -68,21 +98,21 @@ internal class Convolution2PassProcessor : ImageProcessor
{
using Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
// We can create a single sampling map with the size as if we were using the non separated 2D kernel
// the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur.
- using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator);
+ using KernelSamplingMap mapXY = new(this.Configuration.MemoryAllocator);
- mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
+ mapXY.BuildSamplingOffsetMap(this.KernelX.Length, this.KernelX.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
// Horizontal convolution
- var horizontalOperation = new HorizontalConvolutionRowOperation(
+ HorizontalConvolutionRowOperation horizontalOperation = new(
interest,
firstPassPixels,
source.PixelBuffer,
mapXY,
- this.Kernel,
+ this.KernelX,
this.Configuration,
this.PreserveAlpha);
@@ -92,12 +122,12 @@ internal class Convolution2PassProcessor : ImageProcessor
in horizontalOperation);
// Vertical convolution
- var verticalOperation = new VerticalConvolutionRowOperation(
+ VerticalConvolutionRowOperation verticalOperation = new(
interest,
source.PixelBuffer,
firstPassPixels,
mapXY,
- this.Kernel,
+ this.KernelY,
this.Configuration,
this.PreserveAlpha);
@@ -140,7 +170,7 @@ internal class Convolution2PassProcessor : ImageProcessor
}
///
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRequiredBufferLength(Rectangle bounds)
=> 2 * bounds.Width;
@@ -306,7 +336,7 @@ internal class Convolution2PassProcessor : ImageProcessor
}
///
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRequiredBufferLength(Rectangle bounds)
=> 2 * bounds.Width;
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
new file mode 100644
index 0000000000..995a5164d9
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
+
+///
+/// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image.
+///
+public class ConvolutionProcessor : IImageProcessor
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The 2d gradient operator.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public ConvolutionProcessor(
+ in DenseMatrix kernelXY,
+ bool preserveAlpha,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ {
+ this.KernelXY = kernelXY;
+ this.PreserveAlpha = preserveAlpha;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
+ }
+
+ ///
+ /// Gets the 2d convolution kernel.
+ ///
+ public DenseMatrix KernelXY { get; }
+
+ ///
+ /// Gets a value indicating whether the convolution filter is applied to alpha as well as the color channels.
+ ///
+ public bool PreserveAlpha { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
+ ///
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
+ where TPixel : unmanaged,
+ IPixel
+ {
+ if (this.KernelXY.TryGetLinearlySeparableComponents(out float[]? kernelX, out float[]? kernelY))
+ {
+ return new Convolution2PassProcessor(
+ configuration,
+ kernelX,
+ kernelY,
+ this.PreserveAlpha,
+ source,
+ sourceRectangle,
+ this.BorderWrapModeX,
+ this.BorderWrapModeY);
+ }
+
+ return new ConvolutionProcessor(
+ configuration,
+ this.KernelXY,
+ this.PreserveAlpha,
+ source,
+ sourceRectangle,
+ this.BorderWrapModeX,
+ this.BorderWrapModeY);
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
index ae79f2c31d..feaaf30ce0 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
@@ -31,10 +31,34 @@ internal class ConvolutionProcessor : ImageProcessor
bool preserveAlpha,
Image source,
Rectangle sourceRectangle)
+ : this(configuration, kernelXY, preserveAlpha, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The 2d gradient operator.
+ /// Whether the convolution filter is applied to alpha as well as the color channels.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public ConvolutionProcessor(
+ Configuration configuration,
+ in DenseMatrix kernelXY,
+ bool preserveAlpha,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
this.KernelXY = kernelXY;
this.PreserveAlpha = preserveAlpha;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -47,6 +71,16 @@ internal class ConvolutionProcessor : ImageProcessor
///
public bool PreserveAlpha { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
@@ -55,13 +89,13 @@ internal class ConvolutionProcessor : ImageProcessor
source.CopyTo(targetPixels);
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
- using (var map = new KernelSamplingMap(allocator))
+ using (KernelSamplingMap map = new(allocator))
{
- map.BuildSamplingOffsetMap(this.KernelXY, interest);
+ map.BuildSamplingOffsetMap(this.KernelXY.Rows, this.KernelXY.Columns, interest, this.BorderWrapModeX, this.BorderWrapModeY);
- var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha);
+ RowOperation operation = new(interest, targetPixels, source.PixelBuffer, map, this.KernelXY, this.Configuration, this.PreserveAlpha);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -121,7 +155,7 @@ internal class ConvolutionProcessor : ImageProcessor
ref Vector4 targetRowRef = ref MemoryMarshal.GetReference(span);
Span targetRowSpan = this.targetPixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth);
- var state = new ConvolutionState(in this.kernel, this.map);
+ ConvolutionState state = new(in this.kernel, this.map);
int row = y - this.bounds.Y;
ref int sampleRowBase = ref state.GetSampleRow((uint)row);
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
index ae891f3507..eae7481661 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
@@ -58,12 +58,12 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
// We need a clean copy for each pass to start from
using ImageFrame cleanCopy = source.Clone();
- using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[0], true, this.Source, interest))
+ using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[0], true, this.Source, interest))
{
processor.Apply(source);
}
@@ -78,12 +78,12 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor
{
using ImageFrame pass = cleanCopy.Clone();
- using (var processor = new ConvolutionProcessor(this.Configuration, in this.kernels[i], true, this.Source, interest))
+ using (ConvolutionProcessor processor = new(this.Configuration, in this.kernels[i], true, this.Source, interest))
{
processor.Apply(pass);
}
- var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest);
+ RowOperation operation = new(source.PixelBuffer, pass.PixelBuffer, interest);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
index c353f46b5f..3139d24bb4 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
@@ -53,7 +53,7 @@ internal class EdgeDetectorProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new ConvolutionProcessor(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle);
+ using ConvolutionProcessor processor = new(this.Configuration, in this.kernelXY, true, this.Source, this.SourceRectangle);
processor.Apply(source);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
index 6518375b9e..d762dd336b 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
@@ -12,24 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
internal class GaussianBlurProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The configuration which allows altering default behaviour or extending the library.
- /// The defining the processor parameters.
- /// The source for the current processor instance.
- /// The source area to process for the current processor instance.
- public GaussianBlurProcessor(
- Configuration configuration,
- GaussianBlurProcessor definition,
- Image source,
- Rectangle sourceRectangle)
- : base(configuration, source, sourceRectangle)
- {
- int kernelSize = (definition.Radius * 2) + 1;
- this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
- }
-
///
/// Initializes a new instance of the class.
///
@@ -72,7 +54,7 @@ internal class GaussianBlurProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
+ using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
index a286201dff..bdb3a4b380 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
@@ -12,22 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution;
internal class GaussianSharpenProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The configuration which allows altering default behaviour or extending the library.
- /// The defining the processor parameters.
- /// The source for the current processor instance.
- /// The source area to process for the current processor instance.
- public GaussianSharpenProcessor(
- Configuration configuration,
- GaussianSharpenProcessor definition,
- Image source,
- Rectangle sourceRectangle)
- : this(configuration, definition, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
- {
- }
-
///
/// Initializes a new instance of the class.
///
@@ -70,7 +54,7 @@ internal class GaussianSharpenProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
+ using Convolution2PassProcessor processor = new(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs
index fe3a29d437..3e75d8b840 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/MedianBlurProcessor{TPixel}.cs
@@ -29,7 +29,7 @@ internal sealed class MedianBlurProcessor : ImageProcessor
source.CopyTo(targetPixels);
- Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
using KernelSamplingMap map = new(this.Configuration.MemoryAllocator);
map.BuildSamplingOffsetMap(kernelSize, kernelSize, interest, this.definition.BorderWrapModeX, this.definition.BorderWrapModeY);
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
index a680393c8c..565a5746d1 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
@@ -21,12 +21,12 @@ internal static class BokehBlurKernelDataProvider
///
/// Gets the kernel scales to adjust the component values in each kernel
///
- private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f };
+ private static float[] KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f };
///
/// Gets the available bokeh blur kernel parameters
///
- private static IReadOnlyList KernelComponents { get; } = new[]
+ private static Vector4[][] KernelComponents { get; } = new[]
{
// 1 component
new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) },
@@ -112,7 +112,7 @@ internal static class BokehBlurKernelDataProvider
private static (Vector4[] Parameters, float Scale) GetParameters(int componentsCount)
{
// Prepare the kernel components
- int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Count));
+ int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Length));
return (KernelComponents[index], KernelScales[index]);
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErroDither.KnownTypes.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.KnownTypes.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Dithering/ErroDither.KnownTypes.cs
rename to src/ImageSharp/Processing/Processors/Dithering/ErrorDither.KnownTypes.cs
diff --git a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
index ac2921b98d..3217601270 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
@@ -21,7 +21,7 @@ public interface IDither
/// The source image.
/// The destination quantized frame.
/// The region of interest bounds.
- void ApplyQuantizationDither(
+ public void ApplyQuantizationDither(
ref TFrameQuantizer quantizer,
ImageFrame source,
IndexedImageFrame destination,
@@ -38,7 +38,7 @@ public interface IDither
/// The palette dithering processor.
/// The source image.
/// The region of interest bounds.
- void ApplyPaletteDither(
+ public void ApplyPaletteDither(
in TPaletteDitherImageProcessor processor,
ImageFrame source,
Rectangle bounds)
diff --git a/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
index e406d82c69..347e2f0ef6 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
@@ -15,22 +15,22 @@ public interface IPaletteDitherImageProcessor
///
/// Gets the configuration instance to use when performing operations.
///
- Configuration Configuration { get; }
+ public Configuration Configuration { get; }
///
/// Gets the dithering palette.
///
- ReadOnlyMemory Palette { get; }
+ public ReadOnlyMemory Palette { get; }
///
/// Gets the dithering scale used to adjust the amount of dither. Range 0..1.
///
- float DitherScale { get; }
+ public float DitherScale { get; }
///
/// Returns the color from the dithering palette corresponding to the given color.
///
/// The color to match.
/// The match.
- TPixel GetPaletteColor(TPixel color);
+ public TPixel GetPaletteColor(TPixel color);
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
index 982cc7d46c..0d4680e21f 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
@@ -46,7 +46,7 @@ internal sealed class PaletteDitherProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
this.dither.ApplyPaletteDither(in this.ditherProcessor, source, interest);
}
@@ -80,7 +80,7 @@ internal sealed class PaletteDitherProcessor : ImageProcessor
Justification = "https://github.com/dotnet/roslyn-analyzers/issues/6151")]
internal readonly struct DitherProcessor : IPaletteDitherImageProcessor, IDisposable
{
- private readonly EuclideanPixelMap pixelMap;
+ private readonly PixelMap pixelMap;
[MethodImpl(InliningOptions.ShortMethod)]
public DitherProcessor(
@@ -89,7 +89,7 @@ internal sealed class PaletteDitherProcessor : ImageProcessor
float ditherScale)
{
this.Configuration = configuration;
- this.pixelMap = new EuclideanPixelMap(configuration, palette);
+ this.pixelMap = PixelMapFactory.Create(configuration, palette, ColorMatchingMode.Coarse);
this.Palette = palette;
this.DitherScale = ditherScale;
}
diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs
index 847a211a5a..06cfa49b3d 100644
--- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor.cs
@@ -36,14 +36,12 @@ internal sealed class PixelRowDelegateProcessor : IImageProcessor
///
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel
- {
- return new PixelRowDelegateProcessor(
+ => new PixelRowDelegateProcessor(
new PixelRowDelegate(this.PixelRowOperation),
configuration,
this.Modifiers,
source,
sourceRectangle);
- }
///
/// A implementing the row processing logic for .
@@ -54,9 +52,7 @@ internal sealed class PixelRowDelegateProcessor : IImageProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public PixelRowDelegate(PixelRowOperation pixelRowOperation)
- {
- this.pixelRowOperation = pixelRowOperation;
- }
+ => this.pixelRowOperation = pixelRowOperation;
///
[MethodImpl(InliningOptions.ShortMethod)]
diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
index 36bb327cf2..d38ffc801e 100644
--- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
@@ -48,8 +48,8 @@ internal sealed class PixelRowDelegateProcessor : ImageProces
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowOperation(interest.X, source.PixelBuffer, this.Configuration, this.modifiers, this.rowDelegate);
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
+ RowOperation operation = new(interest.X, source.PixelBuffer, this.Configuration, this.modifiers, this.rowDelegate);
ParallelRowIterator.IterateRows(
this.Configuration,
diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs
index 68000ba3e8..c828b95b62 100644
--- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs
@@ -32,7 +32,7 @@ internal class PixelateProcessor : ImageProcessor
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
int size = this.Size;
Guard.MustBeBetweenOrEqualTo(size, 0, interest.Width, nameof(size));
diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
index 5109139647..37286086c6 100644
--- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
@@ -27,15 +27,13 @@ internal class FilterProcessor : ImageProcessor
/// The source area to process for the current processor instance.
public FilterProcessor(Configuration configuration, FilterProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
- {
- this.definition = definition;
- }
+ => this.definition = definition;
///
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowOperation(interest.X, source.PixelBuffer, this.definition.Matrix, this.Configuration);
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
+ RowOperation operation = new(interest.X, source.PixelBuffer, this.definition.Matrix, this.Configuration);
ParallelRowIterator.IterateRows(
this.Configuration,
diff --git a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs
index 93e600106a..41560d1200 100644
--- a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs
@@ -23,9 +23,9 @@ internal sealed class OpaqueProcessor : ImageProcessor
protected override void OnFrameApply(ImageFrame source)
{
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
- var operation = new OpaqueRowOperation(this.Configuration, source.PixelBuffer, interest);
+ OpaqueRowOperation operation = new(this.Configuration, source.PixelBuffer, interest);
ParallelRowIterator.IterateRows(this.Configuration, interest, in operation);
}
diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs
index 2fa79220e5..e1f7d1fffb 100644
--- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs
@@ -95,7 +95,7 @@ public abstract class ImageProcessor : IImageProcessor
protected abstract void OnFrameApply(ImageFrame source);
///
- /// This method is called after the process is applied to prepare the processor.
+ /// This method is called after the process is applied to each frame.
///
/// The source image. Cannot be null.
protected virtual void AfterFrameApply(ImageFrame source)
@@ -103,11 +103,10 @@ public abstract class ImageProcessor : IImageProcessor
}
///
- /// This method is called after the process is applied to prepare the processor.
+ /// This method is called after the process is applied to the complete image.
///
protected virtual void AfterImageApply()
- {
- }
+ => this.Source.Metadata.AfterImageApply(this.Source);
///
/// Disposes the object and frees resources for the Garbage Collector.
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs
index 6f4493f951..606789af96 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs
@@ -28,9 +28,9 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor
/// Indicating whether to clip the histogram bins at a specific value.
/// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.
+ /// Whether to apply a synchronized luminance value to each color channel.
/// The source for the current processor instance.
/// The source area to process for the current processor instance.
- /// Whether to apply a synchronized luminance value to each color channel.
public AutoLevelProcessor(
Configuration configuration,
int luminanceLevels,
@@ -40,9 +40,7 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor source,
Rectangle sourceRectangle)
: base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle)
- {
- this.SyncChannels = syncChannels;
- }
+ => this.SyncChannels = syncChannels;
///
/// Gets a value indicating whether to apply a synchronized luminance value to each color channel.
@@ -54,12 +52,12 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels.
- var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
+ GrayscaleLevelsRowOperation grayscaleOperation = new(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows, Vector4>(
this.Configuration,
interest,
@@ -83,7 +81,7 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor(
this.Configuration,
interest,
@@ -91,7 +89,7 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor(
this.Configuration,
interest,
@@ -136,10 +134,10 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor span)
{
- Span vectorBuffer = span.Slice(0, this.bounds.Width);
+ Span vectorBuffer = span[..this.bounds.Width];
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
- var sourceAccess = new PixelAccessor(this.source);
+ PixelAccessor sourceAccess = new(this.source);
int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
@@ -148,12 +146,11 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow);
@@ -197,10 +194,10 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor span)
{
- Span vectorBuffer = span.Slice(0, this.bounds.Width);
+ Span vectorBuffer = span[..this.bounds.Width];
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
- var sourceAccess = new PixelAccessor(this.source);
+ PixelAccessor sourceAccess = new(this.source);
int levelsMinusOne = this.luminanceLevels - 1;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
@@ -209,7 +206,7 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor : HistogramEqualizat
{
MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;
int numberOfPixels = source.Width * source.Height;
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds);
using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels.
- var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
+ GrayscaleLevelsRowOperation grayscaleOperation = new(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows, Vector4>(
this.Configuration,
interest,
@@ -74,7 +74,7 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
- var cdfOperation = new CdfApplicationRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
+ CdfApplicationRowOperation cdfOperation = new(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -118,7 +118,7 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span span)
{
- Span