Browse Source

Merge branch 'master' into feature/tga

af/merge-core
Brian Popow 7 years ago
committed by GitHub
parent
commit
e4db71a918
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Directory.Build.targets
  2. 2
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  3. 9
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
  4. 38
      src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs
  5. 35
      src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs
  6. 6
      src/ImageSharp/Color/Color.NamedColors.cs
  7. 6
      src/ImageSharp/Color/Color.cs
  8. 2
      src/ImageSharp/Configuration.cs
  9. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
  10. 26
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  11. 20
      src/ImageSharp/Formats/Jpeg/JpegConstants.cs
  12. 2
      src/ImageSharp/ImageFrameCollection{TPixel}.cs
  13. 6
      src/ImageSharp/ImageFrame{TPixel}.cs
  14. 132
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  15. 22
      src/ImageSharp/Memory/Buffer2D{T}.cs
  16. 43
      src/ImageSharp/Memory/RowInterval.cs
  17. 6
      src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs
  18. 648
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  19. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  20. 1659
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  21. 100
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  22. 150
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  23. 131
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  24. 8
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  25. 2
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  26. 2
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  27. 2
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  28. 3
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  29. 3
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  30. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  31. 2
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  32. 2
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  33. 2
      src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs
  34. 2
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  35. 2
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
  36. 2
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  37. 2
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  38. 2
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  39. 8
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  40. 2
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
  41. 2
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  42. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
  43. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  44. 14
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
  45. 2
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
  46. 24
      tests/ImageSharp.Tests/ConfigurationTests.cs
  47. 40
      tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs
  48. 4
      tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs
  49. 2
      tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs
  50. 22
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  51. 2
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  52. 2
      tests/Images/External

2
Directory.Build.targets

@ -25,7 +25,7 @@
<PackageReference Update="BenchmarkDotNet" Version="0.11.5" /> <PackageReference Update="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Update="Colourful" Version="2.0.2" /> <PackageReference Update="Colourful" Version="2.0.2" />
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="7.12.0" /> <PackageReference Update="Magick.NET-Q16-AnyCPU" Version="7.12.0" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.1.0" /> <PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.3.1" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Update="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Update="Moq" Version="4.10.0" /> <PackageReference Update="Moq" Version="4.10.0" />
<PackageReference Update="SixLabors.Core" Version="1.0.0-beta0008" /> <PackageReference Update="SixLabors.Core" Version="1.0.0-beta0008" />

2
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -4,7 +4,7 @@
using System; using System;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

9
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs

@ -5,8 +5,8 @@ using System;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
IBrush brush = this.definition.Brush; IBrush brush = this.definition.Brush;
GraphicsOptions options = this.definition.Options; GraphicsOptions options = this.definition.Options;
@ -53,9 +53,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
// If there's no reason for blending, then avoid it. // If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
{ {
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration)
.MultiplyMinimumPixelsPerTask(4);
TPixel colorPixel = solidBrush.Color.ToPixel<TPixel>(); var colorPixel = solidBrush.Color.ToPixel<TPixel>();
ParallelHelper.IterateRows( ParallelHelper.IterateRows(
workingRect, workingRect,

38
src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs → src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs

@ -1,16 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.ParallelUtils namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{ {
/// <summary> /// <summary>
/// Defines execution settings for methods in <see cref="ParallelHelper"/>. /// Defines execution settings for methods in <see cref="ParallelHelper"/>.
/// </summary> /// </summary>
internal readonly struct ParallelExecutionSettings public readonly struct ParallelExecutionSettings
{ {
/// <summary> /// <summary>
/// Default value for <see cref="MinimumPixelsProcessedPerTask"/>. /// Default value for <see cref="MinimumPixelsProcessedPerTask"/>.
@ -20,11 +21,24 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct. /// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct.
/// </summary> /// </summary>
/// <param name="maxDegreeOfParallelism">The value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.</param>
/// <param name="minimumPixelsProcessedPerTask">The value for <see cref="MinimumPixelsProcessedPerTask"/>.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/>.</param>
public ParallelExecutionSettings( public ParallelExecutionSettings(
int maxDegreeOfParallelism, int maxDegreeOfParallelism,
int minimumPixelsProcessedPerTask, int minimumPixelsProcessedPerTask,
MemoryAllocator memoryAllocator) MemoryAllocator memoryAllocator)
{ {
// Shall be compatible with ParallelOptions.MaxDegreeOfParallelism:
// https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions.maxdegreeofparallelism
if (maxDegreeOfParallelism == 0 || maxDegreeOfParallelism < -1)
{
throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism));
}
Guard.MustBeGreaterThan(minimumPixelsProcessedPerTask, 0, nameof(minimumPixelsProcessedPerTask));
Guard.NotNull(memoryAllocator, nameof(memoryAllocator));
this.MaxDegreeOfParallelism = maxDegreeOfParallelism; this.MaxDegreeOfParallelism = maxDegreeOfParallelism;
this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask;
this.MemoryAllocator = memoryAllocator; this.MemoryAllocator = memoryAllocator;
@ -33,13 +47,15 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct. /// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct.
/// </summary> /// </summary>
/// <param name="maxDegreeOfParallelism">The value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/>.</param>
public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator)
: this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator) : this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator)
{ {
} }
/// <summary> /// <summary>
/// Gets the MemoryAllocator /// Gets the <see cref="MemoryAllocator"/>.
/// </summary> /// </summary>
public MemoryAllocator MemoryAllocator { get; } public MemoryAllocator MemoryAllocator { get; }
@ -60,12 +76,26 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// Creates a new instance of <see cref="ParallelExecutionSettings"/> /// Creates a new instance of <see cref="ParallelExecutionSettings"/>
/// having <see cref="MinimumPixelsProcessedPerTask"/> multiplied by <paramref name="multiplier"/> /// having <see cref="MinimumPixelsProcessedPerTask"/> multiplied by <paramref name="multiplier"/>
/// </summary> /// </summary>
/// <param name="multiplier">The value to multiply <see cref="MinimumPixelsProcessedPerTask"/> with.</param>
/// <returns>The modified <see cref="ParallelExecutionSettings"/>.</returns>
public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier)
{ {
Guard.MustBeGreaterThan(multiplier, 0, nameof(multiplier));
return new ParallelExecutionSettings( return new ParallelExecutionSettings(
this.MaxDegreeOfParallelism, this.MaxDegreeOfParallelism,
this.MinimumPixelsProcessedPerTask * multiplier, this.MinimumPixelsProcessedPerTask * multiplier,
this.MemoryAllocator); this.MemoryAllocator);
} }
/// <summary>
/// Get the default <see cref="SixLabors.ImageSharp.Advanced.ParallelUtils.ParallelExecutionSettings"/> for a <see cref="SixLabors.ImageSharp.Configuration"/>
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/>.</param>
/// <returns>The <see cref="ParallelExecutionSettings"/>.</returns>
public static ParallelExecutionSettings FromConfiguration(Configuration configuration)
{
return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator);
}
} }
} }

35
src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs → src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs

@ -10,29 +10,25 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.ParallelUtils namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{ {
/// <summary> /// <summary>
/// Utility methods for batched processing of pixel row intervals. /// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing. /// Parallel execution is optimized for image processing based on values defined
/// Use this instead of direct <see cref="Parallel"/> calls! /// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </summary> /// </summary>
internal static class ParallelHelper public static class ParallelHelper
{ {
/// <summary>
/// Get the default <see cref="ParallelExecutionSettings"/> for a <see cref="Configuration"/>
/// </summary>
public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration)
{
return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator);
}
/// <summary> /// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s. /// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary> /// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body) public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body)
{ {
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, parallelSettings, body); IterateRows(rectangle, parallelSettings, body);
} }
@ -40,6 +36,9 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// <summary> /// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s. /// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary> /// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows( public static void IterateRows(
Rectangle rectangle, Rectangle rectangle,
in ParallelExecutionSettings parallelSettings, in ParallelExecutionSettings parallelSettings,
@ -47,7 +46,9 @@ namespace SixLabors.ImageSharp.ParallelUtils
{ {
ValidateRectangle(rectangle); ValidateRectangle(rectangle);
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); int maxSteps = DivideCeil(
rectangle.Width * rectangle.Height,
parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s /// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation. /// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary> /// </summary>
public static void IterateRowsWithTempBuffer<T>( internal static void IterateRowsWithTempBuffer<T>(
Rectangle rectangle, Rectangle rectangle,
in ParallelExecutionSettings parallelSettings, in ParallelExecutionSettings parallelSettings,
Action<RowInterval, Memory<T>> body) Action<RowInterval, Memory<T>> body)
@ -133,13 +134,13 @@ namespace SixLabors.ImageSharp.ParallelUtils
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s /// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation. /// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary> /// </summary>
public static void IterateRowsWithTempBuffer<T>( internal static void IterateRowsWithTempBuffer<T>(
Rectangle rectangle, Rectangle rectangle,
Configuration configuration, Configuration configuration,
Action<RowInterval, Memory<T>> body) Action<RowInterval, Memory<T>> body)
where T : unmanaged where T : unmanaged
{ {
IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

6
src/ImageSharp/Color/Color.NamedColors.cs

@ -174,9 +174,9 @@ namespace SixLabors.ImageSharp
public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255);
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8F.
/// </summary> /// </summary>
public static readonly Color DarkSeaGreen = FromRgba(143, 188, 139, 255); public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255);
/// <summary> /// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #483D8B. /// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #483D8B.
@ -718,4 +718,4 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255);
} }
} }

6
src/ImageSharp/Color/Color.cs

@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static Color FromHex(string hex) public static Color FromHex(string hex)
{ {
Rgba32 rgba = Rgba32.FromHex(hex); var rgba = Rgba32.FromHex(hex);
return new Color(rgba); return new Color(rgba);
} }
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source); ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source);
PixelOperations<TPixel>.Instance.FromRgba64(Configuration.Default, rgba64Span, destination); PixelOperations<TPixel>.Instance.FromRgba64(configuration, rgba64Span, destination);
} }
} }
} }

2
src/ImageSharp/Configuration.cs

@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp
get => this.maxDegreeOfParallelism; get => this.maxDegreeOfParallelism;
set set
{ {
if (value <= 0) if (value == 0 || value < -1)
{ {
throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism));
} }

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void CheckBits() public void CheckBits()
{ {
if (this.remainingBits < 16) if (this.remainingBits < JpegConstants.Huffman.MinBits)
{ {
this.FillBuffer(); this.FillBuffer();
} }
@ -85,8 +85,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
// Attempt to load at least the minimum number of required bits into the buffer. // Attempt to load at least the minimum number of required bits into the buffer.
// We fail to do so only if we hit a marker or reach the end of the input stream. // We fail to do so only if we hit a marker or reach the end of the input stream.
this.remainingBits += 48; this.remainingBits += JpegConstants.Huffman.FetchBits;
this.data = (this.data << 48) | this.GetBytes(); this.data = (this.data << JpegConstants.Huffman.FetchBits) | this.GetBytes();
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private ulong GetBytes() private ulong GetBytes()
{ {
ulong temp = 0; ulong temp = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < JpegConstants.Huffman.FetchLoop; i++)
{ {
int b = this.ReadStream(); int b = this.ReadStream();

26
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -82,12 +82,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Figure C.1: make table of Huffman code length for each symbol // Figure C.1: make table of Huffman code length for each symbol
int p = 0; int p = 0;
for (int l = 1; l <= 16; l++) for (int j = 1; j <= 16; j++)
{ {
int i = this.Sizes[l]; int i = this.Sizes[j];
while (i-- != 0) while (i-- != 0)
{ {
huffSize[p++] = (char)l; huffSize[p++] = (char)j;
} }
} }
@ -111,20 +111,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Figure F.15: generate decoding tables for bit-sequential decoding // Figure F.15: generate decoding tables for bit-sequential decoding
p = 0; p = 0;
for (int l = 1; l <= 16; l++) for (int j = 1; j <= 16; j++)
{ {
if (this.Sizes[l] != 0) if (this.Sizes[j] != 0)
{ {
int offset = p - (int)huffCode[p]; this.ValOffset[j] = p - (int)huffCode[p];
this.ValOffset[l] = offset; p += this.Sizes[j];
p += this.Sizes[l]; this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l
this.MaxCode[l] = huffCode[p - 1]; // Maximum code of length l this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify
this.MaxCode[l] <<= 64 - l; // Left justify this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1;
this.MaxCode[l] |= (1ul << (64 - l)) - 1;
} }
else else
{ {
this.MaxCode[l] = 0; this.MaxCode[j] = 0;
} }
} }
@ -142,11 +141,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
p = 0; p = 0;
for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++) for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++)
{ {
int jShift = JpegConstants.Huffman.LookupBits - length;
for (int i = 1; i <= this.Sizes[length]; i++, p++) for (int i = 1; i <= this.Sizes[length]; i++, p++)
{ {
// length = current code's length, p = its index in huffCode[] & Values[]. // length = current code's length, p = its index in huffCode[] & Values[].
// Generate left-justified code followed by all possible bit sequences // Generate left-justified code followed by all possible bit sequences
int lookBits = (int)(huffCode[p] << (JpegConstants.Huffman.LookupBits - length)); int lookBits = (int)(huffCode[p] << jShift);
for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--)
{ {
this.LookaheadSize[lookBits] = (byte)length; this.LookaheadSize[lookBits] = (byte)length;

20
src/ImageSharp/Formats/Jpeg/JpegConstants.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
namespace SixLabors.ImageSharp.Formats.Jpeg namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
@ -249,6 +250,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
public const int RegisterSize = 64; public const int RegisterSize = 64;
/// <summary>
/// The number of bits to fetch when filling the <see cref="HuffmanScanBuffer"/> buffer.
/// </summary>
public const int FetchBits = 48;
/// <summary>
/// The number of times to read the input stream when filling the <see cref="HuffmanScanBuffer"/> buffer.
/// </summary>
public const int FetchLoop = FetchBits / 8;
/// <summary>
/// The minimum number of bits allowed before by the <see cref="HuffmanScanBuffer"/> before fetching.
/// </summary>
public const int MinBits = RegisterSize - FetchBits;
/// <summary> /// <summary>
/// If the next Huffman code is no more than this number of bits, we can obtain its length /// If the next Huffman code is no more than this number of bits, we can obtain its length
/// and the corresponding symbol directly from this tables. /// and the corresponding symbol directly from this tables.
@ -266,4 +282,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public const int LookupSize = 1 << LookupBits; public const int LookupSize = 1 << LookupBits;
} }
} }
} }

2
src/ImageSharp/ImageFrameCollection{TPixel}.cs

@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp
this.parent.GetConfiguration(), this.parent.GetConfiguration(),
source.Size(), source.Size(),
source.Metadata.DeepClone()); source.Metadata.DeepClone());
source.CopyPixelsTo(result.PixelBuffer.Span); source.CopyPixelsTo(result.PixelBuffer.GetSpan());
return result; return result;
} }
} }

6
src/ImageSharp/ImageFrame{TPixel}.cs

@ -6,9 +6,9 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -218,10 +218,10 @@ namespace SixLabors.ImageSharp
if (typeof(TPixel) == typeof(TDestinationPixel)) if (typeof(TPixel) == typeof(TDestinationPixel))
{ {
Span<TPixel> dest1 = MemoryMarshal.Cast<TDestinationPixel, TPixel>(destination); Span<TPixel> dest1 = MemoryMarshal.Cast<TDestinationPixel, TPixel>(destination);
this.PixelBuffer.Span.CopyTo(dest1); this.PixelBuffer.GetSpan().CopyTo(dest1);
} }
PixelOperations<TPixel>.Instance.To(this.Configuration, this.PixelBuffer.Span, destination); PixelOperations<TPixel>.Instance.To(this.Configuration, this.PixelBuffer.GetSpan(), destination);
} }
/// <inheritdoc/> /// <inheritdoc/>

132
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -13,13 +13,69 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Defines extension methods for <see cref="Buffer2D{T}"/>. /// Defines extension methods for <see cref="Buffer2D{T}"/>.
/// </summary> /// </summary>
internal static class Buffer2DExtensions public static class Buffer2DExtensions
{ {
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// </summary>
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
/// <typeparam name="T">The value type.</typeparam>
/// <returns>The <see cref="Span{T}"/> referencing the memory area.</returns>
public static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
return buffer.MemorySource.GetSpan();
}
/// <summary>
/// Gets the <see cref="Memory{T}"/> holding the backing buffer of <paramref name="buffer"/>.
/// </summary>
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
/// <typeparam name="T">The value type.</typeparam>
/// <returns>The <see cref="Memory{T}"/>.</returns>
public static Memory<T> GetMemory<T>(this Buffer2D<T> buffer)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
return buffer.MemorySource.Memory;
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
}
/// <summary>
/// Gets a <see cref="Memory{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
Guard.NotNull(buffer, nameof(buffer));
return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width);
}
/// <summary> /// <summary>
/// Copy <paramref name="columnCount"/> columns of <paramref name="buffer"/> inplace, /// Copy <paramref name="columnCount"/> columns of <paramref name="buffer"/> inplace,
/// from positions starting at <paramref name="sourceIndex"/> to positions at <paramref name="destIndex"/>. /// from positions starting at <paramref name="sourceIndex"/> to positions at <paramref name="destIndex"/>.
/// </summary> /// </summary>
public static unsafe void CopyColumns<T>( internal static unsafe void CopyColumns<T>(
this Buffer2D<T> buffer, this Buffer2D<T> buffer,
int sourceIndex, int sourceIndex,
int destIndex, int destIndex,
@ -37,7 +93,7 @@ namespace SixLabors.ImageSharp.Memory
int dOffset = destIndex * elementSize; int dOffset = destIndex * elementSize;
long count = columnCount * elementSize; long count = columnCount * elementSize;
Span<byte> span = MemoryMarshal.AsBytes(buffer.Memory.Span); Span<byte> span = MemoryMarshal.AsBytes(buffer.GetMemory().Span);
fixed (byte* ptr = span) fixed (byte* ptr = span)
{ {
@ -60,7 +116,7 @@ namespace SixLabors.ImageSharp.Memory
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param> /// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns> /// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer) internal static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
where T : struct where T : struct
{ {
return new Rectangle(0, 0, buffer.Width, buffer.Height); return new Rectangle(0, 0, buffer.Width, buffer.Height);
@ -73,11 +129,11 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param> /// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param> /// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns> /// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle) internal static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct => where T : struct =>
new BufferArea<T>(buffer, rectangle); new BufferArea<T>(buffer, rectangle);
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height) internal static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct => where T : struct =>
new BufferArea<T>(buffer, new Rectangle(x, y, width, height)); new BufferArea<T>(buffer, new Rectangle(x, y, width, height));
@ -87,64 +143,17 @@ namespace SixLabors.ImageSharp.Memory
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param> /// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns> /// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer) internal static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct => where T : struct =>
new BufferArea<T>(buffer); new BufferArea<T>(buffer);
public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct =>
new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));
/// <summary> /// <summary>
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/> /// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
/// </summary> /// </summary>
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows) internal static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows)
where T : struct where T : struct
{ {
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); return buffer.GetSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width);
}
/// <summary>
/// Gets a <see cref="Memory{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
where T : struct
{
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
} }
/// <summary> /// <summary>
@ -153,21 +162,12 @@ namespace SixLabors.ImageSharp.Memory
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param> /// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Size{T}"/> of the buffer</returns> /// <returns>The <see cref="Size{T}"/> of the buffer</returns>
public static Size Size<T>(this Buffer2D<T> buffer) internal static Size Size<T>(this Buffer2D<T> buffer)
where T : struct where T : struct
{ {
return new Size(buffer.Width, buffer.Height); return new Size(buffer.Width, buffer.Height);
} }
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// </summary>
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.MemorySource.GetSpan();
}
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void CheckColumnRegionsDoNotOverlap<T>( private static void CheckColumnRegionsDoNotOverlap<T>(
Buffer2D<T> buffer, Buffer2D<T> buffer,

22
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -12,8 +12,12 @@ namespace SixLabors.ImageSharp.Memory
/// Represents a buffer of value type objects /// Represents a buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements. /// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary> /// </summary>
/// <remarks>
/// Before RC1, this class might be target of API changes, use it on your own risk!
/// </remarks>
/// <typeparam name="T">The value type.</typeparam> /// <typeparam name="T">The value type.</typeparam>
internal sealed class Buffer2D<T> : IDisposable // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core).
public sealed class Buffer2D<T> : IDisposable
where T : struct where T : struct
{ {
private MemorySource<T> memorySource; private MemorySource<T> memorySource;
@ -24,7 +28,7 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="memorySource">The buffer to wrap</param> /// <param name="memorySource">The buffer to wrap</param>
/// <param name="width">The number of elements in a row</param> /// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param> /// <param name="height">The number of rows</param>
public Buffer2D(MemorySource<T> memorySource, int width, int height) internal Buffer2D(MemorySource<T> memorySource, int width, int height)
{ {
this.memorySource = memorySource; this.memorySource = memorySource;
this.Width = width; this.Width = width;
@ -44,11 +48,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Gets the backing <see cref="MemorySource{T}"/> /// Gets the backing <see cref="MemorySource{T}"/>
/// </summary> /// </summary>
public MemorySource<T> MemorySource => this.memorySource; internal MemorySource<T> MemorySource => this.memorySource;
public Memory<T> Memory => this.MemorySource.Memory;
public Span<T> Span => this.Memory.Span;
/// <summary> /// <summary>
/// Gets a reference to the element at the specified position. /// Gets a reference to the element at the specified position.
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="x">The x coordinate (row)</param> /// <param name="x">The x coordinate (row)</param>
/// <param name="y">The y coordinate (position at row)</param> /// <param name="y">The y coordinate (position at row)</param>
/// <returns>A reference to the element.</returns> /// <returns>A reference to the element.</returns>
public ref T this[int x, int y] internal ref T this[int x, int y]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Memory
DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
Span<T> span = this.Span; Span<T> span = this.GetSpan();
return ref span[(this.Width * y) + x]; return ref span[(this.Width * y) + x];
} }
} }
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Memory
DebugGuard.MustBeGreaterThan(h, 0, nameof(h)); DebugGuard.MustBeGreaterThan(h, 0, nameof(h));
DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h)); DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h));
Memory<T> slice = this.Memory.Slice(y * this.Width, h * this.Width); Memory<T> slice = this.GetMemory().Slice(y * this.Width, h * this.Width);
var memory = new MemorySource<T>(slice); var memory = new MemorySource<T>(slice);
return new Buffer2D<T>(memory, this.Width, h); return new Buffer2D<T>(memory, this.Width, h);
} }
@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Memory
/// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1),
/// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2!
/// </summary> /// </summary>
public static void SwapOrCopyContent(Buffer2D<T> destination, Buffer2D<T> source) internal static void SwapOrCopyContent(Buffer2D<T> destination, Buffer2D<T> source)
{ {
MemorySource<T>.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource); MemorySource<T>.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource);
SwapDimensionData(destination, source); SwapDimensionData(destination, source);

43
src/ImageSharp/Memory/RowInterval.cs

@ -10,26 +10,32 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Represents an interval of rows in a <see cref="Rectangle"/> and/or <see cref="Buffer2D{T}"/> /// Represents an interval of rows in a <see cref="Rectangle"/> and/or <see cref="Buffer2D{T}"/>
/// </summary> /// </summary>
internal readonly struct RowInterval : IEquatable<RowInterval> /// <remarks>
/// Before RC1, this class might be target of API changes, use it on your own risk!
/// </remarks>
// TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core).
public readonly struct RowInterval : IEquatable<RowInterval>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RowInterval"/> struct. /// Initializes a new instance of the <see cref="RowInterval"/> struct.
/// </summary> /// </summary>
/// <param name="min">The inclusive minimum row.</param>
/// <param name="max">The exclusive maximum row.</param>
public RowInterval(int min, int max) public RowInterval(int min, int max)
{ {
DebugGuard.MustBeLessThan(min, max, nameof(min)); Guard.MustBeLessThan(min, max, nameof(min));
this.Min = min; this.Min = min;
this.Max = max; this.Max = max;
} }
/// <summary> /// <summary>
/// Gets the INCLUSIVE minimum. /// Gets the inclusive minimum row.
/// </summary> /// </summary>
public int Min { get; } public int Min { get; }
/// <summary> /// <summary>
/// Gets the EXCLUSIVE maximum. /// Gets the exclusive maximum row.
/// </summary> /// </summary>
public int Max { get; } public int Max { get; }
@ -38,33 +44,48 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
public int Height => this.Max - this.Min; public int Height => this.Max - this.Min;
/// <summary>
/// Returns a boolean indicating whether the given two <see cref="RowInterval"/>-s are equal.
/// </summary>
/// <param name="left">The first <see cref="RowInterval"/> to compare.</param>
/// <param name="right">The second <see cref="RowInterval"/> to compare.</param>
/// <returns>True if the given <see cref="RowInterval"/>-s are equal; False otherwise.</returns>
public static bool operator ==(RowInterval left, RowInterval right) public static bool operator ==(RowInterval left, RowInterval right)
{ {
return left.Equals(right); return left.Equals(right);
} }
/// <summary>
/// Returns a boolean indicating whether the given two <see cref="RowInterval"/>-s are not equal.
/// </summary>
/// <param name="left">The first <see cref="RowInterval"/> to compare.</param>
/// <param name="right">The second <see cref="RowInterval"/> to compare.</param>
/// <returns>True if the given <see cref="RowInterval"/>-s are not equal; False otherwise.</returns>
public static bool operator !=(RowInterval left, RowInterval right) public static bool operator !=(RowInterval left, RowInterval right)
{ {
return !left.Equals(right); return !left.Equals(right);
} }
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]";
public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max);
public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length);
public bool Equals(RowInterval other) public bool Equals(RowInterval other)
{ {
return this.Min == other.Min && this.Max == other.Max; return this.Min == other.Min && this.Max == other.Max;
} }
/// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other);
} }
/// <inheritdoc />
public override int GetHashCode() => HashCode.Combine(this.Min, this.Max); public override int GetHashCode() => HashCode.Combine(this.Min, this.Max);
/// <inheritdoc />
public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]";
internal RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max);
internal RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length);
} }
} }

6
src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs

@ -9,17 +9,17 @@ namespace SixLabors.ImageSharp.PixelFormats
public enum PixelAlphaCompositionMode public enum PixelAlphaCompositionMode
{ {
/// <summary> /// <summary>
/// returns the destination over the source. /// Returns the destination over the source.
/// </summary> /// </summary>
SrcOver = 0, SrcOver = 0,
/// <summary> /// <summary>
/// returns the source colors. /// Returns the source colors.
/// </summary> /// </summary>
Src, Src,
/// <summary> /// <summary>
/// returns the source over the destination. /// Returns the source over the destination.
/// </summary> /// </summary>
SrcAtop, SrcAtop,

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

File diff suppressed because it is too large

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

@ -68,9 +68,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
foreach(var blender in blenders) { foreach(var blender in blenders) {
var blender_composer= $"{blender}{composer}"; var blender_composer= $"{blender}{composer}";
#> #>
internal class <#= blender_composer#> : PixelBlender<TPixel> /// <summary>
/// A pixel blender that implements the "<#= blender_composer#>" composition equation.
/// </summary>
public class <#= blender_composer#> : PixelBlender<TPixel>
{ {
/// <summary> /// <summary>
/// Gets the static instance of this blender. /// Gets the static instance of this blender.

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

File diff suppressed because it is too large

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

@ -17,8 +17,6 @@
// Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when // Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when
// AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. // AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit.
#> #>
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -26,9 +24,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
<# void GeneratePixelBlenders(string blender) { #> <# void GeneratePixelBlenders(string blender) { #>
/// <summary>
/// Returns the result of the "<#=blender#>Src" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -37,6 +40,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return source; return source;
} }
/// <summary>
/// Returns the result of the "<#=blender#>SrcAtop" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -45,6 +55,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Atop(backdrop, source, <#=blender#>(backdrop, source)); return Atop(backdrop, source, <#=blender#>(backdrop, source));
} }
/// <summary>
/// Returns the result of the "<#=blender#>SrcOver" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -53,14 +70,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Over(backdrop, source, <#=blender#>(backdrop, source)); return Over(backdrop, source, <#=blender#>(backdrop, source));
} }
/// <summary>
/// Returns the result of the "<#=blender#>SrcIn" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return In(backdrop, source, <#=blender#>(backdrop, source)); return In(backdrop, source);
} }
/// <summary>
/// Returns the result of the "<#=blender#>SrcOut" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -69,12 +100,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Out(backdrop, source); return Out(backdrop, source);
} }
/// <summary>
/// Returns the result of the "<#=blender#>Dest" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity)
{ {
return backdrop; return backdrop;
} }
/// <summary>
/// Returns the result of the "<#=blender#>DestAtop" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -83,6 +128,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Atop(source, backdrop, <#=blender#>(source, backdrop)); return Atop(source, backdrop, <#=blender#>(source, backdrop));
} }
/// <summary>
/// Returns the result of the "<#=blender#>DestOver" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -91,14 +143,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Over(source, backdrop, <#=blender#>(source, backdrop)); return Over(source, backdrop, <#=blender#>(source, backdrop));
} }
/// <summary>
/// Returns the result of the "<#=blender#>DestIn" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return In(source, backdrop, <#=blender#>(source, backdrop)); return In(source, backdrop);
} }
/// <summary>
/// Returns the result of the "<#=blender#>DestOut" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -107,6 +173,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Out(source, backdrop); return Out(source, backdrop);
} }
/// <summary>
/// Returns the result of the "<#=blender#>Xor" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -115,6 +188,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Xor(backdrop, source); return Xor(backdrop, source);
} }
/// <summary>
/// Returns the result of the "<#=blender#>Clear" compositing equation.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity)
{ {
@ -127,6 +207,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
<# void GenerateGenericPixelBlender(string blender, string composer) { #> <# void GenerateGenericPixelBlender(string blender, string composer) { #>
/// <summary>
/// Returns the result of the "<#=blender#><#=composer#>" compositing equation.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="opacity">The source opacity. Range 0..1</param>
/// <returns>The <typeparamref name="TPixel"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity) public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>

150
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
/// <summary> /// <summary>
/// Source over backdrop /// Returns the result of the "Normal" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Normal(Vector4 backdrop, Vector4 source) public static Vector4 Normal(Vector4 backdrop, Vector4 source)
{ {
@ -33,11 +33,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Source multiplied by backdrop /// Returns the result of the "Multiply" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Multiply(Vector4 backdrop, Vector4 source) public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
{ {
@ -45,11 +45,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Source added to backdrop /// Returns the result of the "Add" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Add(Vector4 backdrop, Vector4 source) public static Vector4 Add(Vector4 backdrop, Vector4 source)
{ {
@ -57,11 +57,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Source subtracted from backdrop /// Returns the result of the "Subtract" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Subtract(Vector4 backdrop, Vector4 source) public static Vector4 Subtract(Vector4 backdrop, Vector4 source)
{ {
@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Complement of source multiplied by the complement of backdrop /// Returns the result of the "Screen" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Screen(Vector4 backdrop, Vector4 source) public static Vector4 Screen(Vector4 backdrop, Vector4 source)
{ {
@ -81,11 +81,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Per element, chooses the smallest value of source and backdrop /// Returns the result of the "Darken" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Darken(Vector4 backdrop, Vector4 source) public static Vector4 Darken(Vector4 backdrop, Vector4 source)
{ {
@ -93,11 +93,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Per element, chooses the largest value of source and backdrop /// Returns the result of the "Lighten" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Lighten(Vector4 backdrop, Vector4 source) public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
{ {
@ -105,11 +105,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Overlays source over backdrop /// Returns the result of the "Overlay" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Overlay(Vector4 backdrop, Vector4 source) public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
{ {
@ -121,11 +121,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
} }
/// <summary> /// <summary>
/// Hard light effect /// Returns the result of the "HardLight" compositing equation.
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">Source color</param> /// <param name="source">The source vector.</param>
/// <returns>Output color</returns> /// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 HardLight(Vector4 backdrop, Vector4 source) public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
{ {
@ -145,22 +145,29 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float OverlayValueFunction(float backdrop, float source) private static float OverlayValueFunction(float backdrop, float source)
{ {
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop));
} }
/// <summary>
/// Returns the result of the "Over" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="blend">The amount to blend. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 blend)
{ {
// calculate weights // calculate weights
float blendW = dst.W * src.W; float blendW = destination.W * source.W;
float dstW = dst.W - blendW; float dstW = destination.W - blendW;
float srcW = src.W - blendW; float srcW = source.W - blendW;
// calculate final alpha // calculate final alpha
float alpha = dstW + srcW + blendW; float alpha = dstW + srcW + blendW;
// calculate final color // calculate final color
Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); Vector4 color = (destination * dstW) + (source * srcW) + (blend * blendW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
@ -169,18 +176,25 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
/// <summary>
/// Returns the result of the "Atop" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="blend">The amount to blend. Range 0..1</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 blend)
{ {
// calculate weights // calculate weights
float blendW = dst.W * src.W; float blendW = destination.W * source.W;
float dstW = dst.W - blendW; float dstW = destination.W - blendW;
// calculate final alpha // calculate final alpha
float alpha = dstW + blendW; float alpha = dstW + blendW;
// calculate final color // calculate final color
Vector4 color = (dst * dstW) + (blend * blendW); Vector4 color = (destination * dstW) + (blend * blendW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
@ -189,38 +203,56 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
/// <summary>
/// Returns the result of the "In" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 In(Vector4 destination, Vector4 source)
{ {
float alpha = dst.W * src.W; float alpha = destination.W * source.W;
Vector4 color = src * alpha; // premultiply Vector4 color = source * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha; color.W = alpha;
return color; return color;
} }
/// <summary>
/// Returns the result of the "Out" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Out(Vector4 dst, Vector4 src) public static Vector4 Out(Vector4 destination, Vector4 source)
{ {
float alpha = (1 - dst.W) * src.W; float alpha = (1 - destination.W) * source.W;
Vector4 color = src * alpha; // premultiply Vector4 color = source * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha; color.W = alpha;
return color; return color;
} }
/// <summary>
/// Returns the result of the "XOr" compositing equation.
/// </summary>
/// <param name="destination">The destination vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Xor(Vector4 dst, Vector4 src) public static Vector4 Xor(Vector4 destination, Vector4 source)
{ {
float srcW = 1 - dst.W; float srcW = 1 - destination.W;
float dstW = 1 - src.W; float dstW = 1 - source.W;
float alpha = (src.W * srcW) + (dst.W * dstW); float alpha = (source.W * srcW) + (destination.W * dstW);
Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); Vector4 color = (source.W * source * srcW) + (destination.W * destination * dstW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
@ -235,4 +267,4 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return Vector4.Zero; return Vector4.Zero;
} }
} }
} }

131
src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs

@ -1,10 +1,9 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
@ -13,7 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Abstract base class for calling pixel composition functions /// Abstract base class for calling pixel composition functions
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal abstract class PixelBlender<TPixel> public abstract class PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
@ -23,64 +22,11 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="source">The source color.</param> /// <param name="source">The source color.</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param> /// </param>
/// <returns>The final pixel value after composition</returns> /// <returns>The final pixel value after composition.</returns>
public abstract TPixel Blend(TPixel background, TPixel source, float amount); public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
ReadOnlySpan<float> amount);
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
public void Blend(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixel> source,
ReadOnlySpan<float> amount)
{
this.Blend<TPixel>(configuration, destination, background, source, amount);
}
/// <summary> /// <summary>
/// Blends 2 rows together /// Blends 2 rows together
/// </summary> /// </summary>
@ -90,20 +36,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param> /// </param>
public void Blend<TPixelSrc>( public void Blend<TPixelSrc>(
Configuration configuration, Configuration configuration,
Span<TPixel> destination, Span<TPixel> destination,
ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source, ReadOnlySpan<TPixelSrc> source,
ReadOnlySpan<float> amount) float amount)
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
using (IMemoryOwner<Vector4> buffer = using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3)) configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
@ -124,6 +70,25 @@ namespace SixLabors.ImageSharp.PixelFormats
} }
} }
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param>
public void Blend(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixel> source,
ReadOnlySpan<float> amount)
=> this.Blend<TPixel>(configuration, destination, background, source, amount);
/// <summary> /// <summary>
/// Blends 2 rows together /// Blends 2 rows together
/// </summary> /// </summary>
@ -133,20 +98,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A span with values between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param> /// </param>
public void Blend<TPixelSrc>( public void Blend<TPixelSrc>(
Configuration configuration, Configuration configuration,
Span<TPixel> destination, Span<TPixel> destination,
ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source, ReadOnlySpan<TPixelSrc> source,
float amount) ReadOnlySpan<float> amount)
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
using (IMemoryOwner<Vector4> buffer = using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3)) configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
@ -166,5 +131,37 @@ namespace SixLabors.ImageSharp.PixelFormats
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
} }
} }
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "background" is returned, at amount = 1, "source" is returned.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
ReadOnlySpan<float> amount);
} }
} }

8
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary> /// </summary>
/// <param name="options">the blending and composition to apply</param> /// <param name="options">the blending and composition to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns> /// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options) public PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options)
{ {
return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode);
} }
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="colorMode">The color blending mode to apply</param> /// <param name="colorMode">The color blending mode to apply</param>
/// <param name="alphaMode">The alpha composition mode to apply</param> /// <param name="alphaMode">The alpha composition mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns> /// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) public virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{ {
switch (alphaMode) switch (alphaMode)
{ {
@ -214,4 +214,4 @@ namespace SixLabors.ImageSharp.PixelFormats
} }
} }
} }
} }

2
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -4,7 +4,7 @@
using System; using System;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -8,8 +8,8 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters;

2
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs

@ -5,8 +5,8 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives; using SixLabors.Primitives;

3
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

@ -4,8 +4,9 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives; using SixLabors.Primitives;

3
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs

@ -4,8 +4,9 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs

@ -6,8 +6,8 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Filters;

2
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs

@ -5,8 +5,8 @@ using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs

@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual void Dispose() public void Dispose()
{ {
this.Dispose(true); this.Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);

2
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -8,8 +8,8 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs

@ -5,8 +5,8 @@ using System;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs

@ -6,8 +6,8 @@ using System.Buffers;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs

@ -6,8 +6,8 @@ using System.Buffers;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

8
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -3,7 +3,7 @@
using System; using System;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -47,10 +47,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle bounds = this.cropRectangle; Rectangle bounds = this.cropRectangle;
// Copying is cheap, we should process more pixels per task: // Copying is cheap, we should process more pixels per task:
ParallelExecutionSettings parallelSettings ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration)
= this.Configuration .MultiplyMinimumPixelsPerTask(4);
.GetParallelSettings()
.MultiplyMinimumPixelsPerTask(4);
ParallelHelper.IterateRows( ParallelHelper.IterateRows(
bounds, bounds,

2
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs

@ -5,7 +5,7 @@ using System;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs

@ -5,7 +5,7 @@ using System;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

2
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.DestinationLength = destinationLength; this.DestinationLength = destinationLength;
this.MaxDiameter = (radius * 2) + 1; this.MaxDiameter = (radius * 2) + 1;
this.data = memoryAllocator.Allocate2D<float>(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); this.data = memoryAllocator.Allocate2D<float>(this.MaxDiameter, bufferHeight, AllocationOptions.Clean);
this.pinHandle = this.data.Memory.Pin(); this.pinHandle = this.data.GetMemory().Pin();
this.kernels = new ResizeKernel[destinationLength]; this.kernels = new ResizeKernel[destinationLength];
this.tempValues = new double[this.MaxDiameter]; this.tempValues = new double[this.MaxDiameter];
} }

2
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -4,8 +4,8 @@
using System; using System;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;

14
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs

@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public void FillDestinationPixels(RowInterval rowInterval, Buffer2D<TPixel> destination) public void FillDestinationPixels(RowInterval rowInterval, Buffer2D<TPixel> destination)
{ {
Span<Vector4> tempColSpan = this.tempColumnBuffer.GetSpan(); Span<Vector4> tempColSpan = this.tempColumnBuffer.GetSpan();
Span<Vector4> transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan();
for (int y = rowInterval.Min; y < rowInterval.Max; y++) for (int y = rowInterval.Min; y < rowInterval.Max; y++)
{ {
@ -129,8 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan);
int top = kernel.StartIndex - this.currentWindow.Min; int top = kernel.StartIndex - this.currentWindow.Min;
ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top];
ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top];
for (int x = 0; x < this.destWidth; x++) for (int x = 0; x < this.destWidth; x++)
{ {
@ -167,6 +167,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private void CalculateFirstPassValues(RowInterval calculationInterval) private void CalculateFirstPassValues(RowInterval calculationInterval)
{ {
Span<Vector4> tempRowSpan = this.tempRowBuffer.GetSpan(); Span<Vector4> tempRowSpan = this.tempRowBuffer.GetSpan();
Span<Vector4> transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan();
for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) for (int y = calculationInterval.Min; y < calculationInterval.Max; y++)
{ {
Span<TPixel> sourceRow = this.source.GetRowSpan(y); Span<TPixel> sourceRow = this.source.GetRowSpan(y);
@ -177,17 +179,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
tempRowSpan, tempRowSpan,
this.conversionModifiers); this.conversionModifiers);
// Span<Vector4> firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); // optimization for:
ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; // Span<Vector4> firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min);
ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min];
for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++)
{ {
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X);
// optimization for:
// firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan);
Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan);
} }
} }
} }
} }
} }

2
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs

@ -4,8 +4,8 @@
using System; using System;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;

24
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -63,12 +63,28 @@ namespace SixLabors.ImageSharp.Tests
} }
[Theory] [Theory]
[InlineData(0)] [InlineData(-3, true)]
[InlineData(-42)] [InlineData(-2, true)]
public void Set_MaxDegreeOfParallelism_ToNonPositiveValue_Throws(int value) [InlineData(-1, false)]
[InlineData(0, true)]
[InlineData(1, false)]
[InlineData(5, false)]
public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws)
{ {
var cfg = new Configuration(); var cfg = new Configuration();
Assert.Throws<ArgumentOutOfRangeException>(() => cfg.MaxDegreeOfParallelism = value); if (throws)
{
Assert.Throws<ArgumentOutOfRangeException>(
() =>
{
cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism;
});
}
else
{
cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism;
Assert.Equal(maxDegreeOfParallelism, cfg.MaxDegreeOfParallelism);
}
} }

40
tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs

@ -0,0 +1,40 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Helpers
{
public class ParallelExecutionSettingsTests
{
[Theory]
[InlineData(-3, true)]
[InlineData(-2, true)]
[InlineData(-1, false)]
[InlineData(0, true)]
[InlineData(1, false)]
[InlineData(5, false)]
public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws)
{
if (throws)
{
Assert.Throws<ArgumentOutOfRangeException>(
() =>
{
_ = new ParallelExecutionSettings(
maxDegreeOfParallelism,
Configuration.Default.MemoryAllocator);
});
}
else
{
var parallelSettings = new ParallelExecutionSettings(
maxDegreeOfParallelism,
Configuration.Default.MemoryAllocator);
Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism);
}
}
}
}

4
tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs

@ -7,8 +7,8 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -332,7 +332,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
}); });
// Assert: // Assert:
TestImageExtensions.CompareBuffers(expected.Span, actual.Span); TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan());
} }
} }

2
tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Span<int> span = buffer.GetMultiRowSpan(rows); Span<int> span = buffer.GetMultiRowSpan(rows);
ref int expected0 = ref buffer.Span[min * width]; ref int expected0 = ref buffer.GetSpan()[min * width];
int expectedLength = (max - min) * width; int expectedLength = (max - min) * width;
ref int actual0 = ref span[0]; ref int actual0 = ref span[0];

22
tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{ {
Assert.Equal(width, buffer.Width); Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height); Assert.Equal(height, buffer.Height);
Assert.Equal(width * height, buffer.Memory.Length); Assert.Equal(width * height, buffer.GetMemory().Length);
} }
} }
@ -75,22 +75,6 @@ namespace SixLabors.ImageSharp.Tests.Memory
} }
} }
[Theory]
[InlineData(7, 42, 0, 0)]
[InlineData(7, 42, 3, 10)]
[InlineData(17, 42, 0, 41)]
public void GetRowSpanXY(int width, int height, int x, int y)
{
using (Buffer2D<TestStructs.Foo> buffer = this.MemoryAllocator.Allocate2D<TestStructs.Foo>(width, height))
{
Span<TestStructs.Foo> span = buffer.GetRowSpan(x, y);
// Assert.Equal(width * y + x, span.Start);
Assert.Equal(width - x, span.Length);
Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x);
}
}
[Theory] [Theory]
[InlineData(42, 8, 0, 0)] [InlineData(42, 8, 0, 0)]
[InlineData(400, 1000, 20, 10)] [InlineData(400, 1000, 20, 10)]
@ -140,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var rnd = new Random(123); var rnd = new Random(123);
using (Buffer2D<float> b = this.MemoryAllocator.Allocate2D<float>(width, height)) using (Buffer2D<float> b = this.MemoryAllocator.Allocate2D<float>(width, height))
{ {
rnd.RandomFill(b.Span, 0, 1); rnd.RandomFill(b.GetSpan(), 0, 1);
b.CopyColumns(startIndex, destIndex, columnCount); b.CopyColumns(startIndex, destIndex, columnCount);
@ -162,7 +146,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
var rnd = new Random(123); var rnd = new Random(123);
using (Buffer2D<float> b = this.MemoryAllocator.Allocate2D<float>(100, 100)) using (Buffer2D<float> b = this.MemoryAllocator.Allocate2D<float>(100, 100))
{ {
rnd.RandomFill(b.Span, 0, 1); rnd.RandomFill(b.GetSpan(), 0, 1);
b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22);
b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22);

2
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -7,9 +7,9 @@ using System.IO;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;

2
tests/Images/External

@ -1 +1 @@
Subproject commit 58b2c01f9b66dd42d2b5b040b85e6846083b5e5f Subproject commit 1d3d4e3652dc95bd8bd420346bfe0f189addc587
Loading…
Cancel
Save