@ -5,6 +5,9 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Quantification ;
/// <summary>
/// Provides the normative AV1 DC and AC dequantization values for each quantizer index and supported bit depth.
/// </summary>
internal class Av1QuantizationLookup
{
// Coefficient scaling and quantization with AV1 TX are tailored to
@ -35,6 +38,10 @@ internal class Av1QuantizationLookup
// expects quantizers to be larger for higher-bitdepth input. In
// addition, the minimum allowable quantizer is 4; smaller values will
// underflow to 0 in the actual quantization routines.
/// <summary>
/// The Q3 AC dequantization values for 8-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] AcQlookup8 = [
4 , 8 , 9 , 1 0 , 1 1 , 1 2 , 1 3 , 1 4 , 1 5 , 1 6 , 1 7 , 1 8 , 1 9 , 2 0 , 2 1 , 2 2 , 2 3 , 2 4 , 2 5 ,
2 6 , 2 7 , 2 8 , 2 9 , 3 0 , 3 1 , 3 2 , 3 3 , 3 4 , 3 5 , 3 6 , 3 7 , 3 8 , 3 9 , 4 0 , 4 1 , 4 2 , 4 3 , 4 4 ,
@ -52,6 +59,9 @@ internal class Av1QuantizationLookup
1 5 6 7 , 1 5 9 7 , 1 6 2 8 , 1 6 6 0 , 1 6 9 2 , 1 7 2 5 , 1 7 5 9 , 1 7 9 3 , 1 8 2 8 ,
] ;
/// <summary>
/// The Q3 AC dequantization values for 10-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] AcQlookup10 = [
4 , 9 , 1 1 , 1 3 , 1 6 , 1 8 , 2 1 , 2 4 , 2 7 , 3 0 , 3 3 , 3 7 , 4 0 , 4 4 , 4 8 , 5 1 , 5 5 , 5 9 , 6 3 ,
6 7 , 7 1 , 7 5 , 7 9 , 8 3 , 8 8 , 9 2 , 9 6 , 1 0 0 , 1 0 5 , 1 0 9 , 1 1 4 , 1 1 8 , 1 2 2 , 1 2 7 , 1 3 1 , 1 3 6 , 1 4 0 , 1 4 5 ,
@ -69,6 +79,9 @@ internal class Av1QuantizationLookup
6 2 6 8 , 6 3 8 8 , 6 5 1 2 , 6 6 4 0 , 6 7 6 8 , 6 9 0 0 , 7 0 3 6 , 7 1 7 2 , 7 3 1 2 ,
] ;
/// <summary>
/// The Q3 AC dequantization values for 12-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] AcQlookup12 = [
4 , 1 3 , 1 9 , 2 7 , 3 5 , 4 4 , 5 4 , 6 4 , 7 5 , 8 7 , 9 9 , 1 1 2 , 1 2 6 , 1 3 9 , 1 5 4 , 1 6 8 ,
1 8 3 , 1 9 9 , 2 1 4 , 2 3 0 , 2 4 7 , 2 6 3 , 2 8 0 , 2 9 7 , 3 1 4 , 3 3 1 , 3 4 9 , 3 6 6 , 3 8 4 , 4 0 2 , 4 2 0 , 4 3 8 ,
@ -88,6 +101,9 @@ internal class Av1QuantizationLookup
2 1 9 0 2 , 2 2 3 3 4 , 2 2 7 6 6 , 2 3 2 1 4 , 2 3 6 6 2 , 2 4 1 2 6 , 2 4 5 9 0 , 2 5 0 7 0 , 2 5 5 5 1 , 2 6 0 4 7 , 2 6 5 5 9 , 2 7 0 7 1 , 2 7 5 9 9 , 2 8 1 4 3 , 2 8 6 8 7 , 2 9 2 4 7 ,
] ;
/// <summary>
/// The Q3 DC dequantization values for 8-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] DcQlookup8 = [
4 , 8 , 8 , 9 , 1 0 , 1 1 , 1 2 , 1 2 , 1 3 , 1 4 , 1 5 , 1 6 , 1 7 , 1 8 , 1 9 , 1 9 , 2 0 , 2 1 , 2 2 , 2 3 ,
2 4 , 2 5 , 2 6 , 2 6 , 2 7 , 2 8 , 2 9 , 3 0 , 3 1 , 3 2 , 3 2 , 3 3 , 3 4 , 3 5 , 3 6 , 3 7 , 3 8 , 3 8 , 3 9 , 4 0 ,
@ -104,6 +120,9 @@ internal class Av1QuantizationLookup
7 9 6 , 8 1 9 , 8 4 3 , 8 6 9 , 8 9 6 , 9 2 5 , 9 5 5 , 9 8 8 , 1 0 2 2 , 1 0 5 8 , 1 0 9 8 , 1 1 3 9 , 1 1 8 4 , 1 2 3 2 , 1 2 8 2 , 1 3 3 6 ,
] ;
/// <summary>
/// The Q3 DC dequantization values for 10-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] DcQlookup10 = [
4 , 9 , 1 0 , 1 3 , 1 5 , 1 7 , 2 0 , 2 2 , 2 5 , 2 8 , 3 1 , 3 4 , 3 7 , 4 0 , 4 3 , 4 7 , 5 0 , 5 3 , 5 7 ,
6 0 , 6 4 , 6 8 , 7 1 , 7 5 , 7 8 , 8 2 , 8 6 , 9 0 , 9 3 , 9 7 , 1 0 1 , 1 0 5 , 1 0 9 , 1 1 3 , 1 1 6 , 1 2 0 , 1 2 4 , 1 2 8 ,
@ -121,6 +140,9 @@ internal class Av1QuantizationLookup
3 9 5 3 , 4 0 8 9 , 4 2 3 6 , 4 3 9 4 , 4 5 5 9 , 4 7 3 7 , 4 9 2 9 , 5 1 3 0 , 5 3 4 7 ,
] ;
/// <summary>
/// The Q3 DC dequantization values for 12-bit samples, indexed by quantizer index.
/// </summary>
private static readonly short [ ] DcQlookup12 = [
4 , 1 2 , 1 8 , 2 5 , 3 3 , 4 1 , 5 0 , 6 0 , 7 0 , 8 0 , 9 1 , 1 0 3 , 1 1 5 , 1 2 7 , 1 4 0 , 1 5 3 ,
1 6 6 , 1 8 0 , 1 9 4 , 2 0 8 , 2 2 2 , 2 3 7 , 2 5 1 , 2 6 6 , 2 8 1 , 2 9 6 , 3 1 2 , 3 2 7 , 3 4 3 , 3 5 8 , 3 7 4 , 3 9 0 ,
@ -140,8 +162,16 @@ internal class Av1QuantizationLookup
1 2 7 5 0 , 1 3 1 1 8 , 1 3 5 0 1 , 1 3 9 1 3 , 1 4 3 4 3 , 1 4 8 0 7 , 1 5 2 9 0 , 1 5 8 1 2 , 1 6 3 5 6 , 1 6 9 4 3 , 1 7 5 7 5 , 1 8 2 3 7 , 1 8 9 4 9 , 1 9 7 1 8 , 2 0 5 2 1 , 2 1 3 8 7 ,
] ;
/// <summary>
/// Gets the DC dequantization value after applying a plane delta to the frame quantizer index.
/// </summary>
/// <param name="qIndex">The frame or segment quantizer index.</param>
/// <param name="dcDeltaQ">The signed DC quantizer adjustment for the selected plane.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The Q3 DC dequantization value.</returns>
public static short GetDcQuant ( int qIndex , int dcDeltaQ , Av1BitDepth bitDepth )
{
// Plane deltas may move beyond the signaled 8-bit quantizer domain, where AV1 requires endpoint clamping.
int qClamped = Av1Math . Clamp ( qIndex + dcDeltaQ , 0 , Av1Constants . MaxQ ) ;
switch ( bitDepth )
{
@ -157,8 +187,16 @@ internal class Av1QuantizationLookup
}
}
/// <summary>
/// Gets the AC dequantization value after applying a plane delta to the frame quantizer index.
/// </summary>
/// <param name="qIndex">The frame or segment quantizer index.</param>
/// <param name="dcDeltaQ">The signed AC quantizer adjustment for the selected plane.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The Q3 AC dequantization value.</returns>
public static short GetAcQuant ( int qIndex , int dcDeltaQ , Av1BitDepth bitDepth )
{
// Plane deltas may move beyond the signaled 8-bit quantizer domain, where AV1 requires endpoint clamping.
int qClamped = Av1Math . Clamp ( qIndex + dcDeltaQ , 0 , Av1Constants . MaxQ ) ;
switch ( bitDepth )
{
@ -174,6 +212,13 @@ internal class Av1QuantizationLookup
}
}
/// <summary>
/// Gets the quantizer index for a segment, including its alternative-quantizer feature when active.
/// </summary>
/// <param name="segmentationParameters">The frame segmentation configuration.</param>
/// <param name="segmentId">The zero-based AV1 segment identifier.</param>
/// <param name="baseQIndex">The frame's base quantizer index.</param>
/// <returns>The segment quantizer index clamped to the AV1 quantizer domain.</returns>
public static int GetQIndex ( ObuSegmentationParameters segmentationParameters , int segmentId , int baseQIndex )
{
if ( segmentationParameters . IsFeatureActive ( segmentId , ObuSegmentationLevelFeature . AlternativeQuantizer ) )