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