diff --git a/.editorconfig b/.editorconfig
index 06e698247..0e4883082 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -368,8 +368,6 @@ csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
-csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_for_built_in_types = never
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = false:warning
-
-csharp_prefer_simple_using_statement = false:silent
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 40878c575..f1d4afef4 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -334,6 +334,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingS
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/shared-infrastructure b/shared-infrastructure
index 36b2d55f5..ea561c249 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5
+Subproject commit ea561c249ba86352fe3b69e612b8072f3652eacb
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 2c13e469f..a78a75d42 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -34,4 +34,6 @@
+
+
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index 68d4f8949..d7171aa0f 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -52,4 +52,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation.cs b/src/ImageSharp/Advanced/IRowIntervalOperation.cs
index 3e1b08621..980ed91a7 100644
--- a/src/ImageSharp/Advanced/IRowIntervalOperation.cs
+++ b/src/ImageSharp/Advanced/IRowIntervalOperation.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
diff --git a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
index c18842a92..47fcf253e 100644
--- a/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
+++ b/src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
@@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Buffers;
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
diff --git a/src/ImageSharp/Advanced/IRowOperation.cs b/src/ImageSharp/Advanced/IRowOperation.cs
new file mode 100644
index 000000000..0a6065e4b
--- /dev/null
+++ b/src/ImageSharp/Advanced/IRowOperation.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Defines the contract for an action that operates on a row.
+ ///
+ public interface IRowOperation
+ {
+ ///
+ /// Invokes the method passing the row y coordinate.
+ ///
+ /// The row y coordinate.
+ void Invoke(int y);
+ }
+}
diff --git a/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
new file mode 100644
index 000000000..7a13930fa
--- /dev/null
+++ b/src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Defines the contract for an action that operates on a row with a temporary buffer.
+ ///
+ /// The type of buffer elements.
+ public interface IRowOperation
+ where TBuffer : unmanaged
+ {
+ ///
+ /// Invokes the method passing the row and a buffer.
+ ///
+ /// The row y coordinate.
+ /// The contiguous region of memory.
+ void Invoke(int y, Span span);
+ }
+}
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
index adbad0d66..3f0f77ca3 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
@@ -17,51 +17,130 @@ namespace SixLabors.ImageSharp.Advanced
///
public static partial class ParallelRowIterator
{
- private readonly struct IterationParameters
+ private readonly struct RowOperationWrapper
+ where T : struct, IRowOperation
{
- public readonly int MinY;
- public readonly int MaxY;
- public readonly int StepY;
- public readonly int Width;
+ private readonly int minY;
+ private readonly int maxY;
+ private readonly int stepY;
+ private readonly T action;
- public IterationParameters(int minY, int maxY, int stepY)
- : this(minY, maxY, stepY, 0)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public RowOperationWrapper(
+ int minY,
+ int maxY,
+ int stepY,
+ in T action)
{
+ this.minY = minY;
+ this.maxY = maxY;
+ this.stepY = stepY;
+ this.action = action;
}
- public IterationParameters(int minY, int maxY, int stepY, int width)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(int i)
{
- this.MinY = minY;
- this.MaxY = maxY;
- this.StepY = stepY;
- this.Width = width;
+ int yMin = this.minY + (i * this.stepY);
+
+ if (yMin >= this.maxY)
+ {
+ return;
+ }
+
+ int yMax = Math.Min(yMin + this.stepY, this.maxY);
+
+ for (int y = yMin; y < yMax; y++)
+ {
+ // Skip the safety copy when invoking a potentially impure method on a readonly field
+ Unsafe.AsRef(this.action).Invoke(y);
+ }
+ }
+ }
+
+ private readonly struct RowOperationWrapper
+ where T : struct, IRowOperation
+ where TBuffer : unmanaged
+ {
+ private readonly int minY;
+ private readonly int maxY;
+ private readonly int stepY;
+ private readonly int width;
+ private readonly MemoryAllocator allocator;
+ private readonly T action;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public RowOperationWrapper(
+ int minY,
+ int maxY,
+ int stepY,
+ int width,
+ MemoryAllocator allocator,
+ in T action)
+ {
+ this.minY = minY;
+ this.maxY = maxY;
+ this.stepY = stepY;
+ this.width = width;
+ this.allocator = allocator;
+ this.action = action;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void Invoke(int i)
+ {
+ int yMin = this.minY + (i * this.stepY);
+
+ if (yMin >= this.maxY)
+ {
+ return;
+ }
+
+ int yMax = Math.Min(yMin + this.stepY, this.maxY);
+
+ using IMemoryOwner buffer = this.allocator.Allocate(this.width);
+
+ Span span = buffer.Memory.Span;
+
+ for (int y = yMin; y < yMax; y++)
+ {
+ Unsafe.AsRef(this.action).Invoke(y, span);
+ }
}
}
private readonly struct RowIntervalOperationWrapper
where T : struct, IRowIntervalOperation
{
- private readonly IterationParameters info;
+ private readonly int minY;
+ private readonly int maxY;
+ private readonly int stepY;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperationWrapper(in IterationParameters info, in T operation)
+ public RowIntervalOperationWrapper(
+ int minY,
+ int maxY,
+ int stepY,
+ in T operation)
{
- this.info = info;
+ this.minY = minY;
+ this.maxY = maxY;
+ this.stepY = stepY;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
- int yMin = this.info.MinY + (i * this.info.StepY);
+ int yMin = this.minY + (i * this.stepY);
- if (yMin >= this.info.MaxY)
+ if (yMin >= this.maxY)
{
return;
}
- int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
+ int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
// Skip the safety copy when invoking a potentially impure method on a readonly field
@@ -73,17 +152,26 @@ namespace SixLabors.ImageSharp.Advanced
where T : struct, IRowIntervalOperation
where TBuffer : unmanaged
{
- private readonly IterationParameters info;
+ private readonly int minY;
+ private readonly int maxY;
+ private readonly int stepY;
+ private readonly int width;
private readonly MemoryAllocator allocator;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(
- in IterationParameters info,
+ int minY,
+ int maxY,
+ int stepY,
+ int width,
MemoryAllocator allocator,
in T operation)
{
- this.info = info;
+ this.minY = minY;
+ this.maxY = maxY;
+ this.stepY = stepY;
+ this.width = width;
this.allocator = allocator;
this.operation = operation;
}
@@ -91,17 +179,17 @@ namespace SixLabors.ImageSharp.Advanced
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
- int yMin = this.info.MinY + (i * this.info.StepY);
+ int yMin = this.minY + (i * this.stepY);
- if (yMin >= this.info.MaxY)
+ if (yMin >= this.maxY)
{
return;
}
- int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
+ int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
- using IMemoryOwner buffer = this.allocator.Allocate(this.info.Width);
+ using IMemoryOwner buffer = this.allocator.Allocate(this.width);
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
}
diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs
index 123784c57..fb85de986 100644
--- a/src/ImageSharp/Advanced/ParallelRowIterator.cs
+++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs
@@ -17,6 +17,135 @@ namespace SixLabors.ImageSharp.Advanced
///
public static partial class ParallelRowIterator
{
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ ///
+ /// The type of row operation to perform.
+ /// The to get the parallel settings from.
+ /// The .
+ /// The operation defining the iteration logic on a single row.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
+ where T : struct, IRowOperation
+ {
+ var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
+ IterateRows(rectangle, in parallelSettings, in operation);
+ }
+
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ ///
+ /// The type of row operation to perform.
+ /// The .
+ /// The .
+ /// The operation defining the iteration logic on a single row.
+ public static void IterateRows(
+ Rectangle rectangle,
+ in ParallelExecutionSettings parallelSettings,
+ in T operation)
+ where T : struct, IRowOperation
+ {
+ ValidateRectangle(rectangle);
+
+ int top = rectangle.Top;
+ int bottom = rectangle.Bottom;
+ int width = rectangle.Width;
+ int height = rectangle.Height;
+
+ int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
+ int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
+
+ // Avoid TPL overhead in this trivial case:
+ if (numOfSteps == 1)
+ {
+ for (int y = top; y < bottom; y++)
+ {
+ Unsafe.AsRef(operation).Invoke(y);
+ }
+
+ return;
+ }
+
+ int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
+ var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
+ var wrappingOperation = new RowOperationWrapper(top, bottom, verticalStep, in operation);
+
+ Parallel.For(
+ 0,
+ numOfSteps,
+ parallelOptions,
+ wrappingOperation.Invoke);
+ }
+
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ /// instantiating a temporary buffer for each invocation.
+ ///
+ /// The type of row operation to perform.
+ /// The type of buffer elements.
+ /// The to get the parallel settings from.
+ /// The .
+ /// The operation defining the iteration logic on a single row.
+ public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
+ where T : struct, IRowOperation
+ where TBuffer : unmanaged
+ {
+ var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
+ IterateRows(rectangle, in parallelSettings, in operation);
+ }
+
+ ///
+ /// Iterate through the rows of a rectangle in optimized batches.
+ /// instantiating a temporary buffer for each invocation.
+ ///
+ /// The type of row operation to perform.
+ /// The type of buffer elements.
+ /// The .
+ /// The .
+ /// The operation defining the iteration logic on a single row.
+ public static void IterateRows(
+ Rectangle rectangle,
+ in ParallelExecutionSettings parallelSettings,
+ in T operation)
+ where T : struct, IRowOperation
+ where TBuffer : unmanaged
+ {
+ ValidateRectangle(rectangle);
+
+ int top = rectangle.Top;
+ int bottom = rectangle.Bottom;
+ int width = rectangle.Width;
+ int height = rectangle.Height;
+
+ int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
+ int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
+ MemoryAllocator allocator = parallelSettings.MemoryAllocator;
+
+ // Avoid TPL overhead in this trivial case:
+ if (numOfSteps == 1)
+ {
+ using IMemoryOwner buffer = allocator.Allocate(width);
+ Span span = buffer.Memory.Span;
+
+ for (int y = top; y < bottom; y++)
+ {
+ Unsafe.AsRef(operation).Invoke(y, span);
+ }
+
+ return;
+ }
+
+ int verticalStep = DivideCeil(height, numOfSteps);
+ var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
+ var wrappingOperation = new RowOperationWrapper(top, bottom, verticalStep, width, allocator, in operation);
+
+ Parallel.For(
+ 0,
+ numOfSteps,
+ parallelOptions,
+ wrappingOperation.Invoke);
+ }
+
///
/// Iterate through the rows of a rectangle in optimized batches defined by -s.
///
@@ -25,11 +154,11 @@ namespace SixLabors.ImageSharp.Advanced
/// The .
/// The operation defining the iteration logic on a single .
[MethodImpl(InliningOptions.ShortMethod)]
- public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
+ public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
- IterateRows(rectangle, in parallelSettings, in operation);
+ IterateRowIntervals(rectangle, in parallelSettings, in operation);
}
///
@@ -39,7 +168,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The .
/// The .
/// The operation defining the iteration logic on a single .
- public static void IterateRows(
+ public static void IterateRowIntervals(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
@@ -65,8 +194,7 @@ namespace SixLabors.ImageSharp.Advanced
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
- var info = new IterationParameters(top, bottom, verticalStep);
- var wrappingOperation = new RowIntervalOperationWrapper(in info, in operation);
+ var wrappingOperation = new RowIntervalOperationWrapper(top, bottom, verticalStep, in operation);
Parallel.For(
0,
@@ -84,12 +212,12 @@ namespace SixLabors.ImageSharp.Advanced
/// The to get the parallel settings from.
/// The .
/// The operation defining the iteration logic on a single .
- public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation)
+ public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
- IterateRows(rectangle, in parallelSettings, in operation);
+ IterateRowIntervals(rectangle, in parallelSettings, in operation);
}
///
@@ -101,7 +229,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The .
/// The .
/// The operation defining the iteration logic on a single .
- public static void IterateRows(
+ public static void IterateRowIntervals(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
@@ -123,18 +251,16 @@ namespace SixLabors.ImageSharp.Advanced
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
- using (IMemoryOwner buffer = allocator.Allocate(width))
- {
- Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
- }
+ using IMemoryOwner buffer = allocator.Allocate(width);
+
+ Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
- var info = new IterationParameters(top, bottom, verticalStep, width);
- var wrappingOperation = new RowIntervalOperationWrapper(in info, allocator, in operation);
+ var wrappingOperation = new RowIntervalOperationWrapper(top, bottom, verticalStep, width, allocator, in operation);
Parallel.For(
0,
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index 4be3f0079..5b312f4f8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into .
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -435,4 +435,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index 971bff322..5d8668257 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
+#if !SUPPORTS_SPAN_STREAM
+using System.Buffers;
+#endif
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs
index 1d215d286..3ab1b199a 100644
--- a/src/ImageSharp/Common/Helpers/Guard.cs
+++ b/src/ImageSharp/Common/Helpers/Guard.cs
@@ -22,7 +22,7 @@ namespace SixLabors
{
if (!value.GetType().GetTypeInfo().IsValueType)
{
- ThrowArgumentException("Type must be a struct.", parameterName);
+ ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 66a60d533..7d2799503 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -336,8 +336,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write8BitColor(Stream stream, ImageFrame image, Span colorPalette)
where TPixel : unmanaged, IPixel
{
- using IFrameQuantizer quantizer = this.quantizer.CreateFrameQuantizer(this.configuration);
- using QuantizedFrame quantized = quantizer.QuantizeFrame(image, image.Bounds());
+ using IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration);
+ using IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(image, image.Bounds());
ReadOnlySpan quantizedColors = quantized.Palette.Span;
var color = default(Rgba32);
@@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = image.Height - 1; y >= 0; y--)
{
- ReadOnlySpan pixelSpan = quantized.GetRowSpan(y);
+ ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y);
stream.Write(pixelSpan);
for (int i = 0; i < this.padding; i++)
diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs
index c9d631da0..06c4b3fc6 100644
--- a/src/ImageSharp/Formats/Gif/GifConstants.cs
+++ b/src/ImageSharp/Formats/Gif/GifConstants.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Collections.Generic;
using System.Text;
@@ -21,11 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public const string FileVersion = "89a";
- ///
- /// The ASCII encoded bytes used to identify the GIF file.
- ///
- internal static readonly byte[] MagicNumber = Encoding.ASCII.GetBytes(FileType + FileVersion);
-
///
/// The extension block introducer !.
///
@@ -51,11 +47,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public const string NetscapeApplicationIdentification = "NETSCAPE2.0";
- ///
- /// The ASCII encoded application identification bytes.
- ///
- internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.ASCII.GetBytes(NetscapeApplicationIdentification);
-
///
/// The Netscape looping application sub block size.
///
@@ -110,5 +101,25 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The collection of file extensions that equate to a Gif.
///
public static readonly IEnumerable FileExtensions = new[] { "gif" };
+
+ ///
+ /// Gets the ASCII encoded bytes used to identify the GIF file (combining and ).
+ ///
+ internal static ReadOnlySpan MagicNumber => new[]
+ {
+ (byte)'G', (byte)'I', (byte)'F',
+ (byte)'8', (byte)'9', (byte)'a'
+ };
+
+ ///
+ /// Gets the ASCII encoded application identification bytes (representing ).
+ ///
+ internal static ReadOnlySpan NetscapeApplicationIdentificationBytes => new[]
+ {
+ (byte)'N', (byte)'E', (byte)'T',
+ (byte)'S', (byte)'C', (byte)'A',
+ (byte)'P', (byte)'E',
+ (byte)'2', (byte)'.', (byte)'0'
+ };
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs
index 978609d7f..53c4c6f3f 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -4,6 +4,7 @@
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
@@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Gets or sets the quantizer for reducing the color count.
/// Defaults to the
///
- public IQuantizer Quantizer { get; set; } = new OctreeQuantizer();
+ public IQuantizer Quantizer { get; set; } = KnownQuantizers.Octree;
///
/// Gets or sets the color table mode: Global or local.
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index e32910d37..887540930 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -79,14 +79,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
- QuantizedFrame quantized;
+ IndexedImageFrame quantized;
using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration))
{
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
}
// Get the number of bits.
- this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length);
// Write the header.
this.WriteHeader(stream);
@@ -119,15 +119,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Clean up.
- quantized?.Dispose();
+ quantized.Dispose();
// TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer);
}
- private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream)
+ private void EncodeGlobal(Image image, IndexedImageFrame quantized, int transparencyIndex, Stream stream)
where TPixel : unmanaged, IPixel
{
+ // The palette quantizer can reuse the same pixel map across multiple frames
+ // since the palette is unchanging. This allows a reduction of memory usage across
+ // multi frame gifs using a global palette.
+ EuclideanPixelMap pixelMap = default;
+ bool pixelMapSet = false;
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame frame = image.Frames[i];
@@ -142,22 +147,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
- using (var paletteFrameQuantizer = new PaletteFrameQuantizer(this.configuration, this.quantizer.Options, quantized.Palette))
- using (QuantizedFrame paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame, frame.Bounds()))
+ if (!pixelMapSet)
{
- this.WriteImageData(paletteQuantized, stream);
+ pixelMapSet = true;
+ pixelMap = new EuclideanPixelMap(this.configuration, quantized.Palette);
}
+
+ using var paletteFrameQuantizer = new PaletteFrameQuantizer(this.configuration, this.quantizer.Options, pixelMap);
+ using IndexedImageFrame paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame, frame.Bounds());
+ this.WriteImageData(paletteQuantized, stream);
}
}
}
- private void EncodeLocal(Image image, QuantizedFrame quantized, Stream stream)
+ private void EncodeLocal(Image image, IndexedImageFrame quantized, Stream stream)
where TPixel : unmanaged, IPixel
{
ImageFrame previousFrame = null;
GifFrameMetadata previousMeta = null;
- foreach (ImageFrame frame in image.Frames)
+ for (int i = 0; i < image.Frames.Count; i++)
{
+ ImageFrame frame = image.Frames[i];
ImageFrameMetadata metadata = frame.Metadata;
GifFrameMetadata frameMetadata = metadata.GetGifMetadata();
if (quantized is null)
@@ -173,27 +183,23 @@ namespace SixLabors.ImageSharp.Formats.Gif
MaxColors = frameMetadata.ColorTableLength
};
- using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration, options))
- {
- quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
- }
+ using IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration, options);
+ quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
}
else
{
- using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration))
- {
- quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
- }
+ using IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(this.configuration);
+ quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
}
}
- this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length);
this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
- quantized?.Dispose();
+ quantized.Dispose();
quantized = null; // So next frame can regenerate it
previousFrame = frame;
previousMeta = frameMetadata;
@@ -208,25 +214,23 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The .
///
- private int GetTransparentIndex(QuantizedFrame quantized)
+ private int GetTransparentIndex(IndexedImageFrame quantized)
where TPixel : unmanaged, IPixel
{
- // Transparent pixels are much more likely to be found at the end of a palette
+ // Transparent pixels are much more likely to be found at the end of a palette.
int index = -1;
- int length = quantized.Palette.Length;
+ ReadOnlySpan paletteSpan = quantized.Palette.Span;
- using (IMemoryOwner rgbaBuffer = this.memoryAllocator.Allocate(length))
- {
- Span rgbaSpan = rgbaBuffer.GetSpan();
- ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan);
- PixelOperations.Instance.ToRgba32(this.configuration, quantized.Palette.Span, rgbaSpan);
+ using IMemoryOwner rgbaOwner = quantized.Configuration.MemoryAllocator.Allocate(paletteSpan.Length);
+ Span rgbaSpan = rgbaOwner.GetSpan();
+ PixelOperations.Instance.ToRgba32(quantized.Configuration, paletteSpan, rgbaSpan);
+ ref Rgba32 rgbaSpanRef = ref MemoryMarshal.GetReference(rgbaSpan);
- for (int i = quantized.Palette.Length - 1; i >= 0; i--)
+ for (int i = rgbaSpan.Length - 1; i >= 0; i--)
+ {
+ if (Unsafe.Add(ref rgbaSpanRef, i).Equals(default))
{
- if (Unsafe.Add(ref paletteRef, i).Equals(default))
- {
- index = i;
- }
+ index = i;
}
}
@@ -238,7 +242,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The stream to write to.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber, 0, GifConstants.MagicNumber.Length);
+ private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber);
///
/// Writes the logical screen descriptor to the stream.
@@ -326,8 +330,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
return;
}
- foreach (string comment in metadata.Comments)
+ for (var i = 0; i < metadata.Comments.Count; i++)
{
+ string comment = metadata.Comments[i];
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.CommentLabel;
stream.Write(this.buffer, 0, 2);
@@ -335,7 +340,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Comment will be stored in chunks of 255 bytes, if it exceeds this size.
ReadOnlySpan commentSpan = comment.AsSpan();
int idx = 0;
- for (; idx <= comment.Length - GifConstants.MaxCommentSubBlockLength; idx += GifConstants.MaxCommentSubBlockLength)
+ for (;
+ idx <= comment.Length - GifConstants.MaxCommentSubBlockLength;
+ idx += GifConstants.MaxCommentSubBlockLength)
{
WriteCommentSubBlock(stream, commentSpan, idx, GifConstants.MaxCommentSubBlockLength);
}
@@ -391,7 +398,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The extension to write to the stream.
/// The stream to write to.
- public void WriteExtension(IGifExtension extension, Stream stream)
+ private void WriteExtension(TGifExtension extension, Stream stream)
+ where TGifExtension : struct, IGifExtension
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = extension.Label;
@@ -437,37 +445,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The pixel format.
/// The to encode.
/// The stream to write to.
- private void WriteColorTable(QuantizedFrame image, Stream stream)
+ private void WriteColorTable(IndexedImageFrame image, Stream stream)
where TPixel : unmanaged, IPixel
{
// The maximum number of colors for the bit depth
- int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
- int pixelCount = image.Palette.Length;
+ int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * Unsafe.SizeOf();
- using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
- {
- PixelOperations.Instance.ToRgb24Bytes(
- this.configuration,
- image.Palette.Span,
- colorTable.GetSpan(),
- pixelCount);
- stream.Write(colorTable.Array, 0, colorTableLength);
- }
+ using IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength, AllocationOptions.Clean);
+ PixelOperations.Instance.ToRgb24Bytes(
+ this.configuration,
+ image.Palette.Span,
+ colorTable.GetSpan(),
+ image.Palette.Length);
+
+ stream.Write(colorTable.Array, 0, colorTableLength);
}
///
/// Writes the image pixel data to the stream.
///
/// The pixel format.
- /// The containing indexed pixels.
+ /// The containing indexed pixels.
/// The stream to write to.
- private void WriteImageData(QuantizedFrame image, Stream stream)
+ private void WriteImageData(IndexedImageFrame image, Stream stream)
where TPixel : unmanaged, IPixel
{
- using (var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth))
- {
- encoder.Encode(image.GetPixelSpan(), stream);
- }
+ using var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth);
+ encoder.Encode(image.GetPixelBufferSpan(), stream);
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index eda0c5fb8..056076bf0 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -274,7 +274,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = this.NextPixel(indexedPixels);
- // TODO: PERF: It looks likt hshift could be calculated once statically.
+ // TODO: PERF: It looks like hshift could be calculated once statically.
hshift = 0;
for (fcode = this.hsize; fcode < 65536; fcode *= 2)
{
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
index 8af5b81c2..5e26370ba 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
buffer[0] = GifConstants.ApplicationBlockSize;
// Write NETSCAPE2.0
- GifConstants.NetscapeApplicationIdentificationBytes.AsSpan().CopyTo(buffer.Slice(1, 11));
+ GifConstants.NetscapeApplicationIdentificationBytes.CopyTo(buffer.Slice(1, 11));
// Application Data ----
buffer[12] = 3; // Application block length (always 3)
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
index 54633a5d7..87b486ea6 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
@@ -1,8 +1,7 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -12,24 +11,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
internal static class ProfileResolver
{
///
- /// Describes the JFIF specific markers.
+ /// Gets the JFIF specific markers.
///
- public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0");
+ public static ReadOnlySpan JFifMarker => new[]
+ {
+ (byte)'J', (byte)'F', (byte)'I', (byte)'F', (byte)'\0'
+ };
///
- /// Describes the ICC specific markers.
+ /// Gets the ICC specific markers.
///
- public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0");
+ public static ReadOnlySpan IccMarker => new[]
+ {
+ (byte)'I', (byte)'C', (byte)'C', (byte)'_',
+ (byte)'P', (byte)'R', (byte)'O', (byte)'F',
+ (byte)'I', (byte)'L', (byte)'E', (byte)'\0'
+ };
///
- /// Describes the EXIF specific markers.
+ /// Gets the EXIF specific markers.
///
- public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0");
+ public static ReadOnlySpan ExifMarker => new[]
+ {
+ (byte)'E', (byte)'x', (byte)'i', (byte)'f', (byte)'\0', (byte)'\0'
+ };
///
- /// Describes Adobe specific markers .
+ /// Gets the Adobe specific markers .
///
- public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe");
+ public static ReadOnlySpan AdobeMarker => new[]
+ {
+ (byte)'A', (byte)'d', (byte)'o', (byte)'b', (byte)'e'
+ };
///
/// Returns a value indicating whether the passed bytes are a match to the profile identifier.
@@ -43,4 +56,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
index 059e2052b..669abad28 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
@@ -34,12 +34,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public fixed byte Data[Size];
///
- /// Unzig maps from the zigzag ordering to the natural ordering. For example,
- /// unzig[3] is the column and row of the fourth element in zigzag order. The
- /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
+ /// Gets the unzigs map, which maps from the zigzag ordering to the natural ordering.
+ /// For example, unzig[3] is the column and row of the fourth element in zigzag order.
+ /// The value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
///
- private static readonly byte[] Unzig =
- new byte[Size]
+ private static ReadOnlySpan Unzig => new byte[]
{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
@@ -75,8 +74,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public static ZigZag CreateUnzigTable()
{
ZigZag result = default;
- byte* unzigPtr = result.Data;
- Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, Size);
+ ref byte sourceRef = ref MemoryMarshal.GetReference(Unzig);
+ ref byte destinationRef = ref Unsafe.AsRef(result.Data);
+
+ Unzig.CopyTo(new Span(result.Data, Size));
+
return result;
}
diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs
index 632460ec4..247bb3c75 100644
--- a/src/ImageSharp/Formats/Png/PngConstants.cs
+++ b/src/ImageSharp/Formats/Png/PngConstants.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Collections.Generic;
using System.Text;
@@ -36,21 +37,6 @@ namespace SixLabors.ImageSharp.Formats.Png
///
public static readonly IEnumerable FileExtensions = new[] { "png" };
- ///
- /// The header bytes identifying a Png.
- ///
- public static readonly byte[] HeaderBytes =
- {
- 0x89, // Set the high bit.
- 0x50, // P
- 0x4E, // N
- 0x47, // G
- 0x0D, // Line ending CRLF
- 0x0A, // Line ending CRLF
- 0x1A, // EOF
- 0x0A // LF
- };
-
///
/// The header bytes as a big-endian coded ulong.
///
@@ -77,5 +63,20 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The minimum length of a keyword in a text chunk is 1 byte.
///
public const int MinTextKeywordLength = 1;
+
+ ///
+ /// Gets the header bytes identifying a Png.
+ ///
+ public static ReadOnlySpan HeaderBytes => new byte[]
+ {
+ 0x89, // Set the high bit.
+ 0x50, // P
+ 0x4E, // N
+ 0x47, // G
+ 0x0D, // Line ending CRLF
+ 0x0A, // Line ending CRLF
+ 0x1A, // EOF
+ 0x0A // LF
+ };
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index c62683e10..45e1ffd2d 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -146,10 +146,10 @@ namespace SixLabors.ImageSharp.Formats.Png
ImageMetadata metadata = image.Metadata;
PngMetadata pngMetadata = metadata.GetPngMetadata();
PngEncoderOptionsHelpers.AdjustOptions(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
- QuantizedFrame quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);
+ IndexedImageFrame quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);
this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized);
- stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
+ stream.Write(PngConstants.HeaderBytes);
this.WriteHeaderChunk(stream);
this.WritePaletteChunk(stream, quantized);
@@ -371,7 +371,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The row span.
/// The quantized pixels. Can be null.
/// The row.
- private void CollectPixelBytes(ReadOnlySpan rowSpan, QuantizedFrame quantized, int row)
+ private void CollectPixelBytes(ReadOnlySpan rowSpan, IndexedImageFrame quantized, int row)
where TPixel : unmanaged, IPixel
{
switch (this.options.ColorType)
@@ -380,12 +380,11 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth < 8)
{
- PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.GetRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth);
+ PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.GetPixelRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth);
}
else
{
- int stride = this.currentScanline.Length();
- quantized.GetPixelSpan().Slice(row * stride, stride).CopyTo(this.currentScanline.GetSpan());
+ quantized.GetPixelRowSpan(row).CopyTo(this.currentScanline.GetSpan());
}
break;
@@ -440,7 +439,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The quantized pixels. Can be null.
/// The row.
/// The
- private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, QuantizedFrame quantized, int row)
+ private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, IndexedImageFrame quantized, int row)
where TPixel : unmanaged, IPixel
{
this.CollectPixelBytes(rowSpan, quantized, row);
@@ -546,59 +545,54 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The containing image data.
/// The quantized frame.
- private void WritePaletteChunk(Stream stream, QuantizedFrame quantized)
+ private void WritePaletteChunk(Stream stream, IndexedImageFrame quantized)
where TPixel : unmanaged, IPixel
{
- if (quantized == null)
+ if (quantized is null)
{
return;
}
// Grab the palette and write it to the stream.
ReadOnlySpan palette = quantized.Palette.Span;
- int paletteLength = Math.Min(palette.Length, 256);
- int colorTableLength = paletteLength * 3;
- bool anyAlpha = false;
+ int paletteLength = palette.Length;
+ int colorTableLength = paletteLength * Unsafe.SizeOf();
+ bool hasAlpha = false;
- using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
- using (IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength))
- {
- ref byte colorTableRef = ref MemoryMarshal.GetReference(colorTable.GetSpan());
- ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
- ReadOnlySpan quantizedSpan = quantized.GetPixelSpan();
-
- Rgba32 rgba = default;
-
- for (int i = 0; i < paletteLength; i++)
- {
- if (quantizedSpan.IndexOf((byte)i) > -1)
- {
- int offset = i * 3;
- palette[i].ToRgba32(ref rgba);
+ using IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength);
+ using IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength);
- byte alpha = rgba.A;
+ ref Rgb24 colorTableRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(colorTable.GetSpan()));
+ ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
- Unsafe.Add(ref colorTableRef, offset) = rgba.R;
- Unsafe.Add(ref colorTableRef, offset + 1) = rgba.G;
- Unsafe.Add(ref colorTableRef, offset + 2) = rgba.B;
+ // Bulk convert our palette to RGBA to allow assignment to tables.
+ using IMemoryOwner rgbaOwner = quantized.Configuration.MemoryAllocator.Allocate(paletteLength);
+ Span rgbaPaletteSpan = rgbaOwner.GetSpan();
+ PixelOperations.Instance.ToRgba32(quantized.Configuration, quantized.Palette.Span, rgbaPaletteSpan);
+ ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPaletteSpan);
- if (alpha > this.options.Threshold)
- {
- alpha = byte.MaxValue;
- }
+ // Loop, assign, and extract alpha values from the palette.
+ for (int i = 0; i < paletteLength; i++)
+ {
+ Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, i);
+ byte alpha = rgba.A;
- anyAlpha = anyAlpha || alpha < byte.MaxValue;
- Unsafe.Add(ref alphaTableRef, i) = alpha;
- }
+ Unsafe.Add(ref colorTableRef, i) = rgba.Rgb;
+ if (alpha > this.options.Threshold)
+ {
+ alpha = byte.MaxValue;
}
- this.WriteChunk(stream, PngChunkType.Palette, colorTable.Array, 0, colorTableLength);
+ hasAlpha = hasAlpha || alpha < byte.MaxValue;
+ Unsafe.Add(ref alphaTableRef, i) = alpha;
+ }
- // Write the transparency data
- if (anyAlpha)
- {
- this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
- }
+ this.WriteChunk(stream, PngChunkType.Palette, colorTable.Array, 0, colorTableLength);
+
+ // Write the transparency data
+ if (hasAlpha)
+ {
+ this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
}
}
@@ -783,7 +777,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The image.
/// The quantized pixel data. Can be null.
/// The stream.
- private void WriteDataChunks(ImageFrame pixels, QuantizedFrame quantized, Stream stream)
+ private void WriteDataChunks(ImageFrame pixels, IndexedImageFrame quantized, Stream stream)
where TPixel : unmanaged, IPixel
{
byte[] buffer;
@@ -881,7 +875,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixels.
/// The quantized pixels span.
/// The deflate stream.
- private void EncodePixels(ImageFrame pixels, QuantizedFrame quantized, ZlibDeflateStream deflateStream)
+ private void EncodePixels(ImageFrame pixels, IndexedImageFrame quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel
{
int bytesPerScanline = this.CalculateScanlineLength(this.width);
@@ -960,7 +954,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The type of the pixel.
/// The quantized.
/// The deflate stream.
- private void EncodeAdam7IndexedPixels(QuantizedFrame quantized, ZlibDeflateStream deflateStream)
+ private void EncodeAdam7IndexedPixels(IndexedImageFrame quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel
{
int width = quantized.Width;
@@ -987,7 +981,7 @@ namespace SixLabors.ImageSharp.Formats.Png
row += Adam7.RowIncrement[pass])
{
// collect data
- ReadOnlySpan srcRow = quantized.GetRowSpan(row);
+ ReadOnlySpan srcRow = quantized.GetPixelRowSpan(row);
for (int col = startCol, i = 0;
col < width;
col += Adam7.ColumnIncrement[pass])
diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs b/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
index 20b8c41c9..3f490ca6f 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The type of the pixel.
/// The options.
/// The image.
- public static QuantizedFrame CreateQuantizedFrame(
+ public static IndexedImageFrame CreateQuantizedFrame(
PngEncoderOptions options,
Image image)
where TPixel : unmanaged, IPixel
@@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public static byte CalculateBitDepth(
PngEncoderOptions options,
Image image,
- QuantizedFrame quantizedFrame)
+ IndexedImageFrame quantizedFrame)
where TPixel : unmanaged, IPixel
{
byte bitDepth;
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
index e8dd8a520..543a1fe30 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
@@ -36,12 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private const int EofSymbol = 256;
- // The lengths of the bit length codes are sent in order of decreasing
- // probability, to avoid transmitting the lengths for unused bit length codes.
- private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
-
- private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
-
private static readonly short[] StaticLCodes;
private static readonly byte[] StaticLLength;
private static readonly short[] StaticDCodes;
@@ -128,6 +122,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
}
+ ///
+ /// Gets the lengths of the bit length codes are sent in order of decreasing probability, to avoid transmitting the lengths for unused bit length codes.
+ ///
+ private static ReadOnlySpan BitLengthOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ private static ReadOnlySpan Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+
///
/// Gets the pending buffer to use.
///
@@ -158,6 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.Pending.WriteBits(this.literalTree.NumCodes - 257, 5);
this.Pending.WriteBits(this.distTree.NumCodes - 1, 5);
this.Pending.WriteBits(blTreeCodes - 4, 4);
+
for (int rank = 0; rank < blTreeCodes; rank++)
{
this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3);
@@ -250,6 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.blTree.BuildTree();
int blTreeCodes = 4;
+
for (int i = 18; i > blTreeCodes; i--)
{
if (this.blTree.Length[BitLengthOrder[i]] > 0)
@@ -363,10 +366,30 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
[MethodImpl(InliningOptions.ShortMethod)]
public static short BitReverse(int toReverse)
{
- return (short)(Bit4Reverse[toReverse & 0xF] << 12
- | Bit4Reverse[(toReverse >> 4) & 0xF] << 8
- | Bit4Reverse[(toReverse >> 8) & 0xF] << 4
- | Bit4Reverse[toReverse >> 12]);
+ /* Use unsafe offsetting and manually validate the input index to reduce the
+ * total number of conditional branches. There are two main cases to test here:
+ * 1. In the first 3, the input value (or some combination of it) is combined
+ * with & 0xF, which results in a maximum value of 0xF no matter what the
+ * input value was. That is 15, which is always in range for the target span.
+ * As a result, no input validation is needed at all in this case.
+ * 2. There are two cases where the input value might cause an invalid access:
+ * when it is either negative, or greater than 15 << 12. We can test both
+ * conditions in a single pass by casting the input value to uint and right
+ * shifting it by 12, which also preserves the sign. If it is a negative
+ * value (2-complement), the test will fail as the uint cast will result
+ * in a much larger value. If the value was simply too high, the test will
+ * fail as expected. We can't simply check whether the value is lower than
+ * 15 << 12, because higher values are acceptable in the first 3 accesses.
+ * Doing this reduces the total number of index checks from 4 down to just 1. */
+ int toReverseRightShiftBy12 = toReverse >> 12;
+ Guard.MustBeLessThanOrEqualTo((uint)toReverseRightShiftBy12, 15, nameof(toReverse));
+
+ ref byte bit4ReverseRef = ref MemoryMarshal.GetReference(Bit4Reverse);
+
+ return (short)(Unsafe.Add(ref bit4ReverseRef, toReverse & 0xF) << 12
+ | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 4) & 0xF) << 8
+ | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 8) & 0xF) << 4
+ | Unsafe.Add(ref bit4ReverseRef, toReverseRightShiftBy12));
}
///
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 321f2938c..a35443ec9 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp
var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone());
var operation = new RowIntervalOperation(this, target, configuration);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRowIntervals(
configuration,
this.Bounds(),
in operation);
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index be0e9032b..baf4a2ce1 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -36,7 +36,7 @@
-
+
True
True
@@ -209,5 +209,4 @@
-
diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs
index 131abc5d2..20ee645fd 100644
--- a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs
+++ b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Memory
///
public override Span GetSpan()
{
- if (this.Data == null)
+ if (this.Data is null)
{
ThrowObjectDisposedException();
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
index 2649b7fb1..89aca914d 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
@@ -18,12 +18,12 @@ namespace SixLabors.ImageSharp.Memory
/// Gets the number of elements per contiguous sub-buffer preceding the last buffer.
/// The last buffer is allowed to be smaller.
///
- public int BufferLength { get; }
+ int BufferLength { get; }
///
/// Gets the aggregate number of elements in the group.
///
- public long TotalLength { get; }
+ long TotalLength { get; }
///
/// Gets a value indicating whether the group has been invalidated.
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
index c7112c47a..c58b224e4 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
@@ -1,11 +1,13 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
+
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal static class ExifConstants
{
- public static readonly byte[] LittleEndianByteOrderMarker =
+ public static ReadOnlySpan LittleEndianByteOrderMarker => new byte[]
{
(byte)'I',
(byte)'I',
@@ -13,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x00,
};
- public static readonly byte[] BigEndianByteOrderMarker =
+ public static ReadOnlySpan BigEndianByteOrderMarker => new byte[]
{
(byte)'M',
(byte)'M',
@@ -21,4 +23,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x2A
};
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index c068461b2..b00813730 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int i = 0;
// The byte order marker for little-endian, followed by the number 42 and a 0
- ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i));
+ ExifConstants.LittleEndianByteOrderMarker.CopyTo(result.AsSpan(start: i));
i += ExifConstants.LittleEndianByteOrderMarker.Length;
uint ifdOffset = ((uint)i - startIndex) + 4U;
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
index 94127432b..8184f1577 100644
--- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
@@ -13,7 +13,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
-
///
/// Returns the result of the "NormalSrc" compositing equation.
///
@@ -419,7 +418,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "MultiplySrc" compositing equation.
///
@@ -825,7 +823,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "AddSrc" compositing equation.
///
@@ -1231,7 +1228,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "SubtractSrc" compositing equation.
///
@@ -1637,7 +1633,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "ScreenSrc" compositing equation.
///
@@ -2043,7 +2038,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "DarkenSrc" compositing equation.
///
@@ -2449,7 +2443,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "LightenSrc" compositing equation.
///
@@ -2855,7 +2848,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "OverlaySrc" compositing equation.
///
@@ -3261,7 +3253,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
-
///
/// Returns the result of the "HardLightSrc" compositing equation.
///
diff --git a/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs
index 088f61884..f89540e24 100644
--- a/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing
public static class FilterExtensions
{
///
- /// Filters an image but the given color matrix
+ /// Filters an image by the given color matrix
///
/// The image this method extends.
/// The filter color matrix
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing
=> source.ApplyProcessor(new FilterProcessor(matrix));
///
- /// Filters an image but the given color matrix
+ /// Filters an image by the given color matrix
///
/// The image this method extends.
/// The filter color matrix
@@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle)
=> source.ApplyProcessor(new FilterProcessor(matrix), rectangle);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
index 18b347144..0d363689d 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
@@ -3,8 +3,8 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
bool isAlphaOnly = typeof(TPixel) == typeof(A8);
- var operation = new RowIntervalOperation(interest, source, upper, lower, threshold, isAlphaOnly);
+ var operation = new RowOperation(interest, source, upper, lower, threshold, isAlphaOnly);
ParallelRowIterator.IterateRows(
configuration,
interest,
@@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
///
/// A implementing the clone logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame source;
private readonly TPixel upper;
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
private readonly bool isAlphaOnly;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Rectangle bounds,
ImageFrame source,
TPixel upper,
@@ -84,22 +84,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
Rgba32 rgba = default;
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span row = this.source.GetPixelRowSpan(y);
+ Span row = this.source.GetPixelRowSpan(y);
+ ref TPixel rowRef = ref MemoryMarshal.GetReference(row);
- for (int x = this.minX; x < this.maxX; x++)
- {
- ref TPixel color = ref row[x];
- color.ToRgba32(ref rgba);
+ for (int x = this.minX; x < this.maxX; x++)
+ {
+ ref TPixel color = ref Unsafe.Add(ref rowRef, x);
+ color.ToRgba32(ref rgba);
- // Convert to grayscale using ITU-R Recommendation BT.709 if required
- byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
- color = luminance >= this.threshold ? this.upper : this.lower;
- }
+ // Convert to grayscale using ITU-R Recommendation BT.709 if required
+ byte luminance = this.isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
+ color = luminance >= this.threshold ? this.upper : this.lower;
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
index 17f60ebef..493218cde 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
@@ -72,8 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
protected override void OnFrameApply(ImageFrame source)
{
// Preliminary gamma highlight pass
- var gammaOperation = new ApplyGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
- ParallelRowIterator.IterateRows(
+ var gammaOperation = new ApplyGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, this.Configuration, this.gamma);
+ ParallelRowIterator.IterateRows(
this.Configuration,
this.SourceRectangle,
in gammaOperation);
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
float inverseGamma = 1 / this.gamma;
// Apply the inverse gamma exposure pass, and write the final pixel data
- var operation = new ApplyInverseGammaExposureRowIntervalOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma);
+ var operation = new ApplyInverseGammaExposureRowOperation(this.SourceRectangle, source.PixelBuffer, processingBuffer, this.Configuration, inverseGamma);
ParallelRowIterator.IterateRows(
this.Configuration,
this.SourceRectangle,
@@ -120,14 +120,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Vector4 parameters = Unsafe.Add(ref paramsRef, i);
// Compute the vertical 1D convolution
- var verticalOperation = new ApplyVerticalConvolutionRowIntervalOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel);
+ var verticalOperation = new ApplyVerticalConvolutionRowOperation(sourceRectangle, firstPassBuffer, source.PixelBuffer, kernel);
ParallelRowIterator.IterateRows(
configuration,
sourceRectangle,
in verticalOperation);
// Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer
- var horizontalOperation = new ApplyHorizontalConvolutionRowIntervalOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
+ var horizontalOperation = new ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
ParallelRowIterator.IterateRows(
configuration,
sourceRectangle,
@@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the vertical convolution logic for .
///
- private readonly struct ApplyVerticalConvolutionRowIntervalOperation : IRowIntervalOperation
+ private readonly struct ApplyVerticalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D targetValues;
@@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
- public ApplyVerticalConvolutionRowIntervalOperation(
+ public ApplyVerticalConvolutionRowOperation(
Rectangle bounds,
Buffer2D targetValues,
Buffer2D sourcePixels,
@@ -164,16 +164,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
+ Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
- for (int x = 0; x < this.bounds.Width; x++)
- {
- Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX);
- }
+ for (int x = 0; x < this.bounds.Width; x++)
+ {
+ Buffer2DUtils.Convolve4(this.kernel, this.sourcePixels, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX);
}
}
}
@@ -181,7 +178,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the horizontal convolution logic for .
///
- private readonly struct ApplyHorizontalConvolutionRowIntervalOperation : IRowIntervalOperation
+ private readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D targetValues;
@@ -193,7 +190,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
- public ApplyHorizontalConvolutionRowIntervalOperation(
+ public ApplyHorizontalConvolutionRowOperation(
Rectangle bounds,
Buffer2D targetValues,
Buffer2D sourceValues,
@@ -213,16 +210,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
+ Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
- for (int x = 0; x < this.bounds.Width; x++)
- {
- Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
- }
+ for (int x = 0; x < this.bounds.Width; x++)
+ {
+ Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
}
}
@@ -230,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the gamma exposure logic for .
///
- private readonly struct ApplyGammaExposureRowIntervalOperation : IRowIntervalOperation
+ private readonly struct ApplyGammaExposureRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D targetPixels;
@@ -238,7 +232,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float gamma;
[MethodImpl(InliningOptions.ShortMethod)]
- public ApplyGammaExposureRowIntervalOperation(
+ public ApplyGammaExposureRowOperation(
Rectangle bounds,
Buffer2D targetPixels,
Configuration configuration,
@@ -252,31 +246,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
- for (int y = rows.Min; y < rows.Max; y++)
+ Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply);
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);
+
+ for (int x = 0; x < this.bounds.Width; x++)
{
- Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply);
- ref Vector4 baseRef = ref MemoryMarshal.GetReference(span);
-
- for (int x = 0; x < this.bounds.Width; x++)
- {
- ref Vector4 v = ref Unsafe.Add(ref baseRef, x);
- v.X = MathF.Pow(v.X, this.gamma);
- v.Y = MathF.Pow(v.Y, this.gamma);
- v.Z = MathF.Pow(v.Z, this.gamma);
- }
-
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, x);
+ v.X = MathF.Pow(v.X, this.gamma);
+ v.Y = MathF.Pow(v.Y, this.gamma);
+ v.Z = MathF.Pow(v.Z, this.gamma);
}
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
///
/// A implementing the inverse gamma exposure logic for .
///
- private readonly struct ApplyInverseGammaExposureRowIntervalOperation : IRowIntervalOperation
+ private readonly struct ApplyInverseGammaExposureRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D targetPixels;
@@ -285,7 +276,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly float inverseGamma;
[MethodImpl(InliningOptions.ShortMethod)]
- public ApplyInverseGammaExposureRowIntervalOperation(
+ public ApplyInverseGammaExposureRowOperation(
Rectangle bounds,
Buffer2D targetPixels,
Buffer2D sourceValues,
@@ -301,28 +292,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
Vector4 low = Vector4.Zero;
var high = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
- for (int y = rows.Min; y < rows.Max; y++)
+ Span targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ Span sourceRowSpan = this.sourceValues.GetRowSpan(y).Slice(this.bounds.X);
+ ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
+
+ for (int x = 0; x < this.bounds.Width; x++)
{
- Span targetPixelSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
- Span sourceRowSpan = this.sourceValues.GetRowSpan(y).Slice(this.bounds.X);
- ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceRowSpan);
-
- for (int x = 0; x < this.bounds.Width; x++)
- {
- ref Vector4 v = ref Unsafe.Add(ref sourceRef, x);
- var clamp = Vector4.Clamp(v, low, high);
- v.X = MathF.Pow(clamp.X, this.inverseGamma);
- v.Y = MathF.Pow(clamp.Y, this.inverseGamma);
- v.Z = MathF.Pow(clamp.Z, this.inverseGamma);
- }
-
- PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply);
+ ref Vector4 v = ref Unsafe.Add(ref sourceRef, x);
+ var clamp = Vector4.Clamp(v, low, high);
+ v.X = MathF.Pow(clamp.X, this.inverseGamma);
+ v.Y = MathF.Pow(clamp.Y, this.inverseGamma);
+ v.Z = MathF.Pow(clamp.Z, this.inverseGamma);
}
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, sourceRowSpan.Slice(0, this.bounds.Width), targetPixelSpan, PixelConversionModifiers.Premultiply);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
index b4902fbd7..f7439879e 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
@@ -65,9 +65,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha);
+ var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelY, this.KernelX, this.Configuration, this.PreserveAlpha);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in operation);
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly int maxY;
@@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Rectangle bounds,
Buffer2D targetPixels,
Buffer2D sourcePixels,
@@ -113,52 +113,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
+ Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
- for (int y = rows.Min; y < rows.Max; y++)
+ if (this.preserveAlpha)
{
- Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
-
- if (this.preserveAlpha)
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve2D3(
- in this.kernelY,
- in this.kernelX,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- this.maxY,
- this.bounds.X,
- this.maxX);
- }
+ DenseMatrixUtils.Convolve2D3(
+ in this.kernelY,
+ in this.kernelX,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ this.maxY,
+ this.bounds.X,
+ this.maxX);
}
- else
+ }
+ else
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve2D4(
- in this.kernelY,
- in this.kernelX,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- this.maxY,
- this.bounds.X,
- this.maxX);
- }
+ DenseMatrixUtils.Convolve2D4(
+ in this.kernelY,
+ in this.kernelX,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ this.maxY,
+ this.bounds.X,
+ this.maxX);
}
-
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index 30f2c6b4e..4bbb15cba 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -64,15 +64,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
// Horizontal convolution
- var horizontalOperation = new RowIntervalOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha);
- ParallelRowIterator.IterateRows(
+ var horizontalOperation = new RowOperation(interest, firstPassPixels, source.PixelBuffer, this.KernelX, this.Configuration, this.PreserveAlpha);
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in horizontalOperation);
// Vertical convolution
- var verticalOperation = new RowIntervalOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha);
- ParallelRowIterator.IterateRows(
+ var verticalOperation = new RowOperation(interest, source.PixelBuffer, firstPassPixels, this.KernelY, this.Configuration, this.PreserveAlpha);
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in verticalOperation);
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D targetPixels;
@@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Rectangle bounds,
Buffer2D targetPixels,
Buffer2D sourcePixels,
@@ -109,53 +109,50 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
int maxY = this.bounds.Bottom - 1;
int maxX = this.bounds.Right - 1;
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
+ Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
- if (this.preserveAlpha)
+ if (this.preserveAlpha)
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve3(
- in this.kernel,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- maxY,
- this.bounds.X,
- maxX);
- }
+ DenseMatrixUtils.Convolve3(
+ in this.kernel,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ maxY,
+ this.bounds.X,
+ maxX);
}
- else
+ }
+ else
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve4(
- in this.kernel,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- maxY,
- this.bounds.X,
- maxX);
- }
+ DenseMatrixUtils.Convolve4(
+ in this.kernel,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ maxY,
+ this.bounds.X,
+ maxX);
}
-
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
index b6f9bb319..a2c8fc1fb 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
@@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
source.CopyTo(targetPixels);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowIntervalOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha);
- ParallelRowIterator.IterateRows(
+ var operation = new RowOperation(interest, targetPixels, source.PixelBuffer, this.KernelXY, this.Configuration, this.PreserveAlpha);
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in operation);
@@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly int maxY;
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly bool preserveAlpha;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Rectangle bounds,
Buffer2D targetPixels,
Buffer2D sourcePixels,
@@ -100,50 +100,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
ref Vector4 spanRef = ref MemoryMarshal.GetReference(span);
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
- PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
+ Span targetRowSpan = this.targetPixels.GetRowSpan(y).Slice(this.bounds.X);
+ PixelOperations.Instance.ToVector4(this.configuration, targetRowSpan.Slice(0, span.Length), span);
- if (this.preserveAlpha)
+ if (this.preserveAlpha)
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve3(
- in this.kernel,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- this.maxY,
- this.bounds.X,
- this.maxX);
- }
+ DenseMatrixUtils.Convolve3(
+ in this.kernel,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ this.maxY,
+ this.bounds.X,
+ this.maxX);
}
- else
+ }
+ else
+ {
+ for (int x = 0; x < this.bounds.Width; x++)
{
- for (int x = 0; x < this.bounds.Width; x++)
- {
- DenseMatrixUtils.Convolve4(
- in this.kernel,
- this.sourcePixels,
- ref spanRef,
- y,
- x,
- this.bounds.Y,
- this.maxY,
- this.bounds.X,
- this.maxX);
- }
+ DenseMatrixUtils.Convolve4(
+ in this.kernel,
+ this.sourcePixels,
+ ref spanRef,
+ y,
+ x,
+ this.bounds.Y,
+ this.maxY,
+ this.bounds.X,
+ this.maxX);
}
-
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
+
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, targetRowSpan);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
index f93fe20b1..159c67b5c 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
processor.Apply(pass);
}
- var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, interest);
+ var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Buffer2D targetPixels;
private readonly Buffer2D passPixels;
@@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Buffer2D targetPixels,
Buffer2D passPixels,
Rectangle bounds)
@@ -110,23 +110,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y));
- ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y));
+ ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.GetRowSpan(y));
+ ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.GetRowSpan(y));
- for (int x = this.minX; x < this.maxX; x++)
- {
- // Grab the max components of the two pixels
- ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x);
- ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x);
+ for (int x = this.minX; x < this.maxX; x++)
+ {
+ // Grab the max components of the two pixels
+ ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x);
+ ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, x);
- var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4());
+ var pixelValue = Vector4.Max(currentPassPixel.ToVector4(), currentTargetPixel.ToVector4());
- currentTargetPixel.FromVector4(pixelValue);
- }
+ currentTargetPixel.FromVector4(pixelValue);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
index 7b8e83585..7d30bada6 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
@@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -89,29 +90,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyQuantizationDither(
ref TFrameQuantizer quantizer,
- ReadOnlyMemory palette,
ImageFrame source,
- Memory output,
+ IndexedImageFrame destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer
where TPixel : unmanaged, IPixel
{
- Span outputSpan = output.Span;
- ReadOnlySpan paletteSpan = palette.Span;
- int width = bounds.Width;
int offsetY = bounds.Top;
int offsetX = bounds.Left;
float scale = quantizer.Options.DitherScale;
for (int y = bounds.Top; y < bounds.Bottom; y++)
{
- Span row = source.GetPixelRowSpan(y);
- int rowStart = (y - offsetY) * width;
+ ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));
+ ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY));
for (int x = bounds.Left; x < bounds.Right; x++)
{
- TPixel sourcePixel = row[x];
- outputSpan[rowStart + x - offsetX] = quantizer.GetQuantizedColor(sourcePixel, paletteSpan, out TPixel transformed);
+ TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, x);
+ Unsafe.Add(ref destinationRowRef, x - offsetX) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed);
this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
}
}
@@ -119,25 +116,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyPaletteDither(
- Configuration configuration,
- ReadOnlyMemory palette,
+ public void ApplyPaletteDither(
+ in TPaletteDitherImageProcessor processor,
ImageFrame source,
- Rectangle bounds,
- float scale)
+ Rectangle bounds)
+ where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor
where TPixel : unmanaged, IPixel
{
- var pixelMap = new EuclideanPixelMap(palette);
-
+ float scale = processor.DitherScale;
for (int y = bounds.Top; y < bounds.Bottom; y++)
{
- Span row = source.GetPixelRowSpan(y);
+ ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));
for (int x = bounds.Left; x < bounds.Right; x++)
{
- TPixel sourcePixel = row[x];
- pixelMap.GetClosestColor(sourcePixel, out TPixel transformed);
+ ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x);
+ TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel);
this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
- row[x] = transformed;
+ sourcePixel = transformed;
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
index 3d6edc9fa..8f9d82537 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/IDither.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -19,15 +18,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// The type of frame quantizer.
/// The pixel format.
/// The frame quantizer.
- /// The quantized palette.
/// The source image.
- /// The output target
+ /// The destination quantized frame.
/// The region of interest bounds.
void ApplyQuantizationDither(
ref TFrameQuantizer quantizer,
- ReadOnlyMemory palette,
ImageFrame source,
- Memory output,
+ IndexedImageFrame destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer
where TPixel : unmanaged, IPixel;
@@ -36,18 +33,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// Transforms the image frame applying a dither matrix.
/// This method should be treated as destructive, altering the input pixels.
///
+ /// The type of palette dithering processor.
/// The pixel format.
- /// The configuration.
- /// The quantized palette.
+ /// The palette dithering processor.
/// The source image.
/// The region of interest bounds.
- /// The dithering scale used to adjust the amount of dither. Range 0..1.
- void ApplyPaletteDither(
- Configuration configuration,
- ReadOnlyMemory palette,
+ void ApplyPaletteDither(
+ in TPaletteDitherImageProcessor processor,
ImageFrame source,
- Rectangle bounds,
- float scale)
+ Rectangle bounds)
+ where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor
where TPixel : unmanaged, IPixel;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
new file mode 100644
index 000000000..a8e08fa3f
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Dithering
+{
+ ///
+ /// Implements an algorithm to alter the pixels of an image via palette dithering.
+ ///
+ /// The pixel format.
+ public interface IPaletteDitherImageProcessor
+ where TPixel : unmanaged, IPixel
+ {
+ ///
+ /// Gets the configuration instance to use when performing operations.
+ ///
+ Configuration Configuration { get; }
+
+ ///
+ /// Gets the dithering palette.
+ ///
+ ReadOnlyMemory Palette { get; }
+
+ ///
+ /// Gets the dithering scale used to adjust the amount of dither. Range 0..1.
+ ///
+ 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);
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs
index d3e710782..f6026a64f 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
///
/// An ordered dithering matrix with equal sides of arbitrary length
///
- public readonly partial struct OrderedDither : IDither
+ public readonly partial struct OrderedDither
{
///
/// Applies order dithering using the 2x2 Bayer dithering matrix.
diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
index 7803f92b4..6862cff00 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
@@ -3,8 +3,8 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -105,21 +105,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyQuantizationDither(
ref TFrameQuantizer quantizer,
- ReadOnlyMemory palette,
ImageFrame source,
- Memory output,
+ IndexedImageFrame destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer
where TPixel : unmanaged, IPixel
{
- var ditherOperation = new QuantizeDitherRowIntervalOperation(
+ var ditherOperation = new QuantizeDitherRowOperation(
ref quantizer,
in Unsafe.AsRef(this),
source,
- output,
- bounds,
- palette,
- ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
+ destination,
+ bounds);
ParallelRowIterator.IterateRows(
quantizer.Configuration,
@@ -129,24 +126,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void ApplyPaletteDither(
- Configuration configuration,
- ReadOnlyMemory palette,
+ public void ApplyPaletteDither(
+ in TPaletteDitherImageProcessor processor,
ImageFrame source,
- Rectangle bounds,
- float scale)
+ Rectangle bounds)
+ where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor
where TPixel : unmanaged, IPixel
{
- var ditherOperation = new PaletteDitherRowIntervalOperation(
+ var ditherOperation = new PaletteDitherRowOperation(
+ in processor,
in Unsafe.AsRef(this),
source,
- bounds,
- palette,
- scale,
- ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
+ bounds);
ParallelRowIterator.IterateRows(
- configuration,
+ processor.Configuration,
bounds,
in ditherOperation);
}
@@ -200,102 +194,87 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public override int GetHashCode()
=> HashCode.Combine(this.thresholdMatrix, this.modulusX, this.modulusY);
- private readonly struct QuantizeDitherRowIntervalOperation : IRowIntervalOperation
+ private readonly struct QuantizeDitherRowOperation : IRowOperation
where TFrameQuantizer : struct, IFrameQuantizer
where TPixel : unmanaged, IPixel
{
private readonly TFrameQuantizer quantizer;
private readonly OrderedDither dither;
private readonly ImageFrame source;
- private readonly Memory output;
+ private readonly IndexedImageFrame destination;
private readonly Rectangle bounds;
- private readonly ReadOnlyMemory palette;
private readonly int bitDepth;
[MethodImpl(InliningOptions.ShortMethod)]
- public QuantizeDitherRowIntervalOperation(
+ public QuantizeDitherRowOperation(
ref TFrameQuantizer quantizer,
in OrderedDither dither,
ImageFrame source,
- Memory output,
- Rectangle bounds,
- ReadOnlyMemory palette,
- int bitDepth)
+ IndexedImageFrame destination,
+ Rectangle bounds)
{
this.quantizer = quantizer;
this.dither = dither;
this.source = source;
- this.output = output;
+ this.destination = destination;
this.bounds = bounds;
- this.palette = palette;
- this.bitDepth = bitDepth;
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(destination.Palette.Length);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- ReadOnlySpan paletteSpan = this.palette.Span;
- Span outputSpan = this.output.Span;
- int width = this.bounds.Width;
int offsetY = this.bounds.Top;
int offsetX = this.bounds.Left;
float scale = this.quantizer.Options.DitherScale;
- for (int y = rows.Min; y < rows.Max; y++)
+ ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
+ ref byte destinationRowRef = ref MemoryMarshal.GetReference(this.destination.GetWritablePixelRowSpanUnsafe(y - offsetY));
+
+ for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
- Span row = this.source.GetPixelRowSpan(y);
- int rowStart = (y - offsetY) * width;
-
- // TODO: This can be a bulk operation.
- for (int x = this.bounds.Left; x < this.bounds.Right; x++)
- {
- TPixel dithered = this.dither.Dither(row[x], x, y, this.bitDepth, scale);
- outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(dithered, paletteSpan, out TPixel _);
- }
+ TPixel dithered = this.dither.Dither(Unsafe.Add(ref sourceRowRef, x), x, y, this.bitDepth, scale);
+ Unsafe.Add(ref destinationRowRef, x - offsetX) = Unsafe.AsRef(this.quantizer).GetQuantizedColor(dithered, out TPixel _);
}
}
}
- private readonly struct PaletteDitherRowIntervalOperation : IRowIntervalOperation
+ private readonly struct PaletteDitherRowOperation : IRowOperation
+ where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor
where TPixel : unmanaged, IPixel
{
+ private readonly TPaletteDitherImageProcessor processor;
private readonly OrderedDither dither;
private readonly ImageFrame source;
private readonly Rectangle bounds;
- private readonly EuclideanPixelMap pixelMap;
private readonly float scale;
private readonly int bitDepth;
[MethodImpl(InliningOptions.ShortMethod)]
- public PaletteDitherRowIntervalOperation(
+ public PaletteDitherRowOperation(
+ in TPaletteDitherImageProcessor processor,
in OrderedDither dither,
ImageFrame source,
- Rectangle bounds,
- ReadOnlyMemory palette,
- float scale,
- int bitDepth)
+ Rectangle bounds)
{
+ this.processor = processor;
this.dither = dither;
this.source = source;
this.bounds = bounds;
- this.pixelMap = new EuclideanPixelMap(palette);
- this.scale = scale;
- this.bitDepth = bitDepth;
+ this.scale = processor.DitherScale;
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Span.Length);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
+ ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
+
+ for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
- Span row = this.source.GetPixelRowSpan(y);
-
- for (int x = this.bounds.Left; x < this.bounds.Right; x++)
- {
- TPixel dithered = this.dither.Dither(row[x], x, y, this.bitDepth, this.scale);
- this.pixelMap.GetClosestColor(dithered, out TPixel transformed);
- row[x] = transformed;
- }
+ ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x);
+ TPixel dithered = this.dither.Dither(sourcePixel, x, y, this.bitDepth, this.scale);
+ sourcePixel = Unsafe.AsRef(this.processor).GetPaletteColor(dithered);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
index 254847f45..e0dd4eae1 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
@@ -3,7 +3,9 @@
using System;
using System.Buffers;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
@@ -14,11 +16,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
internal sealed class PaletteDitherProcessor : ImageProcessor
where TPixel : unmanaged, IPixel
{
- private readonly int paletteLength;
+ private readonly DitherProcessor ditherProcessor;
private readonly IDither dither;
- private readonly float ditherScale;
- private readonly ReadOnlyMemory sourcePalette;
- private IMemoryOwner palette;
+ private IMemoryOwner paletteOwner;
private bool isDisposed;
///
@@ -31,37 +31,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public PaletteDitherProcessor(Configuration configuration, PaletteDitherProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
- this.paletteLength = definition.Palette.Span.Length;
this.dither = definition.Dither;
- this.ditherScale = definition.DitherScale;
- this.sourcePalette = definition.Palette;
- }
- ///
- protected override void OnFrameApply(ImageFrame source)
- {
- var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ ReadOnlySpan sourcePalette = definition.Palette.Span;
+ this.paletteOwner = this.Configuration.MemoryAllocator.Allocate(sourcePalette.Length);
+ Color.ToPixel(this.Configuration, sourcePalette, this.paletteOwner.Memory.Span);
- this.dither.ApplyPaletteDither(
+ this.ditherProcessor = new DitherProcessor(
this.Configuration,
- this.palette.Memory,
- source,
- interest,
- this.ditherScale);
+ this.paletteOwner.Memory,
+ definition.DitherScale);
}
///
- protected override void BeforeFrameApply(ImageFrame source)
+ protected override void OnFrameApply(ImageFrame source)
{
- // Lazy init palettes:
- if (this.palette is null)
- {
- this.palette = this.Configuration.MemoryAllocator.Allocate(this.paletteLength);
- ReadOnlySpan sourcePalette = this.sourcePalette.Span;
- Color.ToPixel(this.Configuration, sourcePalette, this.palette.Memory.Span);
- }
-
- base.BeforeFrameApply(source);
+ var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
+ this.dither.ApplyPaletteDither(in this.ditherProcessor, source, interest);
}
///
@@ -72,15 +58,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
return;
}
+ this.isDisposed = true;
if (disposing)
{
- this.palette?.Dispose();
+ this.paletteOwner.Dispose();
}
- this.palette = null;
-
- this.isDisposed = true;
+ this.paletteOwner = null;
base.Dispose(disposing);
}
+
+ ///
+ /// Used to allow inlining of calls to
+ /// .
+ ///
+ private readonly struct DitherProcessor : IPaletteDitherImageProcessor
+ {
+ private readonly EuclideanPixelMap pixelMap;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public DitherProcessor(
+ Configuration configuration,
+ ReadOnlyMemory palette,
+ float ditherScale)
+ {
+ this.Configuration = configuration;
+ this.pixelMap = new EuclideanPixelMap(configuration, palette);
+ this.Palette = palette;
+ this.DitherScale = ditherScale;
+ }
+
+ public Configuration Configuration { get; }
+
+ public ReadOnlyMemory Palette { get; }
+
+ public float DitherScale { get; }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public TPixel GetPaletteColor(TPixel color)
+ {
+ this.pixelMap.GetClosestColor(color, out TPixel match);
+ return match;
+ }
+ }
}
}
diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
index ae938aec1..fca896929 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
@@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
"Cannot draw image because the source image does not overlap the target image.");
}
- var operation = new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity);
+ var operation = new RowOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity);
ParallelRowIterator.IterateRows(
configuration,
workingRect,
@@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
///
/// A implementing the draw logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly ImageFrame sourceFrame;
private readonly Image targetImage;
@@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
ImageFrame sourceFrame,
Image targetImage,
PixelBlender blender,
@@ -146,14 +146,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width);
- Span foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width);
- this.blender.Blend(this.configuration, background, background, foreground, this.opacity);
- }
+ Span background = this.sourceFrame.GetPixelRowSpan(y).Slice(this.minX, this.width);
+ Span foreground = this.targetImage.GetPixelRowSpan(y - this.locationY).Slice(this.targetX, this.width);
+ this.blender.Blend(this.configuration, background, background, foreground, this.opacity);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
index 270eb1343..5ee1e40de 100644
--- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
@@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
source.CopyTo(targetPixels);
var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRowIntervals(
this.Configuration,
this.SourceRectangle,
in operation);
diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
index 1907228c9..71259a618 100644
--- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs
@@ -50,9 +50,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
protected override void OnFrameApply(ImageFrame source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowIntervalOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate);
+ var operation = new RowOperation(interest.X, source, this.Configuration, this.modifiers, this.rowDelegate);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in operation);
@@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly int startX;
private readonly ImageFrame source;
@@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private readonly TDelegate rowProcessor;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
int startX,
ImageFrame source,
Configuration configuration,
@@ -86,18 +86,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
- PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
+ Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
+ PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span, this.modifiers);
- // Run the user defined pixel shader to the current row of pixels
- Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y));
+ // Run the user defined pixel shader to the current row of pixels
+ Unsafe.AsRef(this.rowProcessor).Invoke(span, new Point(this.startX, y));
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers);
- }
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan, this.modifiers);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
index 545c53915..7da4eb1b1 100644
--- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
@@ -36,9 +36,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
protected override void OnFrameApply(ImageFrame source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
- var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration);
+ var operation = new RowOperation(interest.X, source, this.definition.Matrix, this.Configuration);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRows(
this.Configuration,
interest,
in operation);
@@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
///
/// A implementing the convolution logic for .
///
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly int startX;
private readonly ImageFrame source;
@@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
private readonly Configuration configuration;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
int startX,
ImageFrame source,
ColorMatrix matrix,
@@ -69,17 +69,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows, Span span)
+ public void Invoke(int y, Span span)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
- PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span);
+ Span rowSpan = this.source.GetPixelRowSpan(y).Slice(this.startX, span.Length);
+ PixelOperations.Instance.ToVector4(this.configuration, rowSpan, span);
- Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix));
+ Vector4Utils.Transform(span, ref Unsafe.AsRef(this.matrix));
- PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan);
- }
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, span, rowSpan);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
index 5fdedad6e..1547de8ac 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRowIntervals(
this.Configuration,
new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
in operation);
@@ -522,7 +522,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
this.luminanceLevels,
source);
- ParallelRowIterator.IterateRows(
+ ParallelRowIterator.IterateRowIntervals(
this.configuration,
new Rectangle(0, 0, this.sourceWidth, this.tileYStartPositions.Count),
in operation);
diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
index a99fb405e..209135deb 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels
- var grayscaleOperation = new GrayscaleLevelsRowIntervalOperation(interest, histogramBuffer, source, this.LuminanceLevels);
+ var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source, this.LuminanceLevels);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
- var cdfOperation = new CdfApplicationRowIntervalOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
+ var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
this.Configuration,
interest,
@@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
/// A implementing the grayscale levels logic for .
///
- private readonly struct GrayscaleLevelsRowIntervalOperation : IRowIntervalOperation
+ private readonly struct GrayscaleLevelsRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly IMemoryOwner histogramBuffer;
@@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly int luminanceLevels;
[MethodImpl(InliningOptions.ShortMethod)]
- public GrayscaleLevelsRowIntervalOperation(
+ public GrayscaleLevelsRowOperation(
Rectangle bounds,
IMemoryOwner histogramBuffer,
ImageFrame source,
@@ -107,18 +107,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
- for (int y = rows.Min; y < rows.Max; y++)
- {
- ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
+ ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
- for (int x = 0; x < this.bounds.Width; x++)
- {
- int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
- Unsafe.Add(ref histogramBase, luminance)++;
- }
+ for (int x = 0; x < this.bounds.Width; x++)
+ {
+ int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
+ Unsafe.Add(ref histogramBase, luminance)++;
}
}
}
@@ -126,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
/// A implementing the cdf application levels logic for .
///
- private readonly struct CdfApplicationRowIntervalOperation : IRowIntervalOperation
+ private readonly struct CdfApplicationRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly IMemoryOwner cdfBuffer;
@@ -135,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly float numberOfPixelsMinusCdfMin;
[MethodImpl(InliningOptions.ShortMethod)]
- public CdfApplicationRowIntervalOperation(
+ public CdfApplicationRowOperation(
Rectangle bounds,
IMemoryOwner cdfBuffer,
ImageFrame source,
@@ -151,20 +148,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
- for (int y = rows.Min; y < rows.Max; y++)
+ ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
+
+ for (int x = 0; x < this.bounds.Width; x++)
{
- ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
-
- for (int x = 0; x < this.bounds.Width; x++)
- {
- ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
- int luminance = GetLuminance(pixel, this.luminanceLevels);
- float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin;
- pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
- }
+ ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
+ int luminance = GetLuminance(pixel, this.luminanceLevels);
+ float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin;
+ pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
index a1e9d1c4e..727e72469 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
@@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
PixelBlender blender = PixelOperations.Instance.GetPixelBlender(graphicsOptions);
- var operation = new RowIntervalOperation(configuration, interest, blender, amount, colors, source);
+ var operation = new RowOperation(configuration, interest, blender, amount, colors, source);
ParallelRowIterator.IterateRows(
configuration,
interest,
in operation);
}
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
@@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame source;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Configuration configuration,
Rectangle bounds,
PixelBlender blender,
@@ -83,23 +83,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
[MethodImpl(InliningOptions.ShortMethod)]
- public void Invoke(in RowInterval rows)
+ public void Invoke(int y)
{
- for (int y = rows.Min; y < rows.Max; y++)
- {
- Span destination =
- this.source.GetPixelRowSpan(y)
- .Slice(this.bounds.X, this.bounds.Width);
+ Span destination =
+ this.source.GetPixelRowSpan(y)
+ .Slice(this.bounds.X, this.bounds.Width);
- // Switch color & destination in the 2nd and 3rd places because we are
- // applying the target color under the current one.
- this.blender.Blend(
- this.configuration,
- destination,
- this.colors.GetSpan(),
- destination,
- this.amount.GetSpan());
- }
+ // Switch color & destination in the 2nd and 3rd places because we are
+ // applying the target color under the current one.
+ this.blender.Blend(
+ this.configuration,
+ destination,
+ this.colors.GetSpan(),
+ destination,
+ this.amount.GetSpan());
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
index 33c4123e9..fbecbc37c 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
@@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
using IMemoryOwner rowColors = allocator.Allocate(interest.Width);
rowColors.GetSpan().Fill(glowColor);
- var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
- ParallelRowIterator.IterateRows(
+ var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);
+ ParallelRowIterator.IterateRows(
configuration,
interest,
in operation);
}
- private readonly struct RowIntervalOperation : IRowIntervalOperation
+ private readonly struct RowOperation : IRowOperation
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
@@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly ImageFrame source;
[MethodImpl(InliningOptions.ShortMethod)]
- public RowIntervalOperation(
+ public RowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner