diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
index e6b8922e9..6449351cc 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
///
internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
///
internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
///
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
///
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
///
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
index 9b1740013..9232cf454 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
///
internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
///
internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
///
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
///
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
index 37d6b72d7..4f56b75c5 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
///
internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
///
internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
///
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
///
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
///
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
index 638db1d0d..81882185d 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
index 6bf0693c2..f6678a4f8 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
index 6ff87eb38..aae8b2f63 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@@ -45,25 +46,25 @@ namespace SixLabors.ImageSharp.PixelFormats
///
internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
///
internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
///
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
///
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
index 9b4584d76..c828053a4 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
index 8e15ca1f4..9c29bd044 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
index caaba7809..db9cb84be 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
@@ -3,6 +3,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
index 4501f4972..cc8cb0e2f 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
@@ -8,6 +8,7 @@
//
+using SixLabors.ImageSharp.PixelFormats.Utils;
using System;
using System.Buffers;
using System.Numerics;
@@ -114,25 +115,25 @@ using System.Runtime.InteropServices;
///
internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false);
}
///
internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false);
}
///
internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels)
{
- this.RunRgba32CompatibleFromVector4Conversion(configuration, sourceVectors, destPixels);
+ Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true);
}
///
internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors)
{
- this.RunRgba32CompatibleToVector4Conversion(configuration, sourcePixels, destVectors);
+ Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true);
}
<#+
diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
index 9238e1b47..f4eb19be3 100644
--- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
@@ -17,12 +17,6 @@ namespace SixLabors.ImageSharp.PixelFormats
public partial class PixelOperations
where TPixel : struct, IPixel
{
- ///
- /// It's not worth to bother the transitive pixel conversion method below this limit.
- /// The value depends on the actual gain brought by the SIMD characteristics of the executing CPU and JIT.
- ///
- private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
-
///
/// Gets the global instance for the pixel type
///
@@ -42,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
- FromVector4DefaultImpl(sourceVectors, destPixels);
+ Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels);
}
///
@@ -59,7 +53,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
- ToVector4DefaultImpl(sourcePixels, destVectors);
+ Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors);
}
///
@@ -76,15 +70,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors));
- ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
- ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
-
- for (int i = 0; i < sourceVectors.Length; i++)
- {
- ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
- ref TPixel dp = ref Unsafe.Add(ref destRef, i);
- dp.FromScaledVector4(sp);
- }
+ Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destinationColors);
}
///
@@ -101,15 +87,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration));
Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors));
- ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
- ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors);
-
- for (int i = 0; i < sourceColors.Length; i++)
- {
- ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
- ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
- dp = sp.ToScaledVector4();
- }
+ Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors);
}
///
@@ -170,122 +148,5 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromScaledVector4(sp.ToScaledVector4());
}
}
-
- // TODO: The Vector4 helpers should be moved to a utility class.
-
- ///
- /// Provides an efficient default implementation for and
- /// which is applicable for -compatible pixel types where
- /// returns the same scaled result as .
- /// The method is works by internally converting to a therefore it's not applicable for that type!
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- internal void RunRgba32CompatibleToVector4Conversion(
- Configuration configuration,
- ReadOnlySpan sourcePixels,
- Span destVectors)
- {
- Guard.NotNull(configuration, nameof(configuration));
- Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
-
- int count = sourcePixels.Length;
-
- // Not worth for small buffers:
- if (count < Vector4ConversionThreshold)
- {
- ToVector4DefaultImpl(sourcePixels, destVectors);
- return;
- }
-
- // Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation:
- int countWithoutLastItem = count - 1;
- ReadOnlySpan reducedSource = sourcePixels.Slice(0, countWithoutLastItem);
- Span lastQuarterOfDestBuffer = MemoryMarshal.Cast(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
- this.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
-
- // 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
- // but we are always reading/writing at different positions:
- SimdUtils.BulkConvertByteToNormalizedFloat(
- MemoryMarshal.Cast(lastQuarterOfDestBuffer),
- MemoryMarshal.Cast(destVectors.Slice(0, countWithoutLastItem)));
-
- destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
- }
-
- ///
- /// Provides an efficient default implementation for and
- /// which is applicable for -compatible pixel types where
- /// returns the same scaled result as .
- /// The method is works by internally converting to a therefore it's not applicable for that type!
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- internal void RunRgba32CompatibleFromVector4Conversion(
- Configuration configuration,
- ReadOnlySpan sourceVectors,
- Span destPixels)
- {
- Guard.NotNull(configuration, nameof(configuration));
- Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
-
- int count = sourceVectors.Length;
-
- // Not worth for small buffers:
- if (count < Vector4ConversionThreshold)
- {
- FromVector4DefaultImpl(sourceVectors, destPixels);
- return;
- }
-
- // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
- // so let's allocate a temporary buffer as usually:
- using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count))
- {
- Span tempSpan = tempBuffer.Memory.Span;
-
- SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
- MemoryMarshal.Cast(sourceVectors),
- MemoryMarshal.Cast(tempSpan));
-
- this.FromRgba32(configuration, tempSpan, destPixels);
- }
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static void FromVector4DefaultImpl(ReadOnlySpan sourceVectors, Span destPixels)
- {
- ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
- ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
-
- for (int i = 0; i < sourceVectors.Length; i++)
- {
- ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
- ref TPixel dp = ref Unsafe.Add(ref destRef, i);
- dp.FromVector4(sp);
- }
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static void ToVector4DefaultImpl(ReadOnlySpan sourcePixels, Span destVectors)
- {
- ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels);
- ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors);
-
- for (int i = 0; i < sourcePixels.Length; i++)
- {
- ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
- ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
- dp = sp.ToVector4();
- }
- }
-
- private static int CalculateVector4ConversionThreshold()
- {
- if (!Vector.IsHardwareAccelerated)
- {
- return int.MaxValue;
- }
-
- return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128;
- }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/PixelConverter.cs b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
similarity index 98%
rename from src/ImageSharp/PixelFormats/PixelConverter.cs
rename to src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
index 8fde490fd..2336dbee7 100644
--- a/src/ImageSharp/PixelFormats/PixelConverter.cs
+++ b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
@@ -1,11 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
-namespace SixLabors.ImageSharp.PixelFormats
+namespace SixLabors.ImageSharp.PixelFormats.Utils
{
///
/// Contains optimized implementations for conversion between pixel formats.
diff --git a/src/ImageSharp/PixelFormats/PixelExtensions.cs b/src/ImageSharp/PixelFormats/Utils/PixelExtensions.cs
similarity index 93%
rename from src/ImageSharp/PixelFormats/PixelExtensions.cs
rename to src/ImageSharp/PixelFormats/Utils/PixelExtensions.cs
index 175696ab6..2284b8fc2 100644
--- a/src/ImageSharp/PixelFormats/PixelExtensions.cs
+++ b/src/ImageSharp/PixelFormats/Utils/PixelExtensions.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-namespace SixLabors.ImageSharp.PixelFormats
+namespace SixLabors.ImageSharp.PixelFormats.Utils
{
///
/// Low-performance extension methods to help conversion syntax, suitable for testing purposes.
diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
new file mode 100644
index 000000000..139dbfa10
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.PixelFormats.Utils
+{
+ ///
+ /// Helper class for (bulk) conversion of buffers to/from other buffer types.
+ ///
+ internal static partial class Vector4Converters
+ {
+ ///
+ /// Provides default implementations for batched to/from conversion.
+ /// WARNING: The methods are operating without bounds checking and input validation!
+ /// Input validation is the responsibility of the caller!
+ ///
+ public static class Default
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void DangerousFromVector4(
+ ReadOnlySpan sourceVectors,
+ Span destPixels)
+ where TPixel : struct, IPixel
+ {
+ ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
+ ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
+
+ for (int i = 0; i < sourceVectors.Length; i++)
+ {
+ ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref TPixel dp = ref Unsafe.Add(ref destRef, i);
+ dp.FromVector4(sp);
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void DangerousToVector4(
+ ReadOnlySpan sourcePixels,
+ Span destVectors)
+ where TPixel : struct, IPixel
+ {
+ ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels);
+ ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors);
+
+ for (int i = 0; i < sourcePixels.Length; i++)
+ {
+ ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
+ dp = sp.ToVector4();
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void DangerousFromScaledVector4(
+ ReadOnlySpan sourceVectors,
+ Span destinationColors)
+ where TPixel : struct, IPixel
+ {
+ ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
+ ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
+
+ for (int i = 0; i < sourceVectors.Length; i++)
+ {
+ ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref TPixel dp = ref Unsafe.Add(ref destRef, i);
+ dp.FromScaledVector4(sp);
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void DangerousToScaledVector4(
+ ReadOnlySpan sourceColors,
+ Span destinationVectors)
+ where TPixel : struct, IPixel
+ {
+ ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
+ ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors);
+
+ for (int i = 0; i < sourceColors.Length; i++)
+ {
+ ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
+ dp = sp.ToScaledVector4();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
new file mode 100644
index 000000000..7c57fe4fb
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Buffers;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.PixelFormats.Utils
+{
+ ///
+ /// Contains
+ ///
+ internal static partial class Vector4Converters
+ {
+ ///
+ /// Provides efficient implementations for batched to/from conversion.
+ /// which is applicable for -compatible pixel types where
+ /// returns the same scaled result as .
+ /// The method is works by internally converting to a therefore it's not applicable for that type!
+ ///
+ public static class RgbaCompatible
+ {
+ ///
+ /// It's not worth to bother the transitive pixel conversion method below this limit.
+ /// The value depends on the actual gain brought by the SIMD characteristics of the executing CPU and JIT.
+ ///
+ private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold();
+
+ ///
+ /// Provides an efficient default implementation for
+ /// and
+ /// which is applicable for -compatible pixel types where
+ /// returns the same scaled result as .
+ /// The method is works by internally converting to a therefore it's not applicable for that type!
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void ToVector4(
+ Configuration configuration,
+ PixelOperations pixelOperations,
+ ReadOnlySpan sourcePixels,
+ Span destVectors,
+ bool scaled)
+ where TPixel : struct, IPixel
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors));
+
+ int count = sourcePixels.Length;
+
+ // Not worth for small buffers:
+ if (count < Vector4ConversionThreshold)
+ {
+ ToVector4Fallback(sourcePixels, destVectors, scaled);
+
+ return;
+ }
+
+ // Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation:
+ int countWithoutLastItem = count - 1;
+ ReadOnlySpan reducedSource = sourcePixels.Slice(0, countWithoutLastItem);
+ Span lastQuarterOfDestBuffer = MemoryMarshal.Cast(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
+ pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
+
+ // 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
+ // but we are always reading/writing at different positions:
+ SimdUtils.BulkConvertByteToNormalizedFloat(
+ MemoryMarshal.Cast(lastQuarterOfDestBuffer),
+ MemoryMarshal.Cast(destVectors.Slice(0, countWithoutLastItem)));
+
+ destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4();
+ }
+
+ ///
+ /// Provides an efficient default implementation for
+ /// and
+ /// which is applicable for -compatible pixel types where
+ /// returns the same scaled result as .
+ /// The method is works by internally converting to a therefore it's not applicable for that type!
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void FromVector4(
+ Configuration configuration,
+ PixelOperations pixelOperations,
+ ReadOnlySpan sourceVectors,
+ Span destPixels,
+ bool scaled)
+ where TPixel : struct, IPixel
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels));
+
+ int count = sourceVectors.Length;
+
+ // Not worth for small buffers:
+ if (count < Vector4ConversionThreshold)
+ {
+ FromVector4Fallback(sourceVectors, destPixels, scaled);
+
+ return;
+ }
+
+ // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion,
+ // so let's allocate a temporary buffer as usually:
+ using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count))
+ {
+ Span tempSpan = tempBuffer.Memory.Span;
+
+ SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(
+ MemoryMarshal.Cast(sourceVectors),
+ MemoryMarshal.Cast(tempSpan));
+
+ pixelOperations.FromRgba32(configuration, tempSpan, destPixels);
+ }
+ }
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void ToVector4Fallback(ReadOnlySpan sourcePixels, Span destVectors, bool scaled)
+ where TPixel : struct, IPixel
+ {
+ if (scaled)
+ {
+ Default.DangerousToScaledVector4(sourcePixels, destVectors);
+ }
+ else
+ {
+ Default.DangerousToVector4(sourcePixels, destVectors);
+ }
+ }
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void FromVector4Fallback(ReadOnlySpan sourceVectors, Span destPixels, bool scaled)
+ where TPixel : struct, IPixel
+ {
+ if (scaled)
+ {
+ Default.DangerousFromScaledVector4(sourceVectors, destPixels);
+ }
+ else
+ {
+ Default.DangerousFromVector4(sourceVectors, destPixels);
+ }
+ }
+
+ private static int CalculateVector4ConversionThreshold()
+ {
+ if (!Vector.IsHardwareAccelerated)
+ {
+ return int.MaxValue;
+ }
+
+ return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs
index 028bfe46f..39702d525 100644
--- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs
@@ -16,5 +16,26 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
this.source.GetSpan(),
this.destination.GetSpan());
}
+
+ // RESULTS:
+ // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
+ // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:|
+ // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B |
+ // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B |
+ // | | | | | | | | | |
+ // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B |
+ // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B |
+ // | | | | | | | | | |
+ // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B |
+ // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B |
+ // | | | | | | | | | |
+ // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B |
+ // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B |
+ // | | | | | | | | | |
+ // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B |
+ // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B |
+ // | | | | | | | | | |
+ // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B |
+ // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B |
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs
index 424020e2f..9f1b2721b 100644
--- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs
+++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.PixelFormats.Utils;
namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
{
diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs
index 9b32f7aee..c539e9dcf 100644
--- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs
+++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs
@@ -1,4 +1,5 @@
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.PixelFormats.Utils;
using Xunit;