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_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_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_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_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_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@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++) for (int i = 0; i < vectors.Length; i++)
{ {
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Expand(v.X); Expand(ref v);
v.Y = Expand(v.Y);
v.Z = Expand(v.Z);
} }
} }
@ -48,9 +46,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
for (int i = 0; i < vectors.Length; i++) for (int i = 0; i < vectors.Length; i++)
{ {
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Compress(v.X); Compress(ref v);
v.Y = Compress(v.Y);
v.Z = Compress(v.Z);
} }
} }
@ -58,17 +54,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Expands a companded vector to its linear equivalent with respect to the energy. /// Expands a companded vector to its linear equivalent with respect to the energy.
/// </summary> /// </summary>
/// <param name="vector">The vector.</param> /// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the linear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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> /// <summary>
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent. /// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
/// </summary> /// </summary>
/// <param name="vector">The vector.</param> /// <param name="vector">The vector.</param>
/// <returns>The <see cref="Vector4"/> representing the nonlinear channel values.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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> /// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy. /// 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> /// <summary>
/// Verifies, that the `source` span has the length of 'minLength', or longer. /// Verifies, that the `source` span has the length of 'minLength', or longer.
/// </summary> /// </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); Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
// TODO: Investigate if slicing is actually necessary // 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> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
PixelOperations<TPixel>.Instance.ToScaledVector4( ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
configuration, PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
background.Slice(0, background.Length), ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
backgroundSpan); PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(
configuration,
source.Slice(0, background.Length),
sourceSpan);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.FromScaledVector4( Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
configuration, PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
destinationSpan.Slice(0, background.Length),
destination);
} }
} }
@ -161,21 +155,15 @@ namespace SixLabors.ImageSharp.PixelFormats
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
PixelOperations<TPixel>.Instance.ToScaledVector4( ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
configuration, PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
background.Slice(0, background.Length), ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
backgroundSpan); PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(
configuration,
source.Slice(0, background.Length),
sourceSpan);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.FromScaledVector4( Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
configuration, PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
destinationSpan.Slice(0, background.Length),
destination);
} }
} }
} }

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 /> /// <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 /> /// <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 /> /// <inheritdoc />
internal override void ToRgba32(Configuration configuration, ReadOnlySpan<Argb32> sourcePixels, Span<Rgba32> destPixels) 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 /> /// <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 /> /// <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 /> /// <inheritdoc />
internal override void ToArgb32(Configuration configuration, ReadOnlySpan<Bgr24> sourcePixels, Span<Argb32> destPixels) 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 /> /// <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 /> /// <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 /> /// <inheritdoc />
internal override void ToRgba32(Configuration configuration, ReadOnlySpan<Bgra32> sourcePixels, Span<Rgba32> destPixels) 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 /> /// <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 /> /// <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 /> /// <inheritdoc />
internal override void ToArgb32(Configuration configuration, ReadOnlySpan<Rgb24> sourcePixels, Span<Argb32> destPixels) 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 // Types with Rgba32-combatible to/from Vector4 conversion
static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" }; static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" };
void GenerateDefaultSelfConversionMethods(string pixelType) 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 /> /// <inheritdoc />
internal override void FromVector4(Configuration configuration, ReadOnlySpan<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels) internal override void FromVector4(Configuration configuration, Span<Vector4> sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
/// <inheritdoc />
internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<Vector4> destVectors)
{ {
Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>));
} }
/// <inheritdoc /> /// <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)) if (Rgba32CompatibleTypes.Contains(pixelType))
{ {
GenerateRgba32CompatibleVector4ConversionMethods(pixelType); GenerateRgba32CompatibleVector4ConversionMethods(pixelType, pixelType.EndsWith("32"));
} }
var matching32BitTypes = Optimized32BitTypes.Contains(pixelType) ? var matching32BitTypes = Optimized32BitTypes.Contains(pixelType) ?
@ -164,4 +157,4 @@ using System.Runtime.InteropServices;
GenerateDefaultConvertToMethod(pixelType, destPixelType); GenerateDefaultConvertToMethod(pixelType, destPixelType);
} }
} }
#> #>

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

@ -4,6 +4,8 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats.Utils;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
@ -22,49 +24,33 @@ namespace SixLabors.ImageSharp.PixelFormats
internal override void ToVector4( internal override void ToVector4(
Configuration configuration, Configuration configuration,
ReadOnlySpan<Rgba32> sourcePixels, ReadOnlySpan<Rgba32> sourcePixels,
Span<Vector4> destVectors) Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{ {
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
destVectors = destVectors.Slice(0, sourcePixels.Length); destVectors = destVectors.Slice(0, sourcePixels.Length);
SimdUtils.BulkConvertByteToNormalizedFloat( SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(sourcePixels), MemoryMarshal.Cast<Rgba32, byte>(sourcePixels),
MemoryMarshal.Cast<Vector4, float>(destVectors)); MemoryMarshal.Cast<Vector4, float>(destVectors));
Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers);
} }
/// <inheritdoc /> /// <inheritdoc />
internal override void FromVector4( internal override void FromVector4Destructive(
Configuration configuration, Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors, Span<Vector4> sourceVectors,
Span<Rgba32> destPixels) Span<Rgba32> destPixels,
PixelConversionModifiers modifiers)
{ {
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
destPixels = destPixels.Slice(0, sourceVectors.Length); destPixels = destPixels.Slice(0, sourceVectors.Length);
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
MemoryMarshal.Cast<Vector4, float>(sourceVectors), MemoryMarshal.Cast<Vector4, float>(sourceVectors),
MemoryMarshal.Cast<Rgba32, byte>(destPixels)); 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.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats.Utils;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -18,32 +20,29 @@ namespace SixLabors.ImageSharp.PixelFormats
internal class PixelOperations : PixelOperations<RgbaVector> internal class PixelOperations : PixelOperations<RgbaVector>
{ {
/// <inheritdoc /> /// <inheritdoc />
internal override void FromScaledVector4( internal override void FromVector4Destructive(
Configuration configuration, Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors, Span<Vector4> sourceVectors,
Span<RgbaVector> destinationColors) Span<RgbaVector> destinationColors,
PixelConversionModifiers modifiers)
{ {
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors));
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
MemoryMarshal.Cast<Vector4, RgbaVector>(sourceVectors).CopyTo(destinationColors); 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 /> /// <inheritdoc />
internal override void ToVector4( internal override void ToVector4(
Configuration configuration, Configuration configuration,
ReadOnlySpan<RgbaVector> sourcePixels, ReadOnlySpan<RgbaVector> sourcePixels,
Span<Vector4> destVectors) Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{ {
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
MemoryMarshal.Cast<RgbaVector, Vector4>(sourcePixels).CopyTo(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> /// <summary>
/// Bulk version of <see cref="IPixel.FromVector4"/> converting 'sourceVectors.Length' pixels into 'destinationColors'. /// 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> /// </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="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="sourceVectors">The <see cref="Span{T}"/> to the source vectors.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination colors.</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, Configuration configuration,
ReadOnlySpan<Vector4> sourceVectors, Span<Vector4> sourceVectors,
Span<TPixel> destPixels) Span<TPixel> destPixels,
PixelConversionModifiers modifiers)
{ {
Guard.NotNull(configuration, nameof(configuration)); 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> /// <summary>
/// Bulk version of <see cref="IPixel.ToVector4()"/> converting 'sourceColors.Length' pixels into 'destinationVectors'. /// Bulk version of <see cref="IPixel.ToVector4()"/> converting 'sourceColors.Length' pixels into 'destinationVectors'.
/// </summary> /// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param> /// <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="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="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( internal virtual void ToVector4(
Configuration configuration, Configuration configuration,
ReadOnlySpan<TPixel> sourcePixels, ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors) Span<Vector4> destVectors,
PixelConversionModifiers modifiers)
{ {
Guard.NotNull(configuration, nameof(configuration)); 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> /// <summary>
/// Bulk version of <see cref="IPixel.FromScaledVector4"/> converting 'sourceVectors.Length' pixels into 'destinationColors'. /// 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="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'.
/// </summary> /// </summary>
/// <param name="configuration">A <see cref="Configuration"/> to configure internal operations</param> /// <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="sourcePixels">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destinationVectors">The <see cref="Span{T}"/> to the destination vectors.</param> /// <param name="destVectors">The <see cref="Span{T}"/> to the destination vectors.</param>
internal virtual void ToScaledVector4( internal virtual void ToVector4(
Configuration configuration, Configuration configuration,
ReadOnlySpan<TPixel> sourceColors, ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destinationVectors) Span<Vector4> destVectors) =>
{ this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None);
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors));
Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors);
}
/// <summary> /// <summary>
/// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'. /// 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats.Utils namespace SixLabors.ImageSharp.PixelFormats.Utils
{ {
/// <summary> /// <summary>
@ -15,13 +17,75 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
{ {
/// <summary> /// <summary>
/// Provides default implementations for batched to/from <see cref="Vector4"/> conversion. /// 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! /// Input validation is the responsibility of the caller!
/// </summary> /// </summary>
public static class Default public static class Default
{ {
[MethodImpl(InliningOptions.ShortMethod)] [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, ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destPixels) Span<TPixel> destPixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -38,7 +102,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToVector4<TPixel>( private static void UnsafeToVector4Core<TPixel>(
ReadOnlySpan<TPixel> sourcePixels, ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors) Span<Vector4> destVectors)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -55,7 +119,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousFromScaledVector4<TPixel>( private static void UnsafeFromScaledVector4Core<TPixel>(
ReadOnlySpan<Vector4> sourceVectors, ReadOnlySpan<Vector4> sourceVectors,
Span<TPixel> destinationColors) Span<TPixel> destinationColors)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -72,7 +136,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void DangerousToScaledVector4<TPixel>( private static void UnsafeToScaledVector4Core<TPixel>(
ReadOnlySpan<TPixel> sourceColors, ReadOnlySpan<TPixel> sourceColors,
Span<Vector4> destinationVectors) Span<Vector4> destinationVectors)
where TPixel : struct, IPixel<TPixel> 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
namespace SixLabors.ImageSharp.PixelFormats.Utils namespace SixLabors.ImageSharp.PixelFormats.Utils
{ {
/// <content> /// <content>
@ -29,11 +31,8 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
/// <summary> /// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.ToVector4"/> /// 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)"/>
/// and <see cref="PixelOperations{TPixel}.ToScaledVector4"/> /// The method works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// 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!
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void ToVector4<TPixel>( internal static void ToVector4<TPixel>(
@ -41,7 +40,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
PixelOperations<TPixel> pixelOperations, PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<TPixel> sourcePixels, ReadOnlySpan<TPixel> sourcePixels,
Span<Vector4> destVectors, Span<Vector4> destVectors,
bool scaled) PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// Not worth for small buffers: // Not worth for small buffers:
if (count < Vector4ConversionThreshold) if (count < Vector4ConversionThreshold)
{ {
ToVector4Fallback(sourcePixels, destVectors, scaled); Default.UnsafeToVector4(sourcePixels, destVectors, modifiers);
return; return;
} }
@ -70,22 +69,22 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem))); MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem)));
destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
// TODO: Investigate optimized 1-pass approach!
ApplyForwardConversionModifiers(destVectors, modifiers);
} }
/// <summary> /// <summary>
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.FromVector4"/> /// 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)"/>
/// 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"/>.
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type! /// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void FromVector4<TPixel>( internal static void FromVector4<TPixel>(
Configuration configuration, Configuration configuration,
PixelOperations<TPixel> pixelOperations, PixelOperations<TPixel> pixelOperations,
ReadOnlySpan<Vector4> sourceVectors, Span<Vector4> sourceVectors,
Span<TPixel> destPixels, Span<TPixel> destPixels,
bool scaled) PixelConversionModifiers modifiers)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
@ -96,11 +95,14 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// Not worth for small buffers: // Not worth for small buffers:
if (count < Vector4ConversionThreshold) if (count < Vector4ConversionThreshold)
{ {
FromVector4Fallback(sourceVectors, destPixels, scaled); Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers);
return; 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, // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
// so let's allocate a temporary buffer as usually: // so let's allocate a temporary buffer as usually:
using (IMemoryOwner<Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate<Rgba32>(count)) 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() private static int CalculateVector4ConversionThreshold()
{ {
if (!Vector.IsHardwareAccelerated) 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); 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); 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); 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) if (this.paletteVector is null)
{ {
this.paletteVector = new Vector4[this.Palette.Length]; 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); 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. // Collect the palette. Required before the second pass runs.
TPixel[] palette = this.GetPalette(); TPixel[] palette = this.GetPalette();
this.paletteVector = new Vector4[palette.Length]; 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); var quantizedFrame = new QuantizedFrame<TPixel>(image.MemoryAllocator, width, height, palette);
if (this.Dither) 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); 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); 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 System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -216,27 +215,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
workingRect, workingRect,
configuration, configuration,
rows => 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 // X coordinates of source points
Span<TPixel> sourceRow = targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
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)];
}
} }
}); }
});
return; return;
} }
int sourceHeight = source.Height; int sourceHeight = source.Height;
PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply;
if (this.Compand)
{
conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand;
}
// Interpolate the image using the calculated weights. // Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // 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 // 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, processColsRect,
configuration, configuration,
(rows, tempRowBuffer) => (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);
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);
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers);
if (this.Compand) ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];
{
SRgbCompanding.Expand(tempRowSpan);
}
for (int x = minX; x < maxX; x++) for (int x = minX; x < maxX; x++)
{ {
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan);
kernel.Convolve(tempRowSpan);
}
} }
}); }
});
var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY);
@ -283,35 +281,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
processRowsRect, processRowsRect,
configuration, configuration,
(rows, tempRowBuffer) => (rows, tempRowBuffer) =>
{ {
Span<Vector4> tempRowSpan = tempRowBuffer.Span; 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);
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++) ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);
{
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);
// Destination color components for (int x = 0; x < width; x++)
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); {
} 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) Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
{
SRgbCompanding.Compress(tempRowSpan);
}
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y); PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers);
PixelOperations<TPixel>.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan); }
} });
});
} }
} }

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

@ -61,13 +61,13 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
[Benchmark] [Benchmark]
public void PixelOperations_Base() 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] [Benchmark]
public void PixelOperations_Specialized() 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]); 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 namespace SixLabors.ImageSharp.Benchmarks
{ {
[Config(typeof(Config.ShortClr))] [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()); protected readonly Configuration Configuration = new Configuration(new JpegConfigurationModule());
private Image<Rgba32> sourceImage; private Image<TPixel> sourceImage;
private Bitmap sourceBitmap; private Bitmap sourceBitmap;
@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() 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); this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize);
} }
@ -65,83 +66,137 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")]
public int ImageSharp_P1() => this.RunImageSharpResize(1); public int ImageSharp_P1() => this.RunImageSharpResize(1);
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] // Parallel cases have been disabled for fast benchmark execution.
public int ImageSharp_P4() => this.RunImageSharpResize(4); // Uncomment, if you are interested in parallel speedup
[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] //[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")]
public int ImageSharp_P8() => this.RunImageSharpResize(8); //public int ImageSharp_P4() => this.RunImageSharpResize(4);
//[Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")]
//public int ImageSharp_P8() => this.RunImageSharpResize(8);
protected int RunImageSharpResize(int maxDegreeOfParallelism) protected int RunImageSharpResize(int maxDegreeOfParallelism)
{ {
this.Configuration.MaxDegreeOfParallelism = 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; 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) protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{ {
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); 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 // 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 // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.403 // .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT // 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 | // 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
// SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B | // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B | // .NET Core SDK=2.1.602
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B | // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B | // 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
// 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 | // IterationCount=3 LaunchCount=1 WarmupCount=3
// '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 | // 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) protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{ {
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); 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 // 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 // Frequency=2742192 Hz, Resolution=364.6718 ns, Timer=TSC
// .NET Core SDK=2.1.403 // .NET Core SDK=2.1.602
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT // [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 // Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3362.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT // Core : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT
// //
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated | // IterationCount=3 LaunchCount=1 WarmupCount=3
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:| //
// SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B | // 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 |
// '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 | // SystemDrawing | Clr | Clr | 3032 | 400 | 120.7 ms | 68.985 ms | 3.7813 ms | 1.00 | 0.00 | - | - | - | 1638 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 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 | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 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 | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B | // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 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 |
} }
} }

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

@ -53,7 +53,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding
{ {
var rnd = new Random(42); var rnd = new Random(42);
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); 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); SRgbCompanding.Expand(source);
@ -68,7 +76,15 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding
{ {
var rnd = new Random(42); var rnd = new Random(42);
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); 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); 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) public Bgr24OperationsTests(ITestOutputHelper output)
: base(output) : base(output)
{ {
this.HasAlpha = false;
} }
[Fact] [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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -33,6 +33,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
null; null;
#endif #endif
protected bool HasAlpha { get; set; } = true;
protected PixelOperationsTests(ITestOutputHelper output) protected PixelOperationsTests(ITestOutputHelper output)
: base(output) : base(output)
{ {
@ -70,25 +72,33 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
internal static PixelOperations<TPixel> Operations => PixelOperations<TPixel>.Instance; 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]; var expected = new TPixel[source.Length];
for (int i = 0; i < expected.Length; i++) 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; return expected;
} }
internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source) internal static TPixel[] CreateScaledExpectedPixelData(Vector4[] source, RefAction<Vector4> vectorModifier = null)
{ {
var expected = new TPixel[source.Length]; var expected = new TPixel[source.Length];
for (int i = 0; i < expected.Length; i++) 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; return expected;
} }
@ -102,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
TestOperation( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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( TestOperation(
source, source,
expected, 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]; var expected = new Vector4[source.Length];
for (int i = 0; i < expected.Length; i++) 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; return expected;
} }
internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source) internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source, RefAction<Vector4> vectorModifier = null)
{ {
var expected = new Vector4[source.Length]; var expected = new Vector4[source.Length];
for (int i = 0; i < expected.Length; i++) 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; 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 result = new Vector4[length];
var rnd = new Random(42); // Deterministic random values var rnd = new Random(42); // Deterministic random values
for (int i = 0; i < result.Length; i++) 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; return result;
} }
internal static TPixel[] CreatePixelTestData(int length) internal static TPixel[] CreatePixelTestData(int length, RefAction<Vector4> vectorModifier = null)
{ {
var result = new TPixel[length]; var result = new TPixel[length];
@ -534,13 +804,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
Vector4 v = GetVector(rnd); Vector4 v = GetVector(rnd);
vectorModifier?.Invoke(ref v);
result[i].FromVector4(v); result[i].FromVector4(v);
} }
return result; return result;
} }
internal static TPixel[] CreateScaledPixelTestData(int length) internal static TPixel[] CreateScaledPixelTestData(int length, RefAction<Vector4> vectorModifier = null)
{ {
var result = new TPixel[length]; var result = new TPixel[length];
@ -549,6 +822,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
Vector4 v = GetVector(rnd); Vector4 v = GetVector(rnd);
vectorModifier?.Invoke(ref v);
result[i].FromScaledVector4(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(); 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++) for (int i = 0; i < tempSpan.Length; i++)
{ {
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
v.W = 1F; 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