mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 334 additions and 29 deletions
@ -0,0 +1,139 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Components.Alpha; |
|||
|
|||
/// <summary>
|
|||
/// Converts packed ImageSharp alpha values into one native HEIF monochrome plane.
|
|||
/// </summary>
|
|||
internal static class HeifPlanarAlphaEncoder |
|||
{ |
|||
/// <summary>
|
|||
/// Converts one packed image frame into a full-range native alpha plane.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The packed source pixel type.</typeparam>
|
|||
/// <typeparam name="TBuffer">The codec adapter exposing the destination plane.</typeparam>
|
|||
/// <typeparam name="TSample">The native unsigned sample storage type.</typeparam>
|
|||
/// <typeparam name="TStorer">The SIMD narrowing and storage operations for the sample type.</typeparam>
|
|||
/// <param name="configuration">The configuration used for row allocation and pixel conversion.</param>
|
|||
/// <param name="image">The packed source image frame.</param>
|
|||
/// <param name="buffer">The monochrome destination buffer.</param>
|
|||
public static void Convert<TPixel, TBuffer, TSample, TStorer>( |
|||
Configuration configuration, |
|||
ImageFrame<TPixel> image, |
|||
TBuffer buffer) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
where TBuffer : struct, IHeifPlanarSampleBuffer<TSample> |
|||
where TSample : unmanaged |
|||
where TStorer : struct, IHeifSampleConverter<TSample> |
|||
{ |
|||
int width = image.Width; |
|||
|
|||
// Rgba64 preserves the source pixel's normalized alpha precision before quantization to the requested AV1
|
|||
// depth. Both row views share one owner because their lifetimes never escape this conversion operation.
|
|||
using IMemoryOwner<float> rowOwner = configuration.MemoryAllocator.Allocate<float>(width * 3); |
|||
Span<float> rowStorage = rowOwner.GetSpan(); |
|||
Span<Rgba64> packed = MemoryMarshal.Cast<float, Rgba64>(rowStorage[..(width * 2)]); |
|||
Span<float> alpha = rowStorage.Slice(width * 2, width); |
|||
float maximum = (1 << buffer.LumaBitDepth) - 1; |
|||
float scale = maximum / ushort.MaxValue; |
|||
for (int y = 0; y < image.Height; y++) |
|||
{ |
|||
ReadOnlySpan<TPixel> source = image.PixelBuffer.DangerousGetRowSpan(y); |
|||
PixelOperations<TPixel>.Instance.ToRgba64(configuration, source, packed); |
|||
ExtractAlpha(packed, alpha); |
|||
HeifSampleConversion.WriteSamples<TSample, TStorer>( |
|||
alpha, |
|||
buffer.GetLumaRowSpan(y), |
|||
scale, |
|||
0F, |
|||
maximum); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Deinterleaves alpha values from one packed high-precision row.
|
|||
/// </summary>
|
|||
private static void ExtractAlpha(ReadOnlySpan<Rgba64> source, Span<float> destination) |
|||
{ |
|||
ref Rgba64 sourceBase = ref MemoryMarshal.GetReference(source); |
|||
ref float destinationBase = ref MemoryMarshal.GetReference(destination); |
|||
int i = 0; |
|||
|
|||
// Packed RGBA requires a gather before conversion. Constructing vectors from the alpha fields keeps the
|
|||
// widening and stores SIMD-wide without copying or transposing the complete packed row.
|
|||
if (Vector512.IsHardwareAccelerated) |
|||
{ |
|||
nuint vectorCount = Numerics.Vector512Count<float>(destination.Length); |
|||
for (nuint vectorIndex = 0; vectorIndex < vectorCount; vectorIndex++) |
|||
{ |
|||
int offset = (int)(vectorIndex * (uint)Vector512<float>.Count); |
|||
Vector512<uint> values = Vector512.Create( |
|||
CreateAlphaVector256(ref Unsafe.Add(ref sourceBase, offset)), |
|||
CreateAlphaVector256(ref Unsafe.Add(ref sourceBase, offset + Vector256<uint>.Count))); |
|||
|
|||
Vector512.ConvertToSingle(values).StoreUnsafe( |
|||
ref destinationBase, |
|||
(nuint)offset); |
|||
} |
|||
|
|||
i = (int)(vectorCount * (uint)Vector512<float>.Count); |
|||
} |
|||
|
|||
if (Vector256.IsHardwareAccelerated) |
|||
{ |
|||
nuint vectorCount = Numerics.Vector256Count<float>(destination.Length - i); |
|||
for (nuint vectorIndex = 0; vectorIndex < vectorCount; vectorIndex++) |
|||
{ |
|||
int offset = i + (int)(vectorIndex * (uint)Vector256<float>.Count); |
|||
Vector256<uint> values = CreateAlphaVector256(ref Unsafe.Add(ref sourceBase, offset)); |
|||
Vector256.ConvertToSingle(values).StoreUnsafe( |
|||
ref destinationBase, |
|||
(nuint)offset); |
|||
} |
|||
|
|||
i += (int)(vectorCount * (uint)Vector256<float>.Count); |
|||
} |
|||
|
|||
if (Vector128.IsHardwareAccelerated) |
|||
{ |
|||
nuint vectorCount = Numerics.Vector128Count<float>(destination.Length - i); |
|||
for (nuint vectorIndex = 0; vectorIndex < vectorCount; vectorIndex++) |
|||
{ |
|||
int offset = i + (int)(vectorIndex * (uint)Vector128<float>.Count); |
|||
Vector128<uint> values = CreateAlphaVector128(ref Unsafe.Add(ref sourceBase, offset)); |
|||
Vector128.ConvertToSingle(values).StoreUnsafe( |
|||
ref destinationBase, |
|||
(nuint)offset); |
|||
} |
|||
|
|||
i += (int)(vectorCount * (uint)Vector128<float>.Count); |
|||
} |
|||
|
|||
for (; i < destination.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref destinationBase, i) = Unsafe.Add(ref sourceBase, i).A; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector128<uint> CreateAlphaVector128(ref Rgba64 source) |
|||
=> Vector128.Create( |
|||
(uint)source.A, |
|||
Unsafe.Add(ref source, 1).A, |
|||
Unsafe.Add(ref source, 2).A, |
|||
Unsafe.Add(ref source, 3).A); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector256<uint> CreateAlphaVector256(ref Rgba64 source) |
|||
=> Vector256.Create( |
|||
CreateAlphaVector128(ref source), |
|||
CreateAlphaVector128(ref Unsafe.Add(ref source, Vector128<uint>.Count))); |
|||
} |
|||
Loading…
Reference in new issue