mirror of https://github.com/SixLabors/ImageSharp
130 changed files with 2827 additions and 1338 deletions
@ -1,261 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.Intrinsics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
|
||||
|
|
||||
/// <content>
|
|
||||
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
|
|
||||
/// </content>
|
|
||||
internal static partial class HeifYuv420ToRgb8Converter |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
|
|
||||
/// </summary>
|
|
||||
private readonly struct ConversionParameters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The scalar fixed-point coefficients.
|
|
||||
/// </summary>
|
|
||||
public readonly FixedPointParameters Scalar; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The four-lane SIMD coefficients.
|
|
||||
/// </summary>
|
|
||||
public readonly Vector128Parameters FourLane; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The eight-lane SIMD coefficients.
|
|
||||
/// </summary>
|
|
||||
public readonly Vector256Parameters EightLane; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The sixteen-lane SIMD coefficients.
|
|
||||
/// </summary>
|
|
||||
public readonly Vector512Parameters SixteenLane; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="parameters">The shared floating-point conversion parameters.</param>
|
|
||||
public ConversionParameters(in HeifColorConversionParameters parameters) |
|
||||
{ |
|
||||
FixedPointParameters scalar = new(in parameters); |
|
||||
this.Scalar = scalar; |
|
||||
this.FourLane = new(in scalar); |
|
||||
this.EightLane = new(in scalar); |
|
||||
this.SixteenLane = new(in scalar); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the scalar fixed-point coefficients resolved for one image.
|
|
||||
/// </summary>
|
|
||||
private readonly struct FixedPointParameters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="FixedPointParameters"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="parameters">The shared floating-point conversion parameters.</param>
|
|
||||
public FixedPointParameters(in HeifColorConversionParameters parameters) |
|
||||
{ |
|
||||
float scale = 1 << CoefficientShift; |
|
||||
|
|
||||
// Rounding each image-invariant coefficient once gives the integer kernel eight fractional bits.
|
|
||||
// The signed green coefficients retain the exact addition and rounding order used by every SIMD lane.
|
|
||||
this.RedCr = (int)MathF.Round(parameters.RedChromaScale * scale, MidpointRounding.AwayFromZero); |
|
||||
this.GreenCb = -(int)MathF.Round(parameters.GreenBlueChromaScale * scale, MidpointRounding.AwayFromZero); |
|
||||
this.GreenCr = -(int)MathF.Round(parameters.GreenRedChromaScale * scale, MidpointRounding.AwayFromZero); |
|
||||
this.BlueCb = (int)MathF.Round(parameters.BlueChromaScale * scale, MidpointRounding.AwayFromZero); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the red contribution from centered Cr.
|
|
||||
/// </summary>
|
|
||||
public int RedCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green contribution from centered Cb.
|
|
||||
/// </summary>
|
|
||||
public int GreenCb { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green contribution from centered Cr.
|
|
||||
/// </summary>
|
|
||||
public int GreenCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the blue contribution from centered Cb.
|
|
||||
/// </summary>
|
|
||||
public int BlueCb { get; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Broadcasts the fixed-point coefficients for four-lane conversion.
|
|
||||
/// </summary>
|
|
||||
private readonly struct Vector128Parameters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
|
||||
public Vector128Parameters(in FixedPointParameters parameters) |
|
||||
{ |
|
||||
this.ChromaMidpoint = Vector128.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint); |
|
||||
this.RoundingBias = Vector128.Create(HeifYuv420ToRgb8Converter.RoundingBias); |
|
||||
this.Maximum = Vector128.Create((int)byte.MaxValue); |
|
||||
this.RedCr = Vector128.Create(parameters.RedCr); |
|
||||
this.GreenCb = Vector128.Create(parameters.GreenCb); |
|
||||
this.GreenCr = Vector128.Create(parameters.GreenCr); |
|
||||
this.BlueCb = Vector128.Create(parameters.BlueCb); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the neutral chroma code-value lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> ChromaMidpoint { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the fixed-point rounding-bias lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> RoundingBias { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum eight-bit sample lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> Maximum { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the red Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> RedCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> GreenCb { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> GreenCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the blue Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector128<int> BlueCb { get; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Broadcasts the fixed-point coefficients for eight-lane conversion.
|
|
||||
/// </summary>
|
|
||||
private readonly struct Vector256Parameters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
|
||||
public Vector256Parameters(in FixedPointParameters parameters) |
|
||||
{ |
|
||||
this.ChromaMidpoint = Vector256.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint); |
|
||||
this.RoundingBias = Vector256.Create(HeifYuv420ToRgb8Converter.RoundingBias); |
|
||||
this.Maximum = Vector256.Create((int)byte.MaxValue); |
|
||||
this.RedCr = Vector256.Create(parameters.RedCr); |
|
||||
this.GreenCb = Vector256.Create(parameters.GreenCb); |
|
||||
this.GreenCr = Vector256.Create(parameters.GreenCr); |
|
||||
this.BlueCb = Vector256.Create(parameters.BlueCb); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the neutral chroma code-value lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> ChromaMidpoint { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the fixed-point rounding-bias lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> RoundingBias { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum eight-bit sample lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> Maximum { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the red Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> RedCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> GreenCb { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> GreenCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the blue Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector256<int> BlueCb { get; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Broadcasts the fixed-point coefficients for sixteen-lane conversion.
|
|
||||
/// </summary>
|
|
||||
private readonly struct Vector512Parameters |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
|
||||
public Vector512Parameters(in FixedPointParameters parameters) |
|
||||
{ |
|
||||
this.ChromaMidpoint = Vector512.Create(HeifYuv420ToRgb8Converter.ChromaMidpoint); |
|
||||
this.RoundingBias = Vector512.Create(HeifYuv420ToRgb8Converter.RoundingBias); |
|
||||
this.Maximum = Vector512.Create((int)byte.MaxValue); |
|
||||
this.RedCr = Vector512.Create(parameters.RedCr); |
|
||||
this.GreenCb = Vector512.Create(parameters.GreenCb); |
|
||||
this.GreenCr = Vector512.Create(parameters.GreenCr); |
|
||||
this.BlueCb = Vector512.Create(parameters.BlueCb); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the neutral chroma code-value lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> ChromaMidpoint { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the fixed-point rounding-bias lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> RoundingBias { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum eight-bit sample lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> Maximum { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the red Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> RedCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> GreenCb { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green Cr coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> GreenCr { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the blue Cb coefficient lanes.
|
|
||||
/// </summary>
|
|
||||
public Vector512<int> BlueCb { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,105 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Buffers; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Cicp; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts full-range eight-bit HEIF 4:2:0 planes with an unspecified matrix to packed RGB pixels.
|
|
||||
/// </summary>
|
|
||||
internal static partial class HeifYuv420ToRgb8Converter |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The fixed-point precision used for H.273 matrix coefficients.
|
|
||||
/// </summary>
|
|
||||
private const int CoefficientShift = 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The half-unit bias used before fixed-point coefficient results are shifted to integer samples.
|
|
||||
/// </summary>
|
|
||||
private const int RoundingBias = 1 << (CoefficientShift - 1); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The neutral code value for full-range eight-bit chroma.
|
|
||||
/// </summary>
|
|
||||
private const int ChromaMidpoint = 128; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines whether the specialized integer conversion supports the supplied plane and color description.
|
|
||||
/// </summary>
|
|
||||
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
|
|
||||
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
|
|
||||
/// <param name="lumaBitDepth">The luma sample precision in bits.</param>
|
|
||||
/// <param name="chromaBitDepth">The chroma sample precision in bits.</param>
|
|
||||
/// <param name="isFullRange">Whether the samples use the complete numeric range.</param>
|
|
||||
/// <param name="matrixCoefficients">The H.273 matrix-coefficient code point.</param>
|
|
||||
/// <param name="mode">The resolved H.273 conversion operation.</param>
|
|
||||
/// <returns><see langword="true"/> when the planes can use this converter; otherwise, <see langword="false"/>.</returns>
|
|
||||
public static bool IsSupported( |
|
||||
int subsamplingX, |
|
||||
int subsamplingY, |
|
||||
int lumaBitDepth, |
|
||||
int chromaBitDepth, |
|
||||
bool isFullRange, |
|
||||
CicpMatrixCoefficients matrixCoefficients, |
|
||||
HeifColorConversionMode mode) |
|
||||
=> subsamplingX == 1 |
|
||||
&& subsamplingY == 1 |
|
||||
&& lumaBitDepth == 8 |
|
||||
&& chromaBitDepth == 8 |
|
||||
&& isFullRange |
|
||||
&& matrixCoefficients == CicpMatrixCoefficients.Unspecified |
|
||||
&& mode == HeifColorConversionMode.Coefficients; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts supported HEIF component planes to packed pixels using integer SIMD with a scalar tail.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
|
|
||||
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
|
|
||||
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
|
|
||||
/// <param name="buffer">The reconstructed component-plane buffer.</param>
|
|
||||
/// <param name="image">The destination image frame.</param>
|
|
||||
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
|
|
||||
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
|
|
||||
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
|
|
||||
public static void Convert<TPixel, TBuffer>( |
|
||||
Configuration configuration, |
|
||||
TBuffer buffer, |
|
||||
ImageFrame<TPixel> image, |
|
||||
in HeifColorConversionParameters parameters, |
|
||||
int sourceX, |
|
||||
int sourceY) |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort> |
|
||||
{ |
|
||||
ConversionParameters conversionParameters = new(in parameters); |
|
||||
using IMemoryOwner<byte> componentOwner = configuration.MemoryAllocator.Allocate<byte>(image.Width * 3); |
|
||||
Span<byte> components = componentOwner.GetSpan(); |
|
||||
Span<byte> red = components[..image.Width]; |
|
||||
Span<byte> green = components.Slice(image.Width, image.Width); |
|
||||
Span<byte> blue = components.Slice(image.Width * 2, image.Width); |
|
||||
|
|
||||
// The value-type buffer closes the row-access contract at the call site. Constrained calls are therefore
|
|
||||
// devirtualized without boxing while keeping codec-specific buffer ownership outside the color pipeline.
|
|
||||
for (int y = 0; y < image.Height; y++) |
|
||||
{ |
|
||||
int lumaY = sourceY + y; |
|
||||
|
|
||||
// The codec boundary validates 4:2:0 crop offsets in complete chroma-sample units. Each native chroma
|
|
||||
// sample therefore covers one 2x2 luma cell without an alignment branch in the SIMD loop.
|
|
||||
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width); |
|
||||
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(lumaY >> 1).Slice(sourceX >> 1); |
|
||||
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(lumaY >> 1).Slice(sourceX >> 1); |
|
||||
|
|
||||
ConvertRow<FixedPointCoefficientOperator>(luma, chromaBlue, chromaRed, red, green, blue, in conversionParameters); |
|
||||
|
|
||||
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y); |
|
||||
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,151 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides pinned-libheif high-bit-depth coefficient conversion at the source RGB precision.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb16Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb16Operator |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector512<int> y, |
||||
|
Vector512<int> cb, |
||||
|
Vector512<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector512<int> r, |
||||
|
out Vector512<int> g, |
||||
|
out Vector512<int> b) |
||||
|
{ |
||||
|
Vector512Parameters values = parameters.SixteenLane; |
||||
|
Vector512<float> luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector512<float> blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector512<float> redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector512<float> half = Vector512.Create(0.5F); |
||||
|
|
||||
|
// libheif evaluates these as distinct float32 multiplies and adds; FMA changes some 12-bit results by one.
|
||||
|
Vector512<float> redValue = Vector512.Multiply(values.RedCr, redDifference); |
||||
|
redValue = Vector512.Add(luma, redValue); |
||||
|
Vector512<float> greenValue = Vector512.Multiply(values.GreenCb, blueDifference); |
||||
|
greenValue = Vector512.Add(luma, greenValue); |
||||
|
Vector512<float> greenRedValue = Vector512.Multiply(values.GreenCr, redDifference); |
||||
|
greenValue = Vector512.Add(greenValue, greenRedValue); |
||||
|
Vector512<float> blueValue = Vector512.Multiply(values.BlueCb, blueDifference); |
||||
|
blueValue = Vector512.Add(luma, blueValue); |
||||
|
Vector512<int> red = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(redValue, half))); |
||||
|
Vector512<int> green = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(greenValue, half))); |
||||
|
Vector512<int> blue = Vector512.ConvertToInt32(Vector512.Truncate(Vector512.Add(blueValue, half))); |
||||
|
|
||||
|
r = Vector512.Clamp(red, default, values.Maximum); |
||||
|
g = Vector512.Clamp(green, default, values.Maximum); |
||||
|
b = Vector512.Clamp(blue, default, values.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector256<int> y, |
||||
|
Vector256<int> cb, |
||||
|
Vector256<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector256<int> r, |
||||
|
out Vector256<int> g, |
||||
|
out Vector256<int> b) |
||||
|
{ |
||||
|
Vector256Parameters values = parameters.EightLane; |
||||
|
Vector256<float> luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector256<float> blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector256<float> redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector256<float> half = Vector256.Create(0.5F); |
||||
|
Vector256<float> redValue = Vector256.Multiply(values.RedCr, redDifference); |
||||
|
redValue = Vector256.Add(luma, redValue); |
||||
|
Vector256<float> greenValue = Vector256.Multiply(values.GreenCb, blueDifference); |
||||
|
greenValue = Vector256.Add(luma, greenValue); |
||||
|
Vector256<float> greenRedValue = Vector256.Multiply(values.GreenCr, redDifference); |
||||
|
greenValue = Vector256.Add(greenValue, greenRedValue); |
||||
|
Vector256<float> blueValue = Vector256.Multiply(values.BlueCb, blueDifference); |
||||
|
blueValue = Vector256.Add(luma, blueValue); |
||||
|
Vector256<int> red = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(redValue, half))); |
||||
|
Vector256<int> green = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(greenValue, half))); |
||||
|
Vector256<int> blue = Vector256.ConvertToInt32(Vector256.Truncate(Vector256.Add(blueValue, half))); |
||||
|
|
||||
|
r = Vector256.Clamp(red, default, values.Maximum); |
||||
|
g = Vector256.Clamp(green, default, values.Maximum); |
||||
|
b = Vector256.Clamp(blue, default, values.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector128<int> y, |
||||
|
Vector128<int> cb, |
||||
|
Vector128<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector128<int> r, |
||||
|
out Vector128<int> g, |
||||
|
out Vector128<int> b) |
||||
|
{ |
||||
|
Vector128Parameters values = parameters.FourLane; |
||||
|
Vector128<float> luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector128<float> blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector128<float> redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector128<float> half = Vector128.Create(0.5F); |
||||
|
Vector128<float> redValue = Vector128.Multiply(values.RedCr, redDifference); |
||||
|
redValue = Vector128.Add(luma, redValue); |
||||
|
Vector128<float> greenValue = Vector128.Multiply(values.GreenCb, blueDifference); |
||||
|
greenValue = Vector128.Add(luma, greenValue); |
||||
|
Vector128<float> greenRedValue = Vector128.Multiply(values.GreenCr, redDifference); |
||||
|
greenValue = Vector128.Add(greenValue, greenRedValue); |
||||
|
Vector128<float> blueValue = Vector128.Multiply(values.BlueCb, blueDifference); |
||||
|
blueValue = Vector128.Add(luma, blueValue); |
||||
|
Vector128<int> red = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(redValue, half))); |
||||
|
Vector128<int> green = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(greenValue, half))); |
||||
|
Vector128<int> blue = Vector128.ConvertToInt32(Vector128.Truncate(Vector128.Add(blueValue, half))); |
||||
|
|
||||
|
r = Vector128.Clamp(red, default, values.Maximum); |
||||
|
g = Vector128.Clamp(green, default, values.Maximum); |
||||
|
b = Vector128.Clamp(blue, default, values.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
ushort y, |
||||
|
ushort cb, |
||||
|
ushort cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out int r, |
||||
|
out int g, |
||||
|
out int b) |
||||
|
{ |
||||
|
ScalarParameters values = parameters.Scalar; |
||||
|
float luma = (y - values.LumaOffset) * values.LumaScale; |
||||
|
float blueDifference = (cb - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
float redDifference = (cr - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
|
||||
|
// Keep each assignment separate so the JIT cannot fuse the reference float32 operations.
|
||||
|
float redValue = values.RedCr * redDifference; |
||||
|
redValue = luma + redValue; |
||||
|
float greenValue = values.GreenCb * blueDifference; |
||||
|
greenValue = luma + greenValue; |
||||
|
float greenRedValue = values.GreenCr * redDifference; |
||||
|
greenValue += greenRedValue; |
||||
|
float blueValue = values.BlueCb * blueDifference; |
||||
|
blueValue = luma + blueValue; |
||||
|
|
||||
|
r = Numerics.Clamp((int)(redValue + 0.5F), 0, values.Maximum); |
||||
|
g = Numerics.Clamp((int)(greenValue + 0.5F), 0, values.Maximum); |
||||
|
b = Numerics.Clamp((int)(blueValue + 0.5F), 0, values.Maximum); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides pinned-libheif high-bit-depth monochrome presentation without luma-range expansion.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb16Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Copies source-precision luma into each source-precision RGB component.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb16Operator |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector512<int> y, |
||||
|
Vector512<int> cb, |
||||
|
Vector512<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector512<int> r, |
||||
|
out Vector512<int> g, |
||||
|
out Vector512<int> b) |
||||
|
{ |
||||
|
r = y; |
||||
|
g = y; |
||||
|
b = y; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector256<int> y, |
||||
|
Vector256<int> cb, |
||||
|
Vector256<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector256<int> r, |
||||
|
out Vector256<int> g, |
||||
|
out Vector256<int> b) |
||||
|
{ |
||||
|
r = y; |
||||
|
g = y; |
||||
|
b = y; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector128<int> y, |
||||
|
Vector128<int> cb, |
||||
|
Vector128<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector128<int> r, |
||||
|
out Vector128<int> g, |
||||
|
out Vector128<int> b) |
||||
|
{ |
||||
|
r = y; |
||||
|
g = y; |
||||
|
b = y; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
ushort y, |
||||
|
ushort cb, |
||||
|
ushort cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out int r, |
||||
|
out int g, |
||||
|
out int b) |
||||
|
{ |
||||
|
r = y; |
||||
|
g = y; |
||||
|
b = y; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,293 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using SixLabors.ImageSharp.Common.Helpers; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Defines closed high-bit-depth color operators and nearest-sample row traversal.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb16Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines source-precision RGB arithmetic for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
private interface IHeifYuvToRgb16Operator |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts sixteen YCbCr samples to source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The luma lanes.</param>
|
||||
|
/// <param name="cb">The blue-difference lanes.</param>
|
||||
|
/// <param name="cr">The red-difference lanes.</param>
|
||||
|
/// <param name="parameters">The image conversion parameters.</param>
|
||||
|
/// <param name="r">The converted red lanes.</param>
|
||||
|
/// <param name="g">The converted green lanes.</param>
|
||||
|
/// <param name="b">The converted blue lanes.</param>
|
||||
|
public static abstract void Convert( |
||||
|
Vector512<int> y, |
||||
|
Vector512<int> cb, |
||||
|
Vector512<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector512<int> r, |
||||
|
out Vector512<int> g, |
||||
|
out Vector512<int> b); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts eight YCbCr samples to source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The luma lanes.</param>
|
||||
|
/// <param name="cb">The blue-difference lanes.</param>
|
||||
|
/// <param name="cr">The red-difference lanes.</param>
|
||||
|
/// <param name="parameters">The image conversion parameters.</param>
|
||||
|
/// <param name="r">The converted red lanes.</param>
|
||||
|
/// <param name="g">The converted green lanes.</param>
|
||||
|
/// <param name="b">The converted blue lanes.</param>
|
||||
|
public static abstract void Convert( |
||||
|
Vector256<int> y, |
||||
|
Vector256<int> cb, |
||||
|
Vector256<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector256<int> r, |
||||
|
out Vector256<int> g, |
||||
|
out Vector256<int> b); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts four YCbCr samples to source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The luma lanes.</param>
|
||||
|
/// <param name="cb">The blue-difference lanes.</param>
|
||||
|
/// <param name="cr">The red-difference lanes.</param>
|
||||
|
/// <param name="parameters">The image conversion parameters.</param>
|
||||
|
/// <param name="r">The converted red lanes.</param>
|
||||
|
/// <param name="g">The converted green lanes.</param>
|
||||
|
/// <param name="b">The converted blue lanes.</param>
|
||||
|
public static abstract void Convert( |
||||
|
Vector128<int> y, |
||||
|
Vector128<int> cb, |
||||
|
Vector128<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector128<int> r, |
||||
|
out Vector128<int> g, |
||||
|
out Vector128<int> b); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts one YCbCr sample to source-precision RGB.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The luma sample.</param>
|
||||
|
/// <param name="cb">The blue-difference sample.</param>
|
||||
|
/// <param name="cr">The red-difference sample.</param>
|
||||
|
/// <param name="parameters">The image conversion parameters.</param>
|
||||
|
/// <param name="r">The converted red sample.</param>
|
||||
|
/// <param name="g">The converted green sample.</param>
|
||||
|
/// <param name="b">The converted blue sample.</param>
|
||||
|
public static abstract void Convert( |
||||
|
ushort y, |
||||
|
ushort cb, |
||||
|
ushort cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out int r, |
||||
|
out int g, |
||||
|
out int b); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts one luma row and its nearest native chroma row to planar 16-bit RGB storage.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TOperator">The source-precision color arithmetic selected for the row.</typeparam>
|
||||
|
/// <param name="luma">The full-resolution luma samples.</param>
|
||||
|
/// <param name="chromaBlue">The native blue-difference samples.</param>
|
||||
|
/// <param name="chromaRed">The native red-difference samples.</param>
|
||||
|
/// <param name="red">The destination red samples.</param>
|
||||
|
/// <param name="green">The destination green samples.</param>
|
||||
|
/// <param name="blue">The destination blue samples.</param>
|
||||
|
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
|
||||
|
/// <param name="parameters">The image conversion parameters.</param>
|
||||
|
private static void ConvertRow<TOperator>( |
||||
|
ReadOnlySpan<ushort> luma, |
||||
|
ReadOnlySpan<ushort> chromaBlue, |
||||
|
ReadOnlySpan<ushort> chromaRed, |
||||
|
Span<ushort> red, |
||||
|
Span<ushort> green, |
||||
|
Span<ushort> blue, |
||||
|
int subsamplingX, |
||||
|
in ConversionParameters parameters) |
||||
|
where TOperator : struct, IHeifYuvToRgb16Operator |
||||
|
{ |
||||
|
ref ushort lumaBase = ref MemoryMarshal.GetReference(luma); |
||||
|
ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue); |
||||
|
ref ushort chromaRedBase = ref MemoryMarshal.GetReference(chromaRed); |
||||
|
ref ushort redBase = ref MemoryMarshal.GetReference(red); |
||||
|
ref ushort greenBase = ref MemoryMarshal.GetReference(green); |
||||
|
ref ushort blueBase = ref MemoryMarshal.GetReference(blue); |
||||
|
int outputLeftShift = parameters.Scalar.OutputLeftShift; |
||||
|
int x = 0; |
||||
|
|
||||
|
// Each operator produces code values at the source precision. The traversal then left-aligns those values in
|
||||
|
// UInt16 storage, matching libheif's high-bit-depth RGB output without discarding low source bits.
|
||||
|
if (Vector512.IsHardwareAccelerated) |
||||
|
{ |
||||
|
int oneVectorFromEnd = luma.Length - Vector512<int>.Count; |
||||
|
|
||||
|
for (; x <= oneVectorFromEnd; x += Vector512<int>.Count) |
||||
|
{ |
||||
|
Vector512<int> y = LoadVector512(ref Unsafe.Add(ref lumaBase, x)); |
||||
|
Vector512<int> cb = subsamplingX == 0 |
||||
|
? LoadVector512(ref Unsafe.Add(ref chromaBlueBase, x)) |
||||
|
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
||||
|
|
||||
|
Vector512<int> cr = subsamplingX == 0 |
||||
|
? LoadVector512(ref Unsafe.Add(ref chromaRedBase, x)) |
||||
|
: LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
||||
|
|
||||
|
TOperator.Convert(y, cb, cr, in parameters, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b); |
||||
|
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x)); |
||||
|
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x)); |
||||
|
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector256.IsHardwareAccelerated) |
||||
|
{ |
||||
|
int oneVectorFromEnd = luma.Length - Vector256<int>.Count; |
||||
|
|
||||
|
for (; x <= oneVectorFromEnd; x += Vector256<int>.Count) |
||||
|
{ |
||||
|
Vector256<int> y = LoadVector256(ref Unsafe.Add(ref lumaBase, x)); |
||||
|
Vector256<int> cb = subsamplingX == 0 |
||||
|
? LoadVector256(ref Unsafe.Add(ref chromaBlueBase, x)) |
||||
|
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
||||
|
|
||||
|
Vector256<int> cr = subsamplingX == 0 |
||||
|
? LoadVector256(ref Unsafe.Add(ref chromaRedBase, x)) |
||||
|
: LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
||||
|
|
||||
|
TOperator.Convert(y, cb, cr, in parameters, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b); |
||||
|
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x)); |
||||
|
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x)); |
||||
|
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Vector128.IsHardwareAccelerated) |
||||
|
{ |
||||
|
int oneVectorFromEnd = luma.Length - Vector128<int>.Count; |
||||
|
|
||||
|
for (; x <= oneVectorFromEnd; x += Vector128<int>.Count) |
||||
|
{ |
||||
|
Vector128<int> y = LoadVector128(ref Unsafe.Add(ref lumaBase, x)); |
||||
|
Vector128<int> cb = subsamplingX == 0 |
||||
|
? LoadVector128(ref Unsafe.Add(ref chromaBlueBase, x)) |
||||
|
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
||||
|
|
||||
|
Vector128<int> cr = subsamplingX == 0 |
||||
|
? LoadVector128(ref Unsafe.Add(ref chromaRedBase, x)) |
||||
|
: LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
||||
|
|
||||
|
TOperator.Convert(y, cb, cr, in parameters, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b); |
||||
|
HeifUShortSampleConverter.Store(r << outputLeftShift, ref Unsafe.Add(ref redBase, x)); |
||||
|
HeifUShortSampleConverter.Store(g << outputLeftShift, ref Unsafe.Add(ref greenBase, x)); |
||||
|
HeifUShortSampleConverter.Store(b << outputLeftShift, ref Unsafe.Add(ref blueBase, x)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (; x < luma.Length; x++) |
||||
|
{ |
||||
|
TOperator.Convert( |
||||
|
Unsafe.Add(ref lumaBase, x), |
||||
|
Unsafe.Add(ref chromaBlueBase, x >> subsamplingX), |
||||
|
Unsafe.Add(ref chromaRedBase, x >> subsamplingX), |
||||
|
in parameters, |
||||
|
out int r, |
||||
|
out int g, |
||||
|
out int b); |
||||
|
|
||||
|
Unsafe.Add(ref redBase, x) = (ushort)(r << outputLeftShift); |
||||
|
Unsafe.Add(ref greenBase, x) = (ushort)(g << outputLeftShift); |
||||
|
Unsafe.Add(ref blueBase, x) = (ushort)(b << outputLeftShift); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads sixteen native samples as signed 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native sample.</param>
|
||||
|
/// <returns>The widened sample lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<int> LoadVector512(ref ushort source) |
||||
|
{ |
||||
|
(Vector256<uint> lower, Vector256<uint> upper) = Vector256.Widen(Vector256.LoadUnsafe(ref source)); |
||||
|
return Vector512.Create(lower, upper).AsInt32(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads eight native samples as signed 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native sample.</param>
|
||||
|
/// <returns>The widened sample lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<int> LoadVector256(ref ushort source) |
||||
|
{ |
||||
|
Vector128<ushort> samples = Vector128.LoadUnsafe(ref source); |
||||
|
return Vector256.Create(Vector128.WidenLower(samples), Vector128.WidenUpper(samples)).AsInt32(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads four native samples as signed 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native sample.</param>
|
||||
|
/// <returns>The widened sample lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<int> LoadVector128(ref ushort source) |
||||
|
{ |
||||
|
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref source)); |
||||
|
return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads eight chroma samples and repeats each sample into two of sixteen 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native chroma sample.</param>
|
||||
|
/// <returns>The horizontally replicated chroma lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector512<int> LoadRepeatedVector512(ref ushort source) |
||||
|
{ |
||||
|
Vector128<ushort> samples = Vector128.LoadUnsafe(ref source); |
||||
|
Vector128<ushort> lower = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); |
||||
|
Vector128<ushort> upper = Vector128_.UnpackHigh(samples.AsInt16(), samples.AsInt16()).AsUInt16(); |
||||
|
(Vector256<uint> widenedLower, Vector256<uint> widenedUpper) = Vector256.Widen(Vector256.Create(lower, upper)); |
||||
|
return Vector512.Create(widenedLower, widenedUpper).AsInt32(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads four chroma samples and repeats each sample into two of eight 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native chroma sample.</param>
|
||||
|
/// <returns>The horizontally replicated chroma lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector256<int> LoadRepeatedVector256(ref ushort source) |
||||
|
{ |
||||
|
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref source)); |
||||
|
Vector128<ushort> samples = Vector128.CreateScalarUnsafe(packed).AsUInt16(); |
||||
|
Vector128<ushort> repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); |
||||
|
return Vector256.Create(Vector128.WidenLower(repeated), Vector128.WidenUpper(repeated)).AsInt32(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads two chroma samples and repeats each sample into two of four 32-bit SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The first native chroma sample.</param>
|
||||
|
/// <returns>The horizontally replicated chroma lanes.</returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static Vector128<int> LoadRepeatedVector128(ref ushort source) |
||||
|
{ |
||||
|
uint packed = Unsafe.ReadUnaligned<uint>(ref Unsafe.As<ushort, byte>(ref source)); |
||||
|
Vector128<ushort> samples = Vector128.CreateScalarUnsafe(packed).AsUInt16(); |
||||
|
Vector128<ushort> repeated = Vector128_.UnpackLow(samples.AsInt16(), samples.AsInt16()).AsUInt16(); |
||||
|
return Vector128.WidenLower(repeated).AsInt32(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,347 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Cicp; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides scalar and SIMD parameter storage for high-bit-depth pinned-libheif conversion.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb16Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
|
||||
|
/// </summary>
|
||||
|
private readonly struct ConversionParameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
|
||||
|
/// <param name="bitDepth">The common source component precision.</param>
|
||||
|
public ConversionParameters(in HeifColorConversionParameters parameters, int bitDepth) |
||||
|
{ |
||||
|
ScalarParameters scalar = new(in parameters, bitDepth); |
||||
|
this.Scalar = scalar; |
||||
|
this.SixteenLane = new(in scalar); |
||||
|
this.EightLane = new(in scalar); |
||||
|
this.FourLane = new(in scalar); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the scalar conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public ScalarParameters Scalar { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the sixteen-lane conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public Vector512Parameters SixteenLane { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the eight-lane conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public Vector256Parameters EightLane { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the four-lane conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public Vector128Parameters FourLane { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores the scalar arithmetic and output scaling used by pinned libheif.
|
||||
|
/// </summary>
|
||||
|
private readonly struct ScalarParameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ScalarParameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
|
||||
|
/// <param name="bitDepth">The common source component precision.</param>
|
||||
|
public ScalarParameters(in HeifColorConversionParameters parameters, int bitDepth) |
||||
|
{ |
||||
|
this.LumaOffset = parameters.IsFullRange ? 0F : parameters.LumaBias; |
||||
|
this.LumaScale = parameters.IsFullRange ? 1F : 1.1689F; |
||||
|
this.ChromaMidpoint = parameters.ChromaBias; |
||||
|
this.ChromaScale = parameters.IsFullRange ? 1F : 1.1429F; |
||||
|
if (parameters.MatrixCoefficients == CicpMatrixCoefficients.Unspecified) |
||||
|
{ |
||||
|
// libheif falls back to these literal Rec.601 coefficients when no matrix is signaled. Deriving them
|
||||
|
// from Kr and Kb produces different float32 values and can move high-bit-depth green by one code value.
|
||||
|
this.RedCr = 1.402F; |
||||
|
this.GreenCb = -0.344136F; |
||||
|
this.GreenCr = -0.714136F; |
||||
|
this.BlueCb = 1.772F; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
float kr = parameters.Kr; |
||||
|
float kb = parameters.Kb; |
||||
|
this.RedCr = 2F * (-kr + 1F); |
||||
|
this.GreenCb = 2F * kb * (-kb + 1F) / (kb + kr - 1F); |
||||
|
this.GreenCr = 2F * kr * (-kr + 1F) / (kb + kr - 1F); |
||||
|
this.BlueCb = 2F * (-kb + 1F); |
||||
|
} |
||||
|
|
||||
|
this.Maximum = (1 << bitDepth) - 1; |
||||
|
this.OutputLeftShift = 16 - bitDepth; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma code-value offset removed before limited-range expansion.
|
||||
|
/// </summary>
|
||||
|
public float LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma range-expansion factor.
|
||||
|
/// </summary>
|
||||
|
public float LumaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the neutral chroma code value.
|
||||
|
/// </summary>
|
||||
|
public float ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma range-expansion factor.
|
||||
|
/// </summary>
|
||||
|
public float ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red contribution from Cr.
|
||||
|
/// </summary>
|
||||
|
public float RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from Cb.
|
||||
|
/// </summary>
|
||||
|
public float GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from Cr.
|
||||
|
/// </summary>
|
||||
|
public float GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue contribution from Cb.
|
||||
|
/// </summary>
|
||||
|
public float BlueCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the largest source-precision RGB code value.
|
||||
|
/// </summary>
|
||||
|
public int Maximum { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the left shift mapping source-precision RGB into 16-bit pixel storage.
|
||||
|
/// </summary>
|
||||
|
public int OutputLeftShift { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for sixteen-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector512Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public Vector512Parameters(in ScalarParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector512.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector512.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector512.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector512.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector512.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector512.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector512.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector512.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector512.Create(parameters.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma offset lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-midpoint lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> Maximum { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for eight-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector256Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public Vector256Parameters(in ScalarParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector256.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector256.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector256.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector256.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector256.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector256.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector256.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector256.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector256.Create(parameters.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma offset lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-midpoint lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> Maximum { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for four-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector128Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public Vector128Parameters(in ScalarParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector128.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector128.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector128.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector128.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector128.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector128.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector128.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector128.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector128.Create(parameters.Maximum); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma offset lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-midpoint lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma-scale lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum source-precision RGB lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> Maximum { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Buffers; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts high-bit-depth HEIF YUV planes to packed pixels through opaque 16-bit RGB.
|
||||
|
/// </summary>
|
||||
|
internal static partial class HeifYuvToRgb16Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Determines whether the pinned libheif-compatible high-bit-depth conversion supports the supplied planes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
|
||||
|
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
|
||||
|
/// <param name="lumaBitDepth">The luma sample precision in bits.</param>
|
||||
|
/// <param name="chromaBitDepth">The chroma sample precision in bits.</param>
|
||||
|
/// <param name="isMonochrome">Whether the image contains only luma samples.</param>
|
||||
|
/// <param name="mode">The resolved H.273 conversion operation.</param>
|
||||
|
/// <returns><see langword="true"/> when the planes can use this converter; otherwise, <see langword="false"/>.</returns>
|
||||
|
public static bool SupportsLibheifConversion( |
||||
|
int subsamplingX, |
||||
|
int subsamplingY, |
||||
|
int lumaBitDepth, |
||||
|
int chromaBitDepth, |
||||
|
bool isMonochrome, |
||||
|
HeifColorConversionMode mode) |
||||
|
=> (isMonochrome || (subsamplingX is 0 or 1 && subsamplingY is 0 or 1)) |
||||
|
&& lumaBitDepth is > 8 and <= 16 |
||||
|
&& (isMonochrome || chromaBitDepth == lumaBitDepth) |
||||
|
&& mode == HeifColorConversionMode.Coefficients; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts supported high-bit-depth HEVC planes using pinned libheif arithmetic and nearest chroma sampling.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
|
||||
|
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
|
||||
|
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
|
||||
|
/// <param name="buffer">The reconstructed component-plane buffer.</param>
|
||||
|
/// <param name="image">The destination image frame.</param>
|
||||
|
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
|
||||
|
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
|
||||
|
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
|
||||
|
public static void Convert<TPixel, TBuffer>( |
||||
|
Configuration configuration, |
||||
|
TBuffer buffer, |
||||
|
ImageFrame<TPixel> image, |
||||
|
in HeifColorConversionParameters parameters, |
||||
|
int sourceX, |
||||
|
int sourceY) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort> |
||||
|
{ |
||||
|
ConversionParameters conversionParameters = new(in parameters, buffer.LumaBitDepth); |
||||
|
|
||||
|
// Three planar rows and one packed Rgba64 row share a single image-lifetime allocation. The latter occupies
|
||||
|
// four UInt16 values per pixel, so the complete scratch requirement is seven samples per output pixel.
|
||||
|
using IMemoryOwner<ushort> rowOwner = configuration.MemoryAllocator.Allocate<ushort>(image.Width * 7); |
||||
|
Span<ushort> storage = rowOwner.GetSpan(); |
||||
|
Span<ushort> red = storage[..image.Width]; |
||||
|
Span<ushort> green = storage.Slice(image.Width, image.Width); |
||||
|
Span<ushort> blue = storage.Slice(image.Width * 2, image.Width); |
||||
|
Span<Rgba64> packed = MemoryMarshal.Cast<ushort, Rgba64>(storage[(image.Width * 3)..]); |
||||
|
|
||||
|
for (int y = 0; y < image.Height; y++) |
||||
|
{ |
||||
|
int lumaY = sourceY + y; |
||||
|
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width); |
||||
|
if (buffer.IsMonochrome) |
||||
|
{ |
||||
|
// Pinned libheif copies the reconstructed luma code value directly to RGB for monochrome images.
|
||||
|
// Scaling to the 16-bit pixel domain happens after that copy, without limited-range expansion.
|
||||
|
ConvertRow<LibheifMonochromeOperator>( |
||||
|
luma, |
||||
|
luma, |
||||
|
luma, |
||||
|
red, |
||||
|
green, |
||||
|
blue, |
||||
|
0, |
||||
|
in conversionParameters); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int subsamplingX = buffer.ChromaSubsamplingX; |
||||
|
int chromaY = lumaY >> buffer.ChromaSubsamplingY; |
||||
|
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX); |
||||
|
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(chromaY).Slice(sourceX >> subsamplingX); |
||||
|
|
||||
|
// libheif's selected direct conversion addresses the native chroma sample at x >> subsamplingX.
|
||||
|
// The HEIF crop boundary already keeps sourceX aligned to complete chroma samples.
|
||||
|
ConvertRow<LibheifCoefficientOperator>( |
||||
|
luma, |
||||
|
chromaBlue, |
||||
|
chromaRed, |
||||
|
red, |
||||
|
green, |
||||
|
blue, |
||||
|
subsamplingX, |
||||
|
in conversionParameters); |
||||
|
} |
||||
|
|
||||
|
HeifSampleConversion.PackRgba64(red, green, blue, packed); |
||||
|
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y); |
||||
|
PixelOperations<TPixel>.Instance.FromRgba64(configuration, packed, destination); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides the coefficient conversion executed by pinned libheif 1.23.1. Each SIMD lane carries one output pixel.
|
||||
|
/// Range expansion and matrix arithmetic remain in single precision, RGB is rounded and clipped at the coded
|
||||
|
/// precision, and the final integer shift reproduces libheif's separate high-bit-depth-to-eight-bit operation.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb8Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements pinned-libheif coefficient conversion for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifCoefficientOperator : IHeifYuvToRgb8Operator |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector512<int> y, |
||||
|
Vector512<int> cb, |
||||
|
Vector512<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector512<int> r, |
||||
|
out Vector512<int> g, |
||||
|
out Vector512<int> b) |
||||
|
{ |
||||
|
LibheifVector512Parameters values = parameters.LibheifSixteenLane; |
||||
|
Vector512<float> luma = (Vector512.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector512<float> blueDifference = (Vector512.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector512<float> redDifference = (Vector512.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector512<float> half = Vector512.Create(0.5F); |
||||
|
|
||||
|
// Sixteen independent samples use the same float32 ordering as the narrower paths. The closed operator
|
||||
|
// keeps this compatibility arithmetic outside the row dispatch while allowing an exact scalar fallback.
|
||||
|
Vector512<int> red = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.RedCr * redDifference) + half)); |
||||
|
Vector512<int> green = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); |
||||
|
Vector512<int> blue = Vector512.ConvertToInt32(Vector512.Truncate(luma + (values.BlueCb * blueDifference) + half)); |
||||
|
|
||||
|
r = Vector512.Clamp(red, default, values.Maximum) >> values.OutputShift; |
||||
|
g = Vector512.Clamp(green, default, values.Maximum) >> values.OutputShift; |
||||
|
b = Vector512.Clamp(blue, default, values.Maximum) >> values.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector256<int> y, |
||||
|
Vector256<int> cb, |
||||
|
Vector256<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector256<int> r, |
||||
|
out Vector256<int> g, |
||||
|
out Vector256<int> b) |
||||
|
{ |
||||
|
LibheifVector256Parameters values = parameters.LibheifEightLane; |
||||
|
Vector256<float> luma = (Vector256.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector256<float> blueDifference = (Vector256.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector256<float> redDifference = (Vector256.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector256<float> half = Vector256.Create(0.5F); |
||||
|
|
||||
|
// Eight YUV tuples remain planar across the YMM arithmetic. The expression association matches the
|
||||
|
// pinned scalar source, including the two successive green additions before truncation.
|
||||
|
Vector256<int> red = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.RedCr * redDifference) + half)); |
||||
|
Vector256<int> green = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); |
||||
|
Vector256<int> blue = Vector256.ConvertToInt32(Vector256.Truncate(luma + (values.BlueCb * blueDifference) + half)); |
||||
|
|
||||
|
r = Vector256.Clamp(red, default, values.Maximum) >> values.OutputShift; |
||||
|
g = Vector256.Clamp(green, default, values.Maximum) >> values.OutputShift; |
||||
|
b = Vector256.Clamp(blue, default, values.Maximum) >> values.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector128<int> y, |
||||
|
Vector128<int> cb, |
||||
|
Vector128<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector128<int> r, |
||||
|
out Vector128<int> g, |
||||
|
out Vector128<int> b) |
||||
|
{ |
||||
|
LibheifVector128Parameters values = parameters.LibheifFourLane; |
||||
|
Vector128<float> luma = (Vector128.ConvertToSingle(y) - values.LumaOffset) * values.LumaScale; |
||||
|
Vector128<float> blueDifference = (Vector128.ConvertToSingle(cb) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector128<float> redDifference = (Vector128.ConvertToSingle(cr) - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
Vector128<float> half = Vector128.Create(0.5F); |
||||
|
|
||||
|
// Truncate after the explicit half-unit bias to mirror C++ float-to-int conversion. Clipping in integer
|
||||
|
// lanes then preserves the source-precision boundary before the common eight-bit reduction shift.
|
||||
|
Vector128<int> red = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.RedCr * redDifference) + half)); |
||||
|
Vector128<int> green = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + half)); |
||||
|
Vector128<int> blue = Vector128.ConvertToInt32(Vector128.Truncate(luma + (values.BlueCb * blueDifference) + half)); |
||||
|
|
||||
|
r = Vector128.Clamp(red, default, values.Maximum) >> values.OutputShift; |
||||
|
g = Vector128.Clamp(green, default, values.Maximum) >> values.OutputShift; |
||||
|
b = Vector128.Clamp(blue, default, values.Maximum) >> values.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert(ushort y, ushort cb, ushort cr, in ConversionParameters parameters, out byte r, out byte g, out byte b) |
||||
|
{ |
||||
|
LibheifParameters values = parameters.LibheifScalar; |
||||
|
float luma = (y - values.LumaOffset) * values.LumaScale; |
||||
|
float blueDifference = (cb - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
float redDifference = (cr - values.ChromaMidpoint) * values.ChromaScale; |
||||
|
|
||||
|
// libheif's clip_f_u16 adds one half, truncates toward zero, and then clips. RGB is rounded before
|
||||
|
// the high-bit-depth plane is reduced, so moving the shift into the floating-point scale changes bytes.
|
||||
|
int red = (int)(luma + (values.RedCr * redDifference) + 0.5F); |
||||
|
int green = (int)(luma + (values.GreenCb * blueDifference) + (values.GreenCr * redDifference) + 0.5F); |
||||
|
int blue = (int)(luma + (values.BlueCb * blueDifference) + 0.5F); |
||||
|
|
||||
|
r = (byte)(Numerics.Clamp(red, 0, values.Maximum) >> values.OutputShift); |
||||
|
g = (byte)(Numerics.Clamp(green, 0, values.Maximum) >> values.OutputShift); |
||||
|
b = (byte)(Numerics.Clamp(blue, 0, values.Maximum) >> values.OutputShift); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides pinned-libheif monochrome presentation. The luma code value is reduced directly to eight bits and copied
|
||||
|
/// to all RGB components; signaled luma-range expansion is intentionally absent because libheif's direct monochrome
|
||||
|
/// operation does not apply it.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb8Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements pinned-libheif monochrome conversion for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifMonochromeOperator : IHeifYuvToRgb8Operator |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector512<int> y, |
||||
|
Vector512<int> cb, |
||||
|
Vector512<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector512<int> r, |
||||
|
out Vector512<int> g, |
||||
|
out Vector512<int> b) |
||||
|
{ |
||||
|
Vector512<int> value = y >> parameters.LibheifSixteenLane.OutputShift; |
||||
|
r = value; |
||||
|
g = value; |
||||
|
b = value; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector256<int> y, |
||||
|
Vector256<int> cb, |
||||
|
Vector256<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector256<int> r, |
||||
|
out Vector256<int> g, |
||||
|
out Vector256<int> b) |
||||
|
{ |
||||
|
Vector256<int> value = y >> parameters.LibheifEightLane.OutputShift; |
||||
|
r = value; |
||||
|
g = value; |
||||
|
b = value; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert( |
||||
|
Vector128<int> y, |
||||
|
Vector128<int> cb, |
||||
|
Vector128<int> cr, |
||||
|
in ConversionParameters parameters, |
||||
|
out Vector128<int> r, |
||||
|
out Vector128<int> g, |
||||
|
out Vector128<int> b) |
||||
|
{ |
||||
|
Vector128<int> value = y >> parameters.LibheifFourLane.OutputShift; |
||||
|
r = value; |
||||
|
g = value; |
||||
|
b = value; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Convert(ushort y, ushort cb, ushort cr, in ConversionParameters parameters, out byte r, out byte g, out byte b) |
||||
|
{ |
||||
|
byte value = (byte)(y >> parameters.LibheifScalar.OutputShift); |
||||
|
r = value; |
||||
|
g = value; |
||||
|
b = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,540 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Cicp; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
|
||||
|
/// </content>
|
||||
|
internal static partial class HeifYuvToRgb8Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Stores every scalar and SIMD coefficient representation resolved once for an image.
|
||||
|
/// </summary>
|
||||
|
private readonly struct ConversionParameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The scalar fixed-point coefficients.
|
||||
|
/// </summary>
|
||||
|
public readonly FixedPointParameters FixedPointScalar; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The four-lane SIMD coefficients.
|
||||
|
/// </summary>
|
||||
|
public readonly Vector128Parameters FixedPointFourLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The eight-lane SIMD coefficients.
|
||||
|
/// </summary>
|
||||
|
public readonly Vector256Parameters FixedPointEightLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The sixteen-lane SIMD coefficients.
|
||||
|
/// </summary>
|
||||
|
public readonly Vector512Parameters FixedPointSixteenLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The scalar pinned-libheif conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public readonly LibheifParameters LibheifScalar; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The four-lane pinned-libheif conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public readonly LibheifVector128Parameters LibheifFourLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The eight-lane pinned-libheif conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public readonly LibheifVector256Parameters LibheifEightLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The sixteen-lane pinned-libheif conversion parameters.
|
||||
|
/// </summary>
|
||||
|
public readonly LibheifVector512Parameters LibheifSixteenLane; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ConversionParameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The shared floating-point conversion parameters.</param>
|
||||
|
/// <param name="bitDepth">The common source component precision.</param>
|
||||
|
public ConversionParameters(in HeifColorConversionParameters parameters, int bitDepth) |
||||
|
{ |
||||
|
FixedPointParameters scalar = new(in parameters); |
||||
|
this.FixedPointScalar = scalar; |
||||
|
this.FixedPointFourLane = new(in scalar); |
||||
|
this.FixedPointEightLane = new(in scalar); |
||||
|
this.FixedPointSixteenLane = new(in scalar); |
||||
|
|
||||
|
LibheifParameters libheif = new(in parameters, bitDepth); |
||||
|
this.LibheifScalar = libheif; |
||||
|
this.LibheifFourLane = new(in libheif); |
||||
|
this.LibheifEightLane = new(in libheif); |
||||
|
this.LibheifSixteenLane = new(in libheif); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores the scalar fixed-point coefficients resolved for one image.
|
||||
|
/// </summary>
|
||||
|
private readonly struct FixedPointParameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="FixedPointParameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The shared floating-point conversion parameters.</param>
|
||||
|
public FixedPointParameters(in HeifColorConversionParameters parameters) |
||||
|
{ |
||||
|
float scale = 1 << CoefficientShift; |
||||
|
|
||||
|
// Rounding each image-invariant coefficient once gives the integer kernel eight fractional bits.
|
||||
|
// The signed green coefficients retain the exact addition and rounding order used by every SIMD lane.
|
||||
|
this.RedCr = (int)MathF.Round(parameters.RedChromaScale * scale, MidpointRounding.AwayFromZero); |
||||
|
this.GreenCb = -(int)MathF.Round(parameters.GreenBlueChromaScale * scale, MidpointRounding.AwayFromZero); |
||||
|
this.GreenCr = -(int)MathF.Round(parameters.GreenRedChromaScale * scale, MidpointRounding.AwayFromZero); |
||||
|
this.BlueCb = (int)MathF.Round(parameters.BlueChromaScale * scale, MidpointRounding.AwayFromZero); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red contribution from centered Cr.
|
||||
|
/// </summary>
|
||||
|
public int RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from centered Cb.
|
||||
|
/// </summary>
|
||||
|
public int GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from centered Cr.
|
||||
|
/// </summary>
|
||||
|
public int GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue contribution from centered Cb.
|
||||
|
/// </summary>
|
||||
|
public int BlueCb { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts the fixed-point coefficients for four-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector128Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector128Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
||||
|
public Vector128Parameters(in FixedPointParameters parameters) |
||||
|
{ |
||||
|
this.ChromaMidpoint = Vector128.Create(HeifYuvToRgb8Converter.ChromaMidpoint); |
||||
|
this.RoundingBias = Vector128.Create(HeifYuvToRgb8Converter.RoundingBias); |
||||
|
this.Maximum = Vector128.Create((int)byte.MaxValue); |
||||
|
this.RedCr = Vector128.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector128.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector128.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector128.Create(parameters.BlueCb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the neutral chroma code-value lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the fixed-point rounding-bias lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> RoundingBias { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum eight-bit sample lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector128<int> BlueCb { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts the fixed-point coefficients for eight-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector256Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector256Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
||||
|
public Vector256Parameters(in FixedPointParameters parameters) |
||||
|
{ |
||||
|
this.ChromaMidpoint = Vector256.Create(HeifYuvToRgb8Converter.ChromaMidpoint); |
||||
|
this.RoundingBias = Vector256.Create(HeifYuvToRgb8Converter.RoundingBias); |
||||
|
this.Maximum = Vector256.Create((int)byte.MaxValue); |
||||
|
this.RedCr = Vector256.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector256.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector256.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector256.Create(parameters.BlueCb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the neutral chroma code-value lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the fixed-point rounding-bias lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> RoundingBias { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum eight-bit sample lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector256<int> BlueCb { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts the fixed-point coefficients for sixteen-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct Vector512Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Vector512Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar fixed-point coefficients.</param>
|
||||
|
public Vector512Parameters(in FixedPointParameters parameters) |
||||
|
{ |
||||
|
this.ChromaMidpoint = Vector512.Create(HeifYuvToRgb8Converter.ChromaMidpoint); |
||||
|
this.RoundingBias = Vector512.Create(HeifYuvToRgb8Converter.RoundingBias); |
||||
|
this.Maximum = Vector512.Create((int)byte.MaxValue); |
||||
|
this.RedCr = Vector512.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector512.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector512.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector512.Create(parameters.BlueCb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the neutral chroma code-value lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the fixed-point rounding-bias lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> RoundingBias { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the maximum eight-bit sample lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green Cr coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue Cb coefficient lanes.
|
||||
|
/// </summary>
|
||||
|
public Vector512<int> BlueCb { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores the scalar coefficients and range values used by pinned libheif 1.23.1.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifParameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LibheifParameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The resolved H.273 matrix and range values.</param>
|
||||
|
/// <param name="bitDepth">The common source component precision.</param>
|
||||
|
public LibheifParameters(in HeifColorConversionParameters parameters, int bitDepth) |
||||
|
{ |
||||
|
this.LumaOffset = parameters.IsFullRange ? 0F : parameters.LumaBias; |
||||
|
this.LumaScale = parameters.IsFullRange ? 1F : 1.1689F; |
||||
|
this.ChromaMidpoint = parameters.ChromaBias; |
||||
|
this.ChromaScale = parameters.IsFullRange ? 1F : 1.1429F; |
||||
|
if (parameters.MatrixCoefficients == CicpMatrixCoefficients.Unspecified) |
||||
|
{ |
||||
|
// libheif falls back to these literal Rec.601 coefficients when no matrix is signaled.
|
||||
|
this.RedCr = 1.402F; |
||||
|
this.GreenCb = -0.344136F; |
||||
|
this.GreenCr = -0.714136F; |
||||
|
this.BlueCb = 1.772F; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
float kr = parameters.Kr; |
||||
|
float kb = parameters.Kb; |
||||
|
this.RedCr = 2F * (-kr + 1F); |
||||
|
this.GreenCb = 2F * kb * (-kb + 1F) / (kb + kr - 1F); |
||||
|
this.GreenCr = 2F * kr * (-kr + 1F) / (kb + kr - 1F); |
||||
|
this.BlueCb = 2F * (-kb + 1F); |
||||
|
} |
||||
|
|
||||
|
this.Maximum = (1 << bitDepth) - 1; |
||||
|
this.OutputShift = bitDepth - 8; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma code-value offset removed before limited-range expansion.
|
||||
|
/// </summary>
|
||||
|
public float LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the luma range-expansion factor.
|
||||
|
/// </summary>
|
||||
|
public float LumaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the neutral chroma code value.
|
||||
|
/// </summary>
|
||||
|
public float ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chroma range-expansion factor.
|
||||
|
/// </summary>
|
||||
|
public float ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red contribution from Cr.
|
||||
|
/// </summary>
|
||||
|
public float RedCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from Cb.
|
||||
|
/// </summary>
|
||||
|
public float GreenCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green contribution from Cr.
|
||||
|
/// </summary>
|
||||
|
public float GreenCr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue contribution from Cb.
|
||||
|
/// </summary>
|
||||
|
public float BlueCb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the largest RGB code value at the source precision.
|
||||
|
/// </summary>
|
||||
|
public int Maximum { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the right shift reducing source-precision RGB to eight bits.
|
||||
|
/// </summary>
|
||||
|
public int OutputShift { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for four-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifVector128Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LibheifVector128Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public LibheifVector128Parameters(in LibheifParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector128.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector128.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector128.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector128.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector128.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector128.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector128.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector128.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector128.Create(parameters.Maximum); |
||||
|
this.OutputShift = parameters.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <summary>Gets the luma offset lanes.</summary>
|
||||
|
public Vector128<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>Gets the luma scale lanes.</summary>
|
||||
|
public Vector128<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-midpoint lanes.</summary>
|
||||
|
public Vector128<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-scale lanes.</summary>
|
||||
|
public Vector128<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the red Cr coefficient lanes.</summary>
|
||||
|
public Vector128<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cb coefficient lanes.</summary>
|
||||
|
public Vector128<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cr coefficient lanes.</summary>
|
||||
|
public Vector128<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the blue Cb coefficient lanes.</summary>
|
||||
|
public Vector128<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the maximum RGB code-value lanes.</summary>
|
||||
|
public Vector128<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>Gets the output reduction shift.</summary>
|
||||
|
public int OutputShift { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for eight-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifVector256Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LibheifVector256Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public LibheifVector256Parameters(in LibheifParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector256.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector256.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector256.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector256.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector256.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector256.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector256.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector256.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector256.Create(parameters.Maximum); |
||||
|
this.OutputShift = parameters.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <summary>Gets the luma offset lanes.</summary>
|
||||
|
public Vector256<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>Gets the luma scale lanes.</summary>
|
||||
|
public Vector256<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-midpoint lanes.</summary>
|
||||
|
public Vector256<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-scale lanes.</summary>
|
||||
|
public Vector256<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the red Cr coefficient lanes.</summary>
|
||||
|
public Vector256<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cb coefficient lanes.</summary>
|
||||
|
public Vector256<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cr coefficient lanes.</summary>
|
||||
|
public Vector256<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the blue Cb coefficient lanes.</summary>
|
||||
|
public Vector256<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the maximum RGB code-value lanes.</summary>
|
||||
|
public Vector256<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>Gets the output reduction shift.</summary>
|
||||
|
public int OutputShift { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Broadcasts pinned-libheif coefficients for sixteen-lane conversion.
|
||||
|
/// </summary>
|
||||
|
private readonly struct LibheifVector512Parameters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LibheifVector512Parameters"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameters">The scalar pinned-libheif coefficients.</param>
|
||||
|
public LibheifVector512Parameters(in LibheifParameters parameters) |
||||
|
{ |
||||
|
this.LumaOffset = Vector512.Create(parameters.LumaOffset); |
||||
|
this.LumaScale = Vector512.Create(parameters.LumaScale); |
||||
|
this.ChromaMidpoint = Vector512.Create(parameters.ChromaMidpoint); |
||||
|
this.ChromaScale = Vector512.Create(parameters.ChromaScale); |
||||
|
this.RedCr = Vector512.Create(parameters.RedCr); |
||||
|
this.GreenCb = Vector512.Create(parameters.GreenCb); |
||||
|
this.GreenCr = Vector512.Create(parameters.GreenCr); |
||||
|
this.BlueCb = Vector512.Create(parameters.BlueCb); |
||||
|
this.Maximum = Vector512.Create(parameters.Maximum); |
||||
|
this.OutputShift = parameters.OutputShift; |
||||
|
} |
||||
|
|
||||
|
/// <summary>Gets the luma offset lanes.</summary>
|
||||
|
public Vector512<float> LumaOffset { get; } |
||||
|
|
||||
|
/// <summary>Gets the luma scale lanes.</summary>
|
||||
|
public Vector512<float> LumaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-midpoint lanes.</summary>
|
||||
|
public Vector512<float> ChromaMidpoint { get; } |
||||
|
|
||||
|
/// <summary>Gets the chroma-scale lanes.</summary>
|
||||
|
public Vector512<float> ChromaScale { get; } |
||||
|
|
||||
|
/// <summary>Gets the red Cr coefficient lanes.</summary>
|
||||
|
public Vector512<float> RedCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cb coefficient lanes.</summary>
|
||||
|
public Vector512<float> GreenCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the green Cr coefficient lanes.</summary>
|
||||
|
public Vector512<float> GreenCr { get; } |
||||
|
|
||||
|
/// <summary>Gets the blue Cb coefficient lanes.</summary>
|
||||
|
public Vector512<float> BlueCb { get; } |
||||
|
|
||||
|
/// <summary>Gets the maximum RGB code-value lanes.</summary>
|
||||
|
public Vector512<int> Maximum { get; } |
||||
|
|
||||
|
/// <summary>Gets the output reduction shift.</summary>
|
||||
|
public int OutputShift { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,207 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Buffers; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Cicp; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Components; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts HEIF YUV planes directly to packed eight-bit RGB pixels.
|
||||
|
/// </summary>
|
||||
|
internal static partial class HeifYuvToRgb8Converter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The fixed-point precision used for H.273 matrix coefficients.
|
||||
|
/// </summary>
|
||||
|
private const int CoefficientShift = 8; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The half-unit bias used before fixed-point coefficient results are shifted to integer samples.
|
||||
|
/// </summary>
|
||||
|
private const int RoundingBias = 1 << (CoefficientShift - 1); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The neutral code value for full-range eight-bit chroma.
|
||||
|
/// </summary>
|
||||
|
private const int ChromaMidpoint = 128; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the specialized fixed-point conversion supports the supplied plane and color description.
|
||||
|
/// </summary>
|
||||
|
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
|
||||
|
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
|
||||
|
/// <param name="lumaBitDepth">The luma sample precision in bits.</param>
|
||||
|
/// <param name="chromaBitDepth">The chroma sample precision in bits.</param>
|
||||
|
/// <param name="isFullRange">Whether the samples use the complete numeric range.</param>
|
||||
|
/// <param name="matrixCoefficients">The H.273 matrix-coefficient code point.</param>
|
||||
|
/// <param name="mode">The resolved H.273 conversion operation.</param>
|
||||
|
/// <returns><see langword="true"/> when the planes can use this converter; otherwise, <see langword="false"/>.</returns>
|
||||
|
public static bool SupportsFixedPointConversion( |
||||
|
int subsamplingX, |
||||
|
int subsamplingY, |
||||
|
int lumaBitDepth, |
||||
|
int chromaBitDepth, |
||||
|
bool isFullRange, |
||||
|
CicpMatrixCoefficients matrixCoefficients, |
||||
|
HeifColorConversionMode mode) |
||||
|
=> subsamplingX == 1 |
||||
|
&& subsamplingY == 1 |
||||
|
&& lumaBitDepth == 8 |
||||
|
&& chromaBitDepth == 8 |
||||
|
&& isFullRange |
||||
|
&& matrixCoefficients == CicpMatrixCoefficients.Unspecified |
||||
|
&& mode == HeifColorConversionMode.Coefficients; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the pinned libheif-compatible conversion supports the supplied plane and color description.
|
||||
|
/// </summary>
|
||||
|
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
|
||||
|
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
|
||||
|
/// <param name="lumaBitDepth">The luma sample precision in bits.</param>
|
||||
|
/// <param name="chromaBitDepth">The chroma sample precision in bits.</param>
|
||||
|
/// <param name="isMonochrome">Whether the image contains only luma samples.</param>
|
||||
|
/// <param name="mode">The resolved H.273 conversion operation.</param>
|
||||
|
/// <returns><see langword="true"/> when the planes can use this converter; otherwise, <see langword="false"/>.</returns>
|
||||
|
public static bool SupportsLibheifConversion( |
||||
|
int subsamplingX, |
||||
|
int subsamplingY, |
||||
|
int lumaBitDepth, |
||||
|
int chromaBitDepth, |
||||
|
bool isMonochrome, |
||||
|
HeifColorConversionMode mode) |
||||
|
=> (isMonochrome || (subsamplingX is 0 or 1 && subsamplingY is 0 or 1)) |
||||
|
&& lumaBitDepth == 8 |
||||
|
&& (isMonochrome || chromaBitDepth == 8) |
||||
|
&& mode == HeifColorConversionMode.Coefficients; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts supported HEIF component planes to packed pixels using integer SIMD with a scalar tail.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
|
||||
|
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
|
||||
|
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
|
||||
|
/// <param name="buffer">The reconstructed component-plane buffer.</param>
|
||||
|
/// <param name="image">The destination image frame.</param>
|
||||
|
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
|
||||
|
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
|
||||
|
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
|
||||
|
public static void ConvertFixedPoint<TPixel, TBuffer>( |
||||
|
Configuration configuration, |
||||
|
TBuffer buffer, |
||||
|
ImageFrame<TPixel> image, |
||||
|
in HeifColorConversionParameters parameters, |
||||
|
int sourceX, |
||||
|
int sourceY) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort> |
||||
|
{ |
||||
|
ConversionParameters conversionParameters = new(in parameters, 8); |
||||
|
using IMemoryOwner<byte> componentOwner = configuration.MemoryAllocator.Allocate<byte>(image.Width * 3); |
||||
|
Span<byte> components = componentOwner.GetSpan(); |
||||
|
Span<byte> red = components[..image.Width]; |
||||
|
Span<byte> green = components.Slice(image.Width, image.Width); |
||||
|
Span<byte> blue = components.Slice(image.Width * 2, image.Width); |
||||
|
|
||||
|
// The value-type buffer closes the row-access contract at the call site. Constrained calls are therefore
|
||||
|
// devirtualized without boxing while keeping codec-specific buffer ownership outside the color pipeline.
|
||||
|
for (int y = 0; y < image.Height; y++) |
||||
|
{ |
||||
|
int lumaY = sourceY + y; |
||||
|
|
||||
|
// The codec boundary validates 4:2:0 crop offsets in complete chroma-sample units. Each native chroma
|
||||
|
// sample therefore covers one 2x2 luma cell without an alignment branch in the SIMD loop.
|
||||
|
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width); |
||||
|
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(lumaY >> 1).Slice(sourceX >> 1); |
||||
|
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(lumaY >> 1).Slice(sourceX >> 1); |
||||
|
|
||||
|
ConvertRow<FixedPointCoefficientOperator>( |
||||
|
luma, |
||||
|
chromaBlue, |
||||
|
chromaRed, |
||||
|
red, |
||||
|
green, |
||||
|
blue, |
||||
|
1, |
||||
|
in conversionParameters); |
||||
|
|
||||
|
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y); |
||||
|
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts supported HEVC planes with the arithmetic and nearest-sample traversal used by pinned libheif.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
|
||||
|
/// <typeparam name="TBuffer">The codec adapter that exposes reconstructed component rows.</typeparam>
|
||||
|
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
|
||||
|
/// <param name="buffer">The reconstructed component-plane buffer.</param>
|
||||
|
/// <param name="image">The destination image frame.</param>
|
||||
|
/// <param name="parameters">The resolved H.273 conversion parameters.</param>
|
||||
|
/// <param name="sourceX">The horizontal luma-sample offset of the output window.</param>
|
||||
|
/// <param name="sourceY">The vertical luma-sample offset of the output window.</param>
|
||||
|
public static void ConvertLibheif<TPixel, TBuffer>( |
||||
|
Configuration configuration, |
||||
|
TBuffer buffer, |
||||
|
ImageFrame<TPixel> image, |
||||
|
in HeifColorConversionParameters parameters, |
||||
|
int sourceX, |
||||
|
int sourceY) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
where TBuffer : struct, IHeifPlanarSampleBuffer<ushort> |
||||
|
{ |
||||
|
ConversionParameters conversionParameters = new(in parameters, buffer.LumaBitDepth); |
||||
|
using IMemoryOwner<byte> componentOwner = configuration.MemoryAllocator.Allocate<byte>(image.Width * 3); |
||||
|
Span<byte> components = componentOwner.GetSpan(); |
||||
|
Span<byte> red = components[..image.Width]; |
||||
|
Span<byte> green = components.Slice(image.Width, image.Width); |
||||
|
Span<byte> blue = components.Slice(image.Width * 2, image.Width); |
||||
|
|
||||
|
for (int y = 0; y < image.Height; y++) |
||||
|
{ |
||||
|
int lumaY = sourceY + y; |
||||
|
ReadOnlySpan<ushort> luma = buffer.GetLumaRowSpan(lumaY).Slice(sourceX, image.Width); |
||||
|
if (buffer.IsMonochrome) |
||||
|
{ |
||||
|
// Pinned libheif reduces monochrome precision first and copies that code value to RGB. It does not
|
||||
|
// apply the signaled limited-range expansion used by its three-component conversion operation.
|
||||
|
ConvertRow<LibheifMonochromeOperator>( |
||||
|
luma, |
||||
|
luma, |
||||
|
luma, |
||||
|
red, |
||||
|
green, |
||||
|
blue, |
||||
|
0, |
||||
|
in conversionParameters); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int subsamplingX = buffer.ChromaSubsamplingX; |
||||
|
int chromaY = lumaY >> buffer.ChromaSubsamplingY; |
||||
|
|
||||
|
// The HEIF crop boundary validates horizontal offsets in complete chroma-sample units. Slicing once
|
||||
|
// therefore preserves libheif's x >> subsampling mapping without a phase branch in the SIMD loop.
|
||||
|
ReadOnlySpan<ushort> chromaBlue = buffer.GetChromaBlueRowSpan(chromaY).Slice(sourceX >> subsamplingX); |
||||
|
ReadOnlySpan<ushort> chromaRed = buffer.GetChromaRedRowSpan(chromaY).Slice(sourceX >> subsamplingX); |
||||
|
|
||||
|
ConvertRow<LibheifCoefficientOperator>( |
||||
|
luma, |
||||
|
chromaBlue, |
||||
|
chromaRed, |
||||
|
red, |
||||
|
green, |
||||
|
blue, |
||||
|
subsamplingX, |
||||
|
in conversionParameters); |
||||
|
} |
||||
|
|
||||
|
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y); |
||||
|
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-difference-weighted-compound.png
0
tests/Images/Input/Heif/Av1/Conformance/libavif-webp-logo-distance-weighted-compound-libavif.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifSequencesWithSelectableCompoundAndInterIntraMatchesPinnedReferences_Rgba32_libavif-webp-logo-difference-weighted-compound.png
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a |
||||
|
size 445507 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:42c898b491383da95bf01676a8e8f7aa221a171f4b211f08ce5fa6a1c313bfb1 |
||||
|
size 254922 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:5752cc38c38054018850157c1b11805cae28048a58f422389de7a37240af3593 |
||||
|
size 259024 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:bb558d383a95d9dcbcf39637e7295f69881aa75b99134c84e98b02405849f956 |
||||
|
size 2270060 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:988480de65a19b98f7df3d3298bd432beb324b7e7fd4a67370d37bba13526a53 |
||||
|
size 599400 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:704acdf5dabe6d6550373d9f32216fdd0ff17549a6bafba870fab353a0138150 |
||||
|
size 597546 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:82e48bd7d67c6d6bf724f1dd4a07d4ba5ba3532ebab387bd152aca2e54354fa7 |
||||
|
size 596229 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137 |
||||
|
size 263001 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:1f7926906a68d1b0e4813b738ec085e050c358d66b2e5879515db6803e7e9137 |
||||
|
size 263001 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:490756396b21f4aeacc40634483f40c0c42f77d8f70005449e09682729f6e52a |
||||
|
size 83380 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:5a974be508d2f5122ea7e7182da0c4b5313049b5229e1cd4fec77bb57c2b759f |
||||
|
size 249788 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:9b4e6e260ffe16dc728d0064cfa4852730426f14ed6ee16ae074272deee3cff9 |
||||
|
size 157543 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:4b38aee5d910a934868a8ddba5756363ac2f6155c756912f310d2e1a7ef2ccf5 |
||||
|
size 4683537 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:5af1901f1bbd30d2dd98f437cad37b458f2b2defad5b0948959952daf7a48511 |
||||
|
size 2422557 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:8c77bdce5adbf111756bd6f07d5e8550384cb9327e5326dca6a704085a662757 |
||||
|
size 5152549 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:ccb446aec97dc88379861345dd6066b121341c63d0f5fb2ef9a4b5211266e795 |
||||
|
size 952927 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:8fbc99537fa7e121f41a217a196875f95053662ef3ff21c453b8c203e64eb2ba |
||||
|
size 255982 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:2b2eaee6ec269a2658d6a7e7e3be47d2cba8b2d158478606b40cd266efbecfd3 |
||||
|
size 250386 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:5b5d91da29e9ad97cfd64d0b32023e770f7035b51ca37da1825294928d3a2030 |
||||
|
size 221441 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:f93b25daf35a51c5d06e13260406d6206cc7fda532812a43c7c2c7fcdcee2ee8 |
||||
|
size 337393 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:60beaf4ceb7ee35e356225783e04734b8b5e364f93448f716f8ade10f92e2ac4 |
||||
|
size 339585 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:e9f72db003768c465aae57bd4414cf0a7ca844ac0989a7065ba1f423e131b8a8 |
||||
|
size 327991 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:52d44eea9a312d6dfc38ff0f52172e6ccd2aa7ee11ec4427c8dc22a223a12830 |
||||
|
size 455775 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:3951b586abcbcc5522f574cbeca8aeb4ab96e6da848b8b2a46823bb6fb51f43e |
||||
|
size 476713 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:03ccce9838ce1914b644b9f439eeebfa687488690e2dc2a5fdae4cba3225213a |
||||
|
size 445507 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:4a06102b72c546060d1693244e5a9f6de9f5924a634e27450dbde189ca46da4f |
||||
|
size 159244 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:e57731b800d6a7c0e34d3b07ab7ba5690f2b66141c2865c64dc3fdc5afecbcb7 |
||||
|
size 247858 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:1b8b3953335f607c9691c626452dabdf68595017a7eeb94e9bee96c340bf9b0a |
||||
|
size 313388 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:21614e3259ce3ff3565d729600e8aa268691f3450bc91eb2bfb0900864576cf3 |
||||
|
size 5872698 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:007694be6991762d3b7637fa2b6215191a470888695d9fbc098b21058f66a949 |
||||
|
size 10716965 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:7d1d7749d15635514d824d7f52aeb92ac7933feaa98d2c59db9eaebbfd17b183 |
||||
|
size 246199 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:b525da1d3cbc0fede549913eed0f721baf85a4a2c2bc56a1a6d42e326b2ef9ef |
||||
|
size 6251753 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:a96601049c2e6dc5808d30297a5c1fcc7060a3c0bae1ce0c13f7d4d8ae7c2b9e |
||||
|
size 10856528 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:de182fc4852448839fc9090ace49fe375401408aed131c632dcd9b28f42b3dcd |
||||
|
size 3486224 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:19a83def25a4deea548a1328c69fd8f1c771d32d5bf86c9571c5368cafad80c1 |
||||
|
size 552593 |
||||
@ -1,3 +0,0 @@ |
|||||
version https://git-lfs.github.com/spec/v1 |
|
||||
oid sha256:b3d467add4b0a41575228af86011a4f0d003003df267c639ca7dab4a290997a2 |
|
||||
size 2729225 |
|
||||
@ -1,3 +0,0 @@ |
|||||
version https://git-lfs.github.com/spec/v1 |
|
||||
oid sha256:d94be2ff029467ba3f0cc6e48c735966edf9c57424c0175fceed14a5d9434dbb |
|
||||
size 2427308 |
|
||||
@ -1,3 +0,0 @@ |
|||||
version https://git-lfs.github.com/spec/v1 |
|
||||
oid sha256:f27738eb42338db6db0ab3e8b392ac5407e51473ffceefd00f318b06b124d249 |
|
||||
size 5167261 |
|
||||
@ -1,3 +0,0 @@ |
|||||
version https://git-lfs.github.com/spec/v1 |
|
||||
oid sha256:e4b107ab968f826a9d710be90255842af0315e475516b3b5cf3361dcd226fd81 |
|
||||
size 953520 |
|
||||
@ -1,3 +0,0 @@ |
|||||
version https://git-lfs.github.com/spec/v1 |
|
||||
oid sha256:bf61f91cbc21d8172d4000dcffb2e5a6e1d56877cc354465548f00bf106b8923 |
|
||||
size 255832 |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue