mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 666 additions and 21 deletions
@ -0,0 +1,262 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Color; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color; |
|||
|
|||
/// <content>
|
|||
/// Provides fixed-point scalar and SIMD coefficient storage for eight-bit 4:2:0 conversion.
|
|||
/// </content>
|
|||
internal static partial class HevcYuv420ToRgb8Converter |
|||
{ |
|||
/// <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(HevcYuv420ToRgb8Converter.ChromaMidpoint); |
|||
this.RoundingBias = Vector128.Create(HevcYuv420ToRgb8Converter.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(HevcYuv420ToRgb8Converter.ChromaMidpoint); |
|||
this.RoundingBias = Vector256.Create(HevcYuv420ToRgb8Converter.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(HevcYuv420ToRgb8Converter.ChromaMidpoint); |
|||
this.RoundingBias = Vector512.Create(HevcYuv420ToRgb8Converter.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; } |
|||
} |
|||
} |
|||
@ -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; |
|||
using static SixLabors.ImageSharp.Formats.Heif.Color.HeifColorConverterBase; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color; |
|||
|
|||
/// <content>
|
|||
/// Provides the fixed-point scalar and SIMD row kernels for eight-bit 4:2:0 conversion.
|
|||
/// </content>
|
|||
internal static partial class HevcYuv420ToRgb8Converter |
|||
{ |
|||
/// <summary>
|
|||
/// Converts one luma row and its nearest native chroma row to planar eight-bit RGB.
|
|||
/// </summary>
|
|||
/// <param name="luma">The full-resolution luma samples.</param>
|
|||
/// <param name="chromaBlue">The half-width blue-difference samples.</param>
|
|||
/// <param name="chromaRed">The half-width 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="parameters">The fixed-point matrix coefficients.</param>
|
|||
private static void ConvertRow( |
|||
ReadOnlySpan<ushort> luma, |
|||
ReadOnlySpan<ushort> chromaBlue, |
|||
ReadOnlySpan<ushort> chromaRed, |
|||
Span<byte> red, |
|||
Span<byte> green, |
|||
Span<byte> blue, |
|||
in ConversionParameters parameters) |
|||
{ |
|||
ref ushort lumaBase = ref MemoryMarshal.GetReference(luma); |
|||
ref ushort chromaBlueBase = ref MemoryMarshal.GetReference(chromaBlue); |
|||
ref ushort chromaRedBase = ref MemoryMarshal.GetReference(chromaRed); |
|||
ref byte redBase = ref MemoryMarshal.GetReference(red); |
|||
ref byte greenBase = ref MemoryMarshal.GetReference(green); |
|||
ref byte blueBase = ref MemoryMarshal.GetReference(blue); |
|||
int x = 0; |
|||
|
|||
// The shared offset lets the widest supported register consume the row first. Narrower widths then
|
|||
// handle the complete remainder, leaving at most three pixels for the scalar fallback.
|
|||
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 = LoadRepeatedVector512(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
|||
Vector512<int> cr = LoadRepeatedVector512(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
|||
|
|||
Convert(y, cb, cr, in parameters.SixteenLane, out Vector512<int> r, out Vector512<int> g, out Vector512<int> b); |
|||
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x)); |
|||
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x)); |
|||
HeifByteSampleStorer.Store(b, 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 = LoadRepeatedVector256(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
|||
Vector256<int> cr = LoadRepeatedVector256(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
|||
|
|||
Convert(y, cb, cr, in parameters.EightLane, out Vector256<int> r, out Vector256<int> g, out Vector256<int> b); |
|||
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x)); |
|||
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x)); |
|||
HeifByteSampleStorer.Store(b, 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 = LoadRepeatedVector128(ref Unsafe.Add(ref chromaBlueBase, x >> 1)); |
|||
Vector128<int> cr = LoadRepeatedVector128(ref Unsafe.Add(ref chromaRedBase, x >> 1)); |
|||
|
|||
Convert(y, cb, cr, in parameters.FourLane, out Vector128<int> r, out Vector128<int> g, out Vector128<int> b); |
|||
HeifByteSampleStorer.Store(r, ref Unsafe.Add(ref redBase, x)); |
|||
HeifByteSampleStorer.Store(g, ref Unsafe.Add(ref greenBase, x)); |
|||
HeifByteSampleStorer.Store(b, ref Unsafe.Add(ref blueBase, x)); |
|||
} |
|||
} |
|||
|
|||
for (; x < luma.Length; x++) |
|||
{ |
|||
Convert( |
|||
Unsafe.Add(ref lumaBase, x), |
|||
Unsafe.Add(ref chromaBlueBase, x >> 1), |
|||
Unsafe.Add(ref chromaRedBase, x >> 1), |
|||
in parameters.Scalar, |
|||
out Unsafe.Add(ref redBase, x), |
|||
out Unsafe.Add(ref greenBase, x), |
|||
out Unsafe.Add(ref blueBase, x)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads sixteen luma samples as signed 32-bit SIMD lanes.
|
|||
/// </summary>
|
|||
/// <param name="source">The first native luma sample.</param>
|
|||
/// <returns>The widened luma 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 luma samples as signed 32-bit SIMD lanes.
|
|||
/// </summary>
|
|||
/// <param name="source">The first native luma sample.</param>
|
|||
/// <returns>The widened luma 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 luma samples as signed 32-bit SIMD lanes.
|
|||
/// </summary>
|
|||
/// <param name="source">The first native luma sample.</param>
|
|||
/// <returns>The widened luma 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(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts one coefficient-based H.273 YCbCr sample to eight-bit 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 fixed-point matrix coefficients.</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>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void Convert(ushort y, ushort cb, ushort cr, in FixedPointParameters parameters, out byte r, out byte g, out byte b) |
|||
{ |
|||
int centeredBlue = cb - ChromaMidpoint; |
|||
int centeredRed = cr - ChromaMidpoint; |
|||
int red = y + (((parameters.RedCr * centeredRed) + RoundingBias) >> CoefficientShift); |
|||
int green = y + (((parameters.GreenCb * centeredBlue) + (parameters.GreenCr * centeredRed) + RoundingBias) >> CoefficientShift); |
|||
int blue = y + (((parameters.BlueCb * centeredBlue) + RoundingBias) >> CoefficientShift); |
|||
|
|||
r = (byte)Numerics.Clamp(red, 0, byte.MaxValue); |
|||
g = (byte)Numerics.Clamp(green, 0, byte.MaxValue); |
|||
b = (byte)Numerics.Clamp(blue, 0, byte.MaxValue); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts four coefficient-based H.273 YCbCr samples to eight-bit 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 fixed-point matrix coefficient lanes.</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>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void Convert( |
|||
Vector128<int> y, |
|||
Vector128<int> cb, |
|||
Vector128<int> cr, |
|||
in Vector128Parameters parameters, |
|||
out Vector128<int> r, |
|||
out Vector128<int> g, |
|||
out Vector128<int> b) |
|||
{ |
|||
cb -= parameters.ChromaMidpoint; |
|||
cr -= parameters.ChromaMidpoint; |
|||
r = Vector128.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum); |
|||
g = Vector128.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum); |
|||
b = Vector128.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector128<int>.Zero, parameters.Maximum); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts eight coefficient-based H.273 YCbCr samples to eight-bit 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 fixed-point matrix coefficient lanes.</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>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void Convert( |
|||
Vector256<int> y, |
|||
Vector256<int> cb, |
|||
Vector256<int> cr, |
|||
in Vector256Parameters parameters, |
|||
out Vector256<int> r, |
|||
out Vector256<int> g, |
|||
out Vector256<int> b) |
|||
{ |
|||
cb -= parameters.ChromaMidpoint; |
|||
cr -= parameters.ChromaMidpoint; |
|||
r = Vector256.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum); |
|||
g = Vector256.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum); |
|||
b = Vector256.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector256<int>.Zero, parameters.Maximum); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts sixteen coefficient-based H.273 YCbCr samples to eight-bit 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 fixed-point matrix coefficient lanes.</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>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void Convert( |
|||
Vector512<int> y, |
|||
Vector512<int> cb, |
|||
Vector512<int> cr, |
|||
in Vector512Parameters parameters, |
|||
out Vector512<int> r, |
|||
out Vector512<int> g, |
|||
out Vector512<int> b) |
|||
{ |
|||
cb -= parameters.ChromaMidpoint; |
|||
cr -= parameters.ChromaMidpoint; |
|||
r = Vector512.Clamp(y + (((parameters.RedCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum); |
|||
g = Vector512.Clamp(y + (((parameters.GreenCb * cb) + (parameters.GreenCr * cr) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum); |
|||
b = Vector512.Clamp(y + (((parameters.BlueCb * cb) + parameters.RoundingBias) >> CoefficientShift), Vector512<int>.Zero, parameters.Maximum); |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Formats.Heif.Color; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Metadata.Profiles.Cicp; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc.Color; |
|||
|
|||
/// <summary>
|
|||
/// Converts full-range eight-bit HEVC 4:2:0 planes with an unspecified matrix to packed RGB pixels.
|
|||
/// </summary>
|
|||
internal static partial class HevcYuv420ToRgb8Converter |
|||
{ |
|||
/// <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 picture and color description.
|
|||
/// </summary>
|
|||
/// <param name="picture">The reconstructed HEVC picture.</param>
|
|||
/// <param name="colorProfile">The effective H.273 color description.</param>
|
|||
/// <param name="mode">The resolved H.273 conversion operation.</param>
|
|||
/// <returns><see langword="true"/> when the picture can use this converter; otherwise, <see langword="false"/>.</returns>
|
|||
public static bool IsSupported(HevcPictureBuffer picture, CicpProfile colorProfile, HeifColorConversionMode mode) |
|||
=> picture.ChromaFormat == 1 |
|||
&& !picture.SeparateColorPlane |
|||
&& picture.BitDepthLuma == 8 |
|||
&& picture.BitDepthChroma == 8 |
|||
&& colorProfile.FullRange |
|||
&& colorProfile.MatrixCoefficients == CicpMatrixCoefficients.Unspecified |
|||
&& mode == HeifColorConversionMode.Coefficients; |
|||
|
|||
/// <summary>
|
|||
/// Converts a supported HEVC picture to packed pixels using integer SIMD with a scalar tail.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The destination pixel type.</typeparam>
|
|||
/// <param name="configuration">The configuration used for allocation and pixel conversion.</param>
|
|||
/// <param name="picture">The reconstructed HEVC picture.</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>( |
|||
Configuration configuration, |
|||
HevcPictureBuffer picture, |
|||
ImageFrame<TPixel> image, |
|||
in HeifColorConversionParameters parameters, |
|||
int sourceX, |
|||
int sourceY) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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); |
|||
|
|||
for (int y = 0; y < image.Height; y++) |
|||
{ |
|||
int lumaY = sourceY + y; |
|||
|
|||
// HEVC expresses 4:2:0 conformance-window offsets in complete chroma sample units, so both source
|
|||
// offsets are even here. The unspecified-matrix presentation replicates each native chroma sample
|
|||
// across its 2x2 luma cell before applying the default BT.601 coefficients.
|
|||
ReadOnlySpan<ushort> luma = picture.GetRowSpan(HevcPlane.Y, lumaY).Slice(sourceX, image.Width); |
|||
ReadOnlySpan<ushort> chromaBlue = picture.GetRowSpan(HevcPlane.Cb, lumaY >> 1).Slice(sourceX >> 1); |
|||
ReadOnlySpan<ushort> chromaRed = picture.GetRowSpan(HevcPlane.Cr, lumaY >> 1).Slice(sourceX >> 1); |
|||
|
|||
ConvertRow(luma, chromaBlue, chromaRed, red, green, blue, in conversionParameters); |
|||
|
|||
Span<TPixel> destination = image.PixelBuffer.DangerousGetRowSpan(y); |
|||
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(red, green, blue, destination); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue