mirror of https://github.com/SixLabors/ImageSharp
18 changed files with 302 additions and 166 deletions
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Contains optimized implementations for conversion between pixel formats.
|
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Low-performance extension methods to help conversion syntax, suitable for testing purposes.
|
|||
@ -0,0 +1,89 @@ |
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats.Utils |
|||
{ |
|||
/// <summary>
|
|||
/// Helper class for (bulk) conversion of <see cref="Vector4"/> buffers to/from other buffer types.
|
|||
/// </summary>
|
|||
internal static partial class Vector4Converters |
|||
{ |
|||
/// <summary>
|
|||
/// Provides default implementations for batched to/from <see cref="Vector4"/> conversion.
|
|||
/// WARNING: The methods are operating without bounds checking and input validation!
|
|||
/// Input validation is the responsibility of the caller!
|
|||
/// </summary>
|
|||
public static class Default |
|||
{ |
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void DangerousFromVector4<TPixel>( |
|||
ReadOnlySpan<Vector4> sourceVectors, |
|||
Span<TPixel> destPixels) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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<TPixel>( |
|||
ReadOnlySpan<TPixel> sourcePixels, |
|||
Span<Vector4> destVectors) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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<TPixel>( |
|||
ReadOnlySpan<Vector4> sourceVectors, |
|||
Span<TPixel> destinationColors) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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<TPixel>( |
|||
ReadOnlySpan<TPixel> sourceColors, |
|||
Span<Vector4> destinationVectors) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <content>
|
|||
/// Contains <see cref="RgbaCompatible"/>
|
|||
/// </content>
|
|||
internal static partial class Vector4Converters |
|||
{ |
|||
/// <summary>
|
|||
/// Provides efficient implementations for batched to/from <see cref="Vector4"/> conversion.
|
|||
/// 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>
|
|||
public static class RgbaCompatible |
|||
{ |
|||
/// <summary>
|
|||
/// 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.
|
|||
/// </summary>
|
|||
private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); |
|||
|
|||
/// <summary>
|
|||
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.ToVector4"/>
|
|||
/// and <see cref="PixelOperations{TPixel}.ToScaledVector4"/>
|
|||
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
|
|||
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
|
|||
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void ToVector4<TPixel>( |
|||
Configuration configuration, |
|||
PixelOperations<TPixel> pixelOperations, |
|||
ReadOnlySpan<TPixel> sourcePixels, |
|||
Span<Vector4> destVectors, |
|||
bool scaled) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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<TPixel> reducedSource = sourcePixels.Slice(0, countWithoutLastItem); |
|||
Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(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<Rgba32, byte>(lastQuarterOfDestBuffer), |
|||
MemoryMarshal.Cast<Vector4, float>(destVectors.Slice(0, countWithoutLastItem))); |
|||
|
|||
destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides an efficient default implementation for <see cref="PixelOperations{TPixel}.FromVector4"/>
|
|||
/// and <see cref="PixelOperations{TPixel}.FromScaledVector4"/>
|
|||
/// which is applicable for <see cref="Rgba32"/>-compatible pixel types where <see cref="IPixel.ToVector4"/>
|
|||
/// returns the same scaled result as <see cref="IPixel.ToScaledVector4"/>.
|
|||
/// The method is works by internally converting to a <see cref="Rgba32"/> therefore it's not applicable for that type!
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void FromVector4<TPixel>( |
|||
Configuration configuration, |
|||
PixelOperations<TPixel> pixelOperations, |
|||
ReadOnlySpan<Vector4> sourceVectors, |
|||
Span<TPixel> destPixels, |
|||
bool scaled) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
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<Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate<Rgba32>(count)) |
|||
{ |
|||
Span<Rgba32> tempSpan = tempBuffer.Memory.Span; |
|||
|
|||
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( |
|||
MemoryMarshal.Cast<Vector4, float>(sourceVectors), |
|||
MemoryMarshal.Cast<Rgba32, byte>(tempSpan)); |
|||
|
|||
pixelOperations.FromRgba32(configuration, tempSpan, destPixels); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ColdPath)] |
|||
private static void ToVector4Fallback<TPixel>(ReadOnlySpan<TPixel> sourcePixels, Span<Vector4> destVectors, bool scaled) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (scaled) |
|||
{ |
|||
Default.DangerousToScaledVector4(sourcePixels, destVectors); |
|||
} |
|||
else |
|||
{ |
|||
Default.DangerousToVector4(sourcePixels, destVectors); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ColdPath)] |
|||
private static void FromVector4Fallback<TPixel>(ReadOnlySpan<Vector4> sourceVectors, Span<TPixel> destPixels, bool scaled) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (scaled) |
|||
{ |
|||
Default.DangerousFromScaledVector4(sourceVectors, destPixels); |
|||
} |
|||
else |
|||
{ |
|||
Default.DangerousFromVector4(sourceVectors, destPixels); |
|||
} |
|||
} |
|||
|
|||
private static int CalculateVector4ConversionThreshold() |
|||
{ |
|||
if (!Vector.IsHardwareAccelerated) |
|||
{ |
|||
return int.MaxValue; |
|||
} |
|||
|
|||
return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue