Browse Source

Merge pull request #869 from SixLabors/af/pixel-conversion

Introduce extended pixel conversion
pull/879/head
Anton Firsov 7 years ago
committed by GitHub
parent
commit
c2d2ccb5ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ImageSharp.sln.DotSettings
  2. 24
      src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
  3. 20
      src/ImageSharp/Common/Helpers/Guard.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  5. 36
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  6. 40
      src/ImageSharp/PixelFormats/PixelConversionModifiers.cs
  7. 20
      src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs
  8. 21
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
  9. 21
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
  10. 21
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
  11. 21
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
  12. 33
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
  13. 34
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs
  14. 21
      src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs
  15. 73
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  16. 74
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
  17. 58
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
  18. 48
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs
  19. 2
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  20. 2
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  21. 2
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  22. 2
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
  23. 2
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
  24. 2
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
  25. 2
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  26. 2
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
  27. 102
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  28. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs
  29. 2
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  30. 155
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  31. 20
      tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs
  32. 1
      tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs
  33. 24
      tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs
  34. 308
      tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs
  35. 4
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  36. 2
      tests/Images/External

1
ImageSharp.sln.DotSettings

@ -382,6 +382,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>

24
src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs

@ -30,9 +30,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Expand(v.X);
v.Y = Expand(v.Y);
v.Z = Expand(v.Z);
Expand(ref v);
}
}
@ -48,9 +46,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Compress(v.X);
v.Y = Compress(v.Y);
v.Z = Compress(v.Z);
Compress(ref v);
}
}
@ -58,17 +54,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Expands a companded vector to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the linear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W);
public static void Expand(ref Vector4 vector)
{
vector.X = Expand(vector.X);
vector.Y = Expand(vector.Y);
vector.Z = Expand(vector.Z);
}
/// <summary>
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the nonlinear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W);
public static void Compress(ref Vector4 vector)
{
vector.X = Compress(vector.X);
vector.Y = Compress(vector.Y);
vector.Z = Compress(vector.Z);
}
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.

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

@ -257,6 +257,26 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Verifies that the 'destination' span is not shorter than 'source'.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="destination">The destination span</param>
/// <param name="destinationParamName">The name of the argument for 'destination'</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void DestinationShouldNotBeTooShort<TSource, TDest>(
Span<TSource> source,
Span<TDest> destination,
string destinationParamName)
{
if (destination.Length < source.Length)
{
ThrowArgumentException($"Destination span is too short!", destinationParamName);
}
}
/// <summary>
/// Verifies, that the `source` span has the length of 'minLength', or longer.
/// </summary>

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
// TODO: Investigate if slicing is actually necessary
PixelOperations<TPixel>.Instance.FromVector4(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow);
}
}
}

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

@ -112,21 +112,15 @@ namespace SixLabors.ImageSharp.PixelFormats
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
PixelOperations<TPixel>.Instance.ToScaledVector4(
configuration,
background.Slice(0, background.Length),
backgroundSpan);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(
configuration,
source.Slice(0, background.Length),
sourceSpan);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.FromScaledVector4(
configuration,
destinationSpan.Slice(0, background.Length),
destination);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
}
}
@ -161,21 +155,15 @@ namespace SixLabors.ImageSharp.PixelFormats
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
PixelOperations<TPixel>.Instance.ToScaledVector4(
configuration,
background.Slice(0, background.Length),
backgroundSpan);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(
configuration,
source.Slice(0, background.Length),
sourceSpan);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.FromScaledVector4(
configuration,
destinationSpan.Slice(0, background.Length),
destination);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
}
}
}

40
src/ImageSharp/PixelFormats/PixelConversionModifiers.cs

@ -0,0 +1,40 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Flags responsible to select additional operations which could be effitiently applied in
/// <see cref="PixelOperations{TPixel}.ToVector4(SixLabors.ImageSharp.Configuration,System.ReadOnlySpan{TPixel},System.Span{System.Numerics.Vector4},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/>
/// or
/// <see cref="PixelOperations{TPixel}.FromVector4Destructive(SixLabors.ImageSharp.Configuration,System.Span{System.Numerics.Vector4},System.Span{TPixel},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/>
/// knowing the pixel type.
/// </summary>
[Flags]
internal enum PixelConversionModifiers
{
/// <summary>
/// No special operation is selected
/// </summary>
None = 0,
/// <summary>
/// Select <see cref="IPixel.ToScaledVector4"/> and <see cref="IPixel.FromScaledVector4"/> instead the standard (non scaled) variants.
/// </summary>
Scale = 1 << 0,
/// <summary>
/// Enable alpha premultiplication / unpremultiplication
/// </summary>
Premultiply = 1 << 1,
/// <summary>
/// Enable SRGB companding (defined in <see cref="SRgbCompanding"/>).
/// </summary>
SRgbCompand = 1 << 2,
}
}

20
src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats
{
internal static class PixelConversionModifiersExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) =>
(modifiers & expected) == expected;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PixelConversionModifiers Remove(
this PixelConversionModifiers modifiers,
PixelConversionModifiers removeThis) =>
modifiers & ~removeThis;
}
}

21
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs

@ -42,29 +42,16 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Argb32> destPixels)
internal override void FromVector4Destructive(Configuration configuration, Span<Vector4> sourceVectors, Span<Argb32> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale));
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Vector4> destVectors)
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Vector4> destVectors, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale));
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Argb32> destPixels)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Vector4> destVectors)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />
internal override void ToRgba32(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Rgba32> destPixels)
{

21
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs

@ -42,30 +42,17 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgr24> destPixels)
internal override void FromVector4Destructive(Configuration configuration, Span<Vector4> sourceVectors, Span<Bgr24> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply));
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Vector4> destVectors)
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Vector4> destVectors, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply));
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgr24> destPixels)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Vector4> destVectors)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />
internal override void ToArgb32(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Argb32> destPixels)
{

21
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs

@ -42,29 +42,16 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgra32> destPixels)
internal override void FromVector4Destructive(Configuration configuration, Span<Vector4> sourceVectors, Span<Bgra32> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale));
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Vector4> destVectors)
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Vector4> destVectors, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale));
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Bgra32> destPixels)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Vector4> destVectors)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />
internal override void ToRgba32(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Rgba32> destPixels)
{

21
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs

@ -42,30 +42,17 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Rgb24> destPixels)
internal override void FromVector4Destructive(Configuration configuration, Span<Vector4> sourceVectors, Span<Rgb24> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply));
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Vector4> destVectors)
internal override void ToVector4(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Vector4> destVectors, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply));
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<Rgb24> destPixels)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Vector4> destVectors)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
/// <inheritdoc />
internal override void ToArgb32(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Argb32> destPixels)
{

33
src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude

@ -22,6 +22,7 @@ using System.Runtime.InteropServices;
// Types with Rgba32-combatible to/from Vector4 conversion
static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" };
void GenerateDefaultSelfConversionMethods(string pixelType)
{
#>
@ -107,33 +108,25 @@ using System.Runtime.InteropServices;
<#+
}
void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType)
void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType, bool hasAlpha)
{
string removeTheseModifiers = "PixelConversionModifiers.Scale";
if (!hasAlpha)
{
removeTheseModifiers += " | PixelConversionModifiers.Premultiply";
}
#>
/// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors)
internal override void FromVector4(Configuration configuration, Span<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>));
}
/// <inheritdoc />
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels)
internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(<#=removeTheseModifiers#>));
}
/// <inheritdoc />
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors)
{
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
<#+
}
@ -143,7 +136,7 @@ using System.Runtime.InteropServices;
if (Rgba32CompatibleTypes.Contains(pixelType))
{
GenerateRgba32CompatibleVector4ConversionMethods(pixelType);
GenerateRgba32CompatibleVector4ConversionMethods(pixelType, pixelType.EndsWith("32"));
}
var matching32BitTypes = Optimized32BitTypes.Contains(pixelType) ?
@ -164,4 +157,4 @@ using System.Runtime.InteropServices;
GenerateDefaultConvertToMethod(pixelType, destPixelType);
}
}
#>
#>

34
src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs

@ -4,6 +4,8 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats.Utils;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats
@ -22,49 +24,33 @@ namespace SixLabors.ImageSharp.PixelFormats
internal override void ToVector4(
Configuration configuration,
ReadOnlySpan<Rgba32> sourcePixels,
Span<Vector4> destVectors)
Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
destVectors = destVectors.Slice(0, sourcePixels.Length);
SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(sourcePixels),
MemoryMarshal.Cast<Vector4, float>(destVectors));
Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers);
}
/// <inheritdoc />
internal override void FromVector4(
internal override void FromVector4Destructive(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<Rgba32> destPixels)
Span<Vector4> sourceVectors,
Span<Rgba32> destPixels,
PixelConversionModifiers modifiers)
{
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
destPixels = destPixels.Slice(0, sourceVectors.Length);
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
MemoryMarshal.Cast<Vector4, float>(sourceVectors),
MemoryMarshal.Cast<Rgba32, byte>(destPixels));
}
/// <inheritdoc />
internal override void ToScaledVector4(
Configuration configuration,
ReadOnlySpan<Rgba32> sourceColors,
Span<Vector4> destinationVectors)
{
this.ToVector4(configuration, sourceColors, destinationVectors);
}
/// <inheritdoc />
internal override void FromScaledVector4(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<Rgba32> destinationColors)
{
this.FromVector4(configuration, sourceVectors, destinationColors);
}
}
}
}

21
src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs

@ -5,6 +5,8 @@ using System;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats.Utils;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -18,32 +20,29 @@ namespace SixLabors.ImageSharp.PixelFormats
internal class PixelOperations : PixelOperations<RgbaVector>
{
/// <inheritdoc />
internal override void FromScaledVector4(
internal override void FromVector4Destructive(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<RgbaVector> destinationColors)
Span<Vector4> sourceVectors,
Span<RgbaVector> destinationColors,
PixelConversionModifiers modifiers)
{
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors));
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
MemoryMarshal.Cast<Vector4, RgbaVector>(sourceVectors).CopyTo(destinationColors);
}
/// <inheritdoc />
internal override void ToScaledVector4(
Configuration configuration,
ReadOnlySpan<RgbaVector> sourceColors,
Span<Vector4> destinationVectors)
=> this.ToVector4(configuration, sourceColors, destinationVectors);
/// <inheritdoc />
internal override void ToVector4(
Configuration configuration,
ReadOnlySpan<RgbaVector> sourcePixels,
Span<Vector4> destVectors)
Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
MemoryMarshal.Cast<RgbaVector, Vector4>(sourcePixels).CopyTo(destVectors);
Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers);
}
}
}

73
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -24,71 +24,70 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Bulk version of <see cref="IPixel.FromVector4"/> converting 'sourceVectors.Length' pixels into 'destinationColors'.
/// The method is DESTRUCTIVE altering the contents of <paramref name="sourceVectors"/>.
/// </summary>
/// <remarks>
/// The destructive behavior is a design choice for performance reasons.
/// In a typical use case the contents of <paramref name="sourceVectors"/> are abandoned after the conversion.
/// </remarks>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param>
/// <param name="sourceVectors">The <see cref="Span{T}"/> to the source vectors.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination colors.</param>
internal virtual void FromVector4(
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the conversion</param>
internal virtual void FromVector4Destructive(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels)
Span<Vector4> sourceVectors,
Span<TPixel> destPixels,
PixelConversionModifiers modifiers)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels);
Utils.Vector4Converters.Default.FromVector4(sourceVectors, destPixels, modifiers);
}
/// <summary>
/// Bulk version of <see cref="IPixel.FromVector4"/> converting 'sourceVectors.Length' pixels into 'destinationColors'.
/// The method is DESTRUCTIVE altering the contents of <paramref name="sourceVectors"/>.
/// </summary>
/// <remarks>
/// The destructive behavior is a design choice for performance reasons.
/// In a typical use case the contents of <paramref name="sourceVectors"/> are abandoned after the conversion.
/// </remarks>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param>
/// <param name="sourceVectors">The <see cref="Span{T}"/> to the source vectors.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination colors.</param>
internal void FromVector4Destructive(Configuration configuration, Span<Vector4> sourceVectors, Span<TPixel> destPixels) =>
this.FromVector4Destructive(configuration, sourceVectors, destPixels, PixelConversionModifiers.None);
/// <summary>
/// Bulk version of <see cref="IPixel.ToVector4()"/> converting 'sourceColors.Length' pixels into 'destinationVectors'.
/// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param>
/// <param name="sourcePixels">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="Span{T}"/> to the destination vectors.</param>
/// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the conversion</param>
internal virtual void ToVector4(
Configuration configuration,
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors)
Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors);
Utils.Vector4Converters.Default.ToVector4(sourcePixels, destVectors, modifiers);
}
/// <summary>
/// Bulk version of <see cref="IPixel.FromScaledVector4"/> converting 'sourceVectors.Length' pixels into 'destinationColors'.
/// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param>
/// <param name="sourceVectors">The <see cref="Span{T}"/> to the source vectors.</param>
/// <param name="destinationColors">The <see cref="Span{T}"/> to the destination colors.</param>
internal virtual void FromScaledVector4(
Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destinationColors)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors));
Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destinationColors);
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToScaledVector4()"/> converting 'sourceColors.Length' pixels into 'destinationVectors'.
/// Bulk version of <see cref="IPixel.ToVector4()"/> converting 'sourceColors.Length' pixels into 'destinationVectors'.
/// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destinationVectors">The <see cref="Span{T}"/> to the destination vectors.</param>
internal virtual void ToScaledVector4(
/// <param name="sourcePixels">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="Span{T}"/> to the destination vectors.</param>
internal virtual void ToVector4(
Configuration configuration,
ReadOnlySpan<TPixel> sourceColors,
Span<Vector4> destinationVectors)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors));
Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors);
}
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors) =>
this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None);
/// <summary>
/// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'.

74
src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs

@ -6,6 +6,8 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <summary>
@ -15,13 +17,75 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <summary>
/// Provides default implementations for batched to/from <see cref="Vector4"/> conversion.
/// WARNING: The methods are operating without bounds checking and input validation!
/// WARNING: The methods prefixed with "Unsafe" are operating without bounds checking and input validation!
/// Input validation is the responsibility of the caller!
/// </summary>
public static class Default
{
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousFromVector4<TPixel>(
public static void FromVector4<TPixel>(
Span<Vector4> sourceVectors,
Span<TPixel> destPixels,
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
UnsafeFromVector4(sourceVectors, destPixels, modifiers);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void ToVector4<TPixel>(
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
UnsafeToVector4(sourcePixels, destVectors, modifiers);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void UnsafeFromVector4<TPixel>(
Span<Vector4> sourceVectors,
Span<TPixel> destPixels,
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
ApplyBackwardConversionModifiers(sourceVectors, modifiers);
if (modifiers.IsDefined(PixelConversionModifiers.Scale))
{
UnsafeFromScaledVector4Core(sourceVectors, destPixels);
}
else
{
UnsafeFromVector4Core(sourceVectors, destPixels);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void UnsafeToVector4<TPixel>(
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
if (modifiers.IsDefined(PixelConversionModifiers.Scale))
{
UnsafeToScaledVector4Core(sourcePixels, destVectors);
}
else
{
UnsafeToVector4Core(sourcePixels, destVectors);
}
ApplyForwardConversionModifiers(destVectors, modifiers);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void UnsafeFromVector4Core<TPixel>(
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels)
where TPixel : struct, IPixel<TPixel>
@ -38,7 +102,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToVector4<TPixel>(
private static void UnsafeToVector4Core<TPixel>(
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors)
where TPixel : struct, IPixel<TPixel>
@ -55,7 +119,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousFromScaledVector4<TPixel>(
private static void UnsafeFromScaledVector4Core<TPixel>(
ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destinationColors)
where TPixel : struct, IPixel<TPixel>
@ -72,7 +136,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToScaledVector4<TPixel>(
private static void UnsafeToScaledVector4Core<TPixel>(
ReadOnlySpan<TPixel> sourceColors,
Span<Vector4> destinationVectors)
where TPixel : struct, IPixel<TPixel>

58
src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs

@ -7,6 +7,8 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
/// <content>
@ -29,11 +31,8 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
/// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.ToVector4"/>
/// and <see cref="PixelOperations{TPixel}.ToScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.ToVector4(SixLabors.ImageSharp.Configuration,System.ReadOnlySpan{TPixel},System.Span{System.Numerics.Vector4},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/>
/// The method works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void ToVector4<TPixel>(
@ -41,7 +40,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors,
bool scaled)
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(configuration, nameof(configuration));
@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
ToVector4Fallback(sourcePixels, destVectors, scaled);
Default.UnsafeToVector4(sourcePixels, destVectors, modifiers);
return;
}
@ -70,22 +69,22 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem)));
destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
// TODO: Investigate optimized 1-pass approach!
ApplyForwardConversionModifiers(destVectors, modifiers);
}
/// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.FromVector4"/>
/// and <see cref="PixelOperations{TPixel}.FromScaledVector4"/>
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.FromVector4Destructive(SixLabors.ImageSharp.Configuration,System.Span{System.Numerics.Vector4},System.Span{TPixel},SixLabors.ImageSharp.PixelFormats.PixelConversionModifiers)"/>
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void FromVector4<TPixel>(
Configuration configuration,
PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<Vector4> sourceVectors,
Span<Vector4> sourceVectors,
Span<TPixel> destPixels,
bool scaled)
PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(configuration, nameof(configuration));
@ -96,11 +95,14 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// Not worth for small buffers:
if (count < Vector4ConversionThreshold)
{
FromVector4Fallback(sourceVectors, destPixels, scaled);
Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers);
return;
}
// TODO: Investigate optimized 1-pass approach!
ApplyBackwardConversionModifiers(sourceVectors, modifiers);
// For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
// so let's allocate a temporary buffer as usually:
using (IMemoryOwner<Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate<Rgba32>(count))
@ -115,34 +117,6 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void ToVector4Fallback<TPixel>(ReadOnlySpan<TPixel> sourcePixels, Span<Vector4> destVectors, bool scaled)
where TPixel : struct, IPixel<TPixel>
{
if (scaled)
{
Default.DangerousToScaledVector4(sourcePixels, destVectors);
}
else
{
Default.DangerousToVector4(sourcePixels, destVectors);
}
}
[MethodImpl(InliningOptions.ColdPath)]
private static void FromVector4Fallback<TPixel>(ReadOnlySpan<Vector4> sourceVectors, Span<TPixel> destPixels, bool scaled)
where TPixel : struct, IPixel<TPixel>
{
if (scaled)
{
Default.DangerousFromScaledVector4(sourceVectors, destPixels);
}
else
{
Default.DangerousFromVector4(sourceVectors, destPixels);
}
}
private static int CalculateVector4ConversionThreshold()
{
if (!Vector.IsHardwareAccelerated)

48
src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats.Utils
{
internal static partial class Vector4Converters
{
/// <summary>
/// Apply modifiers used requested by ToVector4() conversion.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ApplyForwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers)
{
if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand))
{
SRgbCompanding.Expand(vectors);
}
if (modifiers.IsDefined(PixelConversionModifiers.Premultiply))
{
Vector4Utils.Premultiply(vectors);
}
}
/// <summary>
/// Apply modifiers used requested by FromVector4() conversion.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ApplyBackwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers)
{
if (modifiers.IsDefined(PixelConversionModifiers.Premultiply))
{
Vector4Utils.UnPremultiply(vectors);
}
if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand))
{
SRgbCompanding.Compress(vectors);
}
}
}
}

2
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve2D(in matrixY, in matrixX, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX);
}
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan);
}
});

2
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve(in matrix, sourcePixels, vectorSpan, y, x, maxY, maxX, startX);
}
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan);
}
});
}

2
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
DenseMatrixUtils.Convolve(in matrix, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX);
}
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan.Slice(0, length), targetRowSpan);
}
});

2
src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs

@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
if (this.paletteVector is null)
{
this.paletteVector = new Vector4[this.Palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(configuration, this.Palette, this.paletteVector);
PixelOperations<TPixel>.Instance.ToVector4(configuration, (ReadOnlySpan<TPixel>)this.Palette, (Span<Vector4>)this.paletteVector, PixelConversionModifiers.Scale);
}
}
}

2
src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
Vector4Utils.Transform(vectorSpan, ref matrix);
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan, rowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan);
}
});
}

2
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs

@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
// Collect the palette. Required before the second pass runs.
TPixel[] palette = this.GetPalette();
this.paletteVector = new Vector4[palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(image.Configuration, palette, this.paletteVector);
PixelOperations<TPixel>.Instance.ToVector4(image.Configuration, (ReadOnlySpan<TPixel>)palette, (Span<Vector4>)this.paletteVector, PixelConversionModifiers.Scale);
var quantizedFrame = new QuantizedFrame<TPixel>(image.MemoryAllocator, width, height, palette);
if (this.Dither)

2
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs

@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan);
}
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
}
});
}

2
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs

@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan);
}
PixelOperations<TPixel>.Instance.FromVector4(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
}
});
}

102
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -9,7 +9,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
@ -216,27 +215,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
for (int y = rows.Min; y < rows.Max; y++)
// Y coordinates of source points
Span<TPixel> sourceRow =
source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
Span<TPixel> targetRow = destination.GetPixelRowSpan(y);
for (int x = minX; x < maxX; x++)
{
// Y coordinates of source points
Span<TPixel> sourceRow =
source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
Span<TPixel> targetRow = destination.GetPixelRowSpan(y);
for (int x = minX; x < maxX; x++)
{
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
}
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
}
});
}
});
return;
}
int sourceHeight = source.Height;
PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply;
if (this.Compand)
{
conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand;
}
// Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY
@ -251,30 +256,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
processColsRect,
configuration,
(rows, tempRowBuffer) =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX);
Span<Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan);
Vector4Utils.Premultiply(tempRowSpan);
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX);
Span<Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX);
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers);
if (this.Compand)
{
SRgbCompanding.Expand(tempRowSpan);
}
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];
for (int x = minX; x < maxX; x++)
{
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) =
kernel.Convolve(tempRowSpan);
}
for (int x = minX; x < maxX; x++)
{
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan);
}
});
}
});
var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY);
@ -283,35 +281,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
processRowsRect,
configuration,
(rows, tempRowBuffer) =>
{
Span<Vector4> tempRowSpan = tempRowBuffer.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
// Ensure offsets are normalized for cropping and padding.
ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY);
{
Span<Vector4> tempRowSpan = tempRowBuffer.Span;
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);
for (int y = rows.Min; y < rows.Max; y++)
{
// Ensure offsets are normalized for cropping and padding.
ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY);
for (int x = 0; x < width; x++)
{
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);
// Destination color components
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
}
for (int x = 0; x < width; x++)
{
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);
Vector4Utils.UnPremultiply(tempRowSpan);
// Destination color components
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
}
if (this.Compand)
{
SRgbCompanding.Compress(tempRowSpan);
}
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan);
}
});
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers);
}
});
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs

@ -61,13 +61,13 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
[Benchmark]
public void PixelOperations_Base()
{
new PixelOperations<TPixel>().FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
new PixelOperations<TPixel>().FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
}
[Benchmark]
public void PixelOperations_Specialized()
{
PixelOperations<TPixel>.Instance.FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, this.source.GetSpan(), this.destination.GetSpan());
}
}

2
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks
destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]);
}
PixelOperations<TPixel>.Instance.FromVector4(this.Configuration, destinationSpan, destination);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, destinationSpan, destination);
}
}

155
tests/ImageSharp.Benchmarks/Samplers/Resize.cs

@ -14,11 +14,12 @@ using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Benchmarks
{
[Config(typeof(Config.ShortClr))]
public abstract class ResizeBenchmarkBase
public abstract class ResizeBenchmarkBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
protected readonly Configuration Configuration = new Configuration(new JpegConfigurationModule());
private Image<Rgba32> sourceImage;
private Image<TPixel> sourceImage;
private Bitmap sourceBitmap;
@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[GlobalSetup]
public void Setup()
{
this.sourceImage = new Image<Rgba32>(this.Configuration, this.SourceSize, this.SourceSize);
this.sourceImage = new Image<TPixel>(this.Configuration, this.SourceSize, this.SourceSize);
this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize);
}
@ -65,83 +66,137 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")]
public int ImageSharp_P1() => this.RunImageSharpResize(1);
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")]
public int ImageSharp_P4() => this.RunImageSharpResize(4);
// Parallel cases have been disabled for fast benchmark execution.
// Uncomment, if you are interested in parallel speedup
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")]
public int ImageSharp_P8() => this.RunImageSharpResize(8);
//[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")]
//public int ImageSharp_P4() => this.RunImageSharpResize(4);
//[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")]
//public int ImageSharp_P8() => this.RunImageSharpResize(8);
protected int RunImageSharpResize(int maxDegreeOfParallelism)
{
this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism;
using (Image<Rgba32> clone = this.sourceImage.Clone(this.ExecuteResizeOperation))
using (Image<TPixel> clone = this.sourceImage.Clone(this.ExecuteResizeOperation))
{
return clone.Width;
}
}
protected abstract void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx);
protected abstract void ExecuteResizeOperation(IImageProcessingContext<TPixel> ctx);
}
public class Resize_Bicubic : ResizeBenchmarkBase
public class Resize_Bicubic_Rgba32 : ResizeBenchmarkBase<Rgba32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
// RESULTS (2018 October):
// RESULTS (2019 April):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4)
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
//
// IterationCount=3 LaunchCount=1 WarmupCount=3
//
// Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
// ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:|
// SystemDrawing | Clr | Clr | 3032 | 400 | 118.71 ms | 4.884 ms | 0.2677 ms | 1.00 | - | - | - | 2048 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 94.55 ms | 16.160 ms | 0.8858 ms | 0.80 | - | - | - | 16384 B |
// | | | | | | | | | | | | |
// SystemDrawing | Core | Core | 3032 | 400 | 118.38 ms | 2.814 ms | 0.1542 ms | 1.00 | - | - | - | 96 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 90.28 ms | 4.679 ms | 0.2565 ms | 0.76 | - | - | - | 15712 B |
}
public class Resize_Bicubic_Bgra32 : ResizeBenchmarkBase<Bgra32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Bgra32> ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
// RESULTS (2019 April):
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B |
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4)
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
//
// IterationCount=3 LaunchCount=1 WarmupCount=3
//
// Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
// ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|------------:|------------:|------------:|--------------------:|
// SystemDrawing | Clr | Clr | 3032 | 400 | 119.01 ms | 18.513 ms | 1.0147 ms | 1.00 | - | - | - | 1638 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 104.71 ms | 16.078 ms | 0.8813 ms | 0.88 | - | - | - | 45056 B |
// | | | | | | | | | | | | |
// SystemDrawing | Core | Core | 3032 | 400 | 121.58 ms | 50.084 ms | 2.7453 ms | 1.00 | - | - | - | 96 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 96.96 ms | 7.899 ms | 0.4329 ms | 0.80 | - | - | - | 44512 B |
}
public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase<Rgb24>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgb24> ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
// RESULTS (2019 April):
//
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4)
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
//
// Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
// ----------------------------------------- |----- |-------- |----------- |--------- |----------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:|
// SystemDrawing | Clr | Clr | 3032 | 400 | 121.37 ms | 48.580 ms | 2.6628 ms | 1.00 | 0.00 | - | - | - | 2048 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 99.36 ms | 11.356 ms | 0.6224 ms | 0.82 | 0.02 | - | - | - | 45056 B |
// | | | | | | | | | | | | | |
// SystemDrawing | Core | Core | 3032 | 400 | 118.06 ms | 15.667 ms | 0.8587 ms | 1.00 | 0.00 | - | - | - | 96 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 92.47 ms | 5.683 ms | 0.3115 ms | 0.78 | 0.01 | - | - | - | 44512 B |
}
public class Resize_BicubicCompand : ResizeBenchmarkBase
public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase<Rgba32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true);
}
// RESULTS (2018 October):
// RESULTS (2019 April):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4)
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B |
// Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
//
// IterationCount=3 LaunchCount=1 WarmupCount=3
//
// Method | Job | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
// ----------------------------------------- |----- |-------- |----------- |--------- |---------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:|
// SystemDrawing | Clr | Clr | 3032 | 400 | 120.7 ms | 68.985 ms | 3.7813 ms | 1.00 | 0.00 | - | - | - | 1638 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | Clr | 3032 | 400 | 132.2 ms | 15.976 ms | 0.8757 ms | 1.10 | 0.04 | - | - | - | 16384 B |
// | | | | | | | | | | | | | |
// SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B |
}
}
}

20
tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs

@ -53,7 +53,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding
{
var rnd = new Random(42);
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
Vector4[] expected = source.Select(v => SRgbCompanding.Expand(v)).ToArray();
var expected = new Vector4[source.Length];
for (int i = 0; i < source.Length; i++)
{
Vector4 s = source[i];
ref Vector4 e = ref expected[i];
SRgbCompanding.Expand(ref s);
e = s;
}
SRgbCompanding.Expand(source);
@ -68,7 +76,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding
{
var rnd = new Random(42);
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1);
Vector4[] expected = source.Select(v => SRgbCompanding.Compress(v)).ToArray();
var expected = new Vector4[source.Length];
for (int i = 0; i < source.Length; i++)
{
Vector4 s = source[i];
ref Vector4 e = ref expected[i];
SRgbCompanding.Compress(ref s);
e = s;
}
SRgbCompanding.Compress(source);

1
tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs

@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
public Bgr24OperationsTests(ITestOutputHelper output)
: base(output)
{
this.HasAlpha = false;
}
[Fact]

24
tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb24OperationsTests.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
{
public partial class PixelOperationsTests
{
public class Rgb24OperationsTests : PixelOperationsTests<Rgb24>
{
public Rgb24OperationsTests(ITestOutputHelper output)
: base(output)
{
this.HasAlpha = false;
}
[Fact]
public void IsSpecialImplementation() => Assert.IsType<Rgb24.PixelOperations>(PixelOperations<Rgb24>.Instance);
}
}
}

308
tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs

@ -6,7 +6,7 @@ using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -33,6 +33,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
null;
#endif
protected bool HasAlpha { get; set; } = true;
protected PixelOperationsTests(ITestOutputHelper output)
: base(output)
{
@ -70,25 +72,33 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
internal static PixelOperations<TPixel> Operations => PixelOperations<TPixel>.Instance;
internal static TPixel[] CreateExpectedPixelData(Vector4[] source)
internal static TPixel[] CreateExpectedPixelData(Vector4[] source, RefAction<Vector4> vectorModifier = null)
{
var expected = new TPixel[source.Length];
for (int i = 0; i < expected.Length; i++)
{
expected[i].FromVector4(source[i]);
Vector4 v = source[i];
vectorModifier?.Invoke(ref v);
expected[i].FromVector4(v);
}
return expected;
}
internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source)
internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source, RefAction<Vector4> vectorModifier = null)
{
var expected = new TPixel[source.Length];
for (int i = 0; i < expected.Length; i++)
{
expected[i].FromScaledVector4(source[i]);
Vector4 v = source[i];
vectorModifier?.Invoke(ref v);
expected[i].FromScaledVector4(v);
}
return expected;
}
@ -102,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
TestOperation(
source,
expected,
(s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan())
(s, d) => Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan())
);
}
@ -116,7 +126,140 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
TestOperation(
source,
expected,
(s, d) => Operations.FromScaledVector4(this.Configuration, s, d.GetSpan())
(s, d) =>
{
Span<TPixel> destPixels = d.GetSpan();
Operations.FromVector4Destructive(this.Configuration, (Span<Vector4>)s, destPixels, PixelConversionModifiers.Scale);
});
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void FromCompandedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
SRgbCompanding.Expand(ref v);
}
void expectedAction(ref Vector4 v)
{
SRgbCompanding.Compress(ref v);
}
Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v));
TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.FromVector4Destructive(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void FromPremultipliedVector4(int count)
{
void sourceAction(ref Vector4 v)
{
if (this.HasAlpha)
{
Vector4Utils.Premultiply(ref v);
}
}
void expectedAction(ref Vector4 v)
{
if (this.HasAlpha)
{
Vector4Utils.UnPremultiply(ref v);
}
}
Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v));
TPixel[] expected = CreateExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.FromVector4Destructive(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void FromPremultipliedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
if (this.HasAlpha)
{
Vector4Utils.Premultiply(ref v);
}
}
void expectedAction(ref Vector4 v)
{
if (this.HasAlpha)
{
Vector4Utils.UnPremultiply(ref v);
}
}
Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v));
TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.FromVector4Destructive(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void FromCompandedPremultipliedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
SRgbCompanding.Expand(ref v);
if (this.HasAlpha)
{
Vector4Utils.Premultiply(ref v);
}
}
void expectedAction(ref Vector4 v)
{
if (this.HasAlpha)
{
Vector4Utils.UnPremultiply(ref v);
}
SRgbCompanding.Compress(ref v);
}
Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => sourceAction(ref v));
TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.FromVector4Destructive(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale)
);
}
@ -144,7 +287,119 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
TestOperation(
source,
expected,
(s, d) => Operations.ToScaledVector4(this.Configuration, s, d.GetSpan())
(s, d) =>
{
Span<Vector4> destVectors = d.GetSpan();
Operations.ToVector4(this.Configuration, (ReadOnlySpan<TPixel>)s, destVectors, PixelConversionModifiers.Scale);
});
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToCompandedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
SRgbCompanding.Compress(ref v);
}
void expectedAction(ref Vector4 v)
{
SRgbCompanding.Expand(ref v);
}
TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v));
Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.ToVector4(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Scale)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToPremultipliedVector4(int count)
{
void sourceAction(ref Vector4 v)
{
Vector4Utils.UnPremultiply(ref v);
}
void expectedAction(ref Vector4 v)
{
Vector4Utils.Premultiply(ref v);
}
TPixel[] source = CreatePixelTestData(count, (ref Vector4 v) => sourceAction(ref v));
Vector4[] expected = CreateExpectedVector4Data(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.ToVector4(this.Configuration, s, d.GetSpan(), PixelConversionModifiers.Premultiply)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToPremultipliedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
Vector4Utils.UnPremultiply(ref v);
}
void expectedAction(ref Vector4 v)
{
Vector4Utils.Premultiply(ref v);
}
TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v));
Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.ToVector4(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale));
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToCompandedPremultipliedScaledVector4(int count)
{
void sourceAction(ref Vector4 v)
{
Vector4Utils.UnPremultiply(ref v);
SRgbCompanding.Compress(ref v);
}
void expectedAction(ref Vector4 v)
{
SRgbCompanding.Expand(ref v);
Vector4Utils.Premultiply(ref v);
}
TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => sourceAction(ref v));
Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => expectedAction(ref v));
TestOperation(
source,
expected,
(s, d) => Operations.ToVector4(
this.Configuration,
s,
d.GetSpan(),
PixelConversionModifiers.SRgbCompand | PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale)
);
}
@ -477,25 +732,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
);
}
internal static Vector4[] CreateExpectedVector4Data(TPixel[] source)
public delegate void RefAction<T1>(ref T1 arg1);
internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction<Vector4> vectorModifier = null)
{
var expected = new Vector4[source.Length];
for (int i = 0; i < expected.Length; i++)
{
expected[i] = source[i].ToVector4();
var v = source[i].ToVector4();
vectorModifier?.Invoke(ref v);
expected[i] = v;
}
return expected;
}
internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source)
internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source, RefAction<Vector4> vectorModifier = null)
{
var expected = new Vector4[source.Length];
for (int i = 0; i < expected.Length; i++)
{
expected[i] = source[i].ToScaledVector4();
Vector4 v = source[i].ToScaledVector4();
vectorModifier?.Invoke(ref v);
expected[i] = v;
}
return expected;
}
@ -513,19 +780,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
}
}
internal static Vector4[] CreateVector4TestData(int length)
internal static Vector4[] CreateVector4TestData(int length, RefAction<Vector4> vectorModifier = null)
{
var result = new Vector4[length];
var rnd = new Random(42); // Deterministic random values
for (int i = 0; i < result.Length; i++)
{
result[i] = GetVector(rnd);
Vector4 v = GetVector(rnd);
vectorModifier?.Invoke(ref v);
result[i] = v;
}
return result;
}
internal static TPixel[] CreatePixelTestData(int length)
internal static TPixel[] CreatePixelTestData(int length, RefAction<Vector4> vectorModifier = null)
{
var result = new TPixel[length];
@ -534,13 +804,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
for (int i = 0; i < result.Length; i++)
{
Vector4 v = GetVector(rnd);
vectorModifier?.Invoke(ref v);
result[i].FromVector4(v);
}
return result;
}
internal static TPixel[] CreateScaledPixelTestData(int length)
internal static TPixel[] CreateScaledPixelTestData(int length, RefAction<Vector4> vectorModifier = null)
{
var result = new TPixel[length];
@ -549,6 +822,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
for (int i = 0; i < result.Length; i++)
{
Vector4 v = GetVector(rnd);
vectorModifier?.Invoke(ref v);
result[i].FromScaledVector4(v);
}

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

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests
{
Span<TPixel> pixelSpan = frame.GetPixelSpan();
PixelOperations<TPixel>.Instance.ToScaledVector4(configuration, pixelSpan, tempSpan);
PixelOperations<TPixel>.Instance.ToVector4(configuration, pixelSpan, tempSpan, PixelConversionModifiers.Scale);
for (int i = 0; i < tempSpan.Length; i++)
{
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
v.W = 1F;
}
PixelOperations<TPixel>.Instance.FromScaledVector4(configuration, tempSpan, pixelSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, tempSpan, pixelSpan, PixelConversionModifiers.Scale);
}
}
});

2
tests/Images/External

@ -1 +1 @@
Subproject commit c4098e463ab0e7128ae196b7f963369271df8fd3
Subproject commit 9d71985545bb827270ca34af3f78e4c220560ef1
Loading…
Cancel
Save