diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs index 05656a6d18..a8f1a86658 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs @@ -140,4 +140,30 @@ internal static class Av1Constants public const int MaxTransformCategories = 4; public const int CoefficientContextCount = 6; + + public const int BaseLevelsCount = 2; + + public const int CoefficientBaseRange = 12; + + public const int TransformPadHorizontalLog2 = 2; + + public const int TransformPadHorizontal = 1 << TransformPadHorizontalLog2; + + public const int TransformPadVertical = 6; + + public const int TransformPadEnd = 16; + + public const int CoefficientContextBits = 6; + + public const int CoefficientContextMask = (1 << CoefficientContextBits) - 1; + + public const int TransformPad2d = ((MaxTransformSize + TransformPadHorizontal) * (MaxTransformSize + TransformPadVertical)) + TransformPadEnd; + + public const int MaxTransformSize = 1 << 6; + + public const int TransformPadTop = 2; + + public const int BaseRangeSizeMinus1 = 3; + + public const int MaxBaseRange = 15; } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs index a107562637..7509da20a8 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs @@ -33,7 +33,7 @@ internal class ObuFrameHeader public ObuFilmGrainParameters FilmGrainParameters { get; set; } = new ObuFilmGrainParameters(); - public bool ReducedTransformSet { get; set; } + public bool UseReducedTransformSet { get; set; } public ObuLoopFilterParameters LoopFilterParameters { get; set; } = new ObuLoopFilterParameters(); diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs index db0131604a..95736d9dc4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs @@ -1159,7 +1159,7 @@ internal class ObuReader frameInfo.AllowWarpedMotion = reader.ReadBoolean(); } - frameInfo.ReducedTransformSet = reader.ReadBoolean(); + frameInfo.UseReducedTransformSet = reader.ReadBoolean(); ReadGlobalMotionParameters(ref reader, sequenceHeader, frameInfo, isIntraFrame); frameInfo.FilmGrainParameters = ReadFilmGrainFilterParameters(ref reader, sequenceHeader, frameInfo); } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs index 226116c797..aa4b22a03b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs @@ -415,7 +415,7 @@ internal class ObuWriter // Not applicable for INTRA frames. // WriteFrameReferenceMode(ref writer, frameInfo.ReferenceMode, isIntraFrame); // WriteSkipModeParameters(ref writer, sequenceHeader, frameInfo, isIntraFrame, frameInfo.ReferenceMode); - writer.WriteBoolean(frameInfo.ReducedTransformSet); + writer.WriteBoolean(frameInfo.UseReducedTransformSet); // Not applicable for INTRA frames. // WriteGlobalMotionParameters(ref writer, sequenceHeader, frameInfo, isIntraFrame); diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs new file mode 100644 index 0000000000..d3b1df0357 --- /dev/null +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; + +internal static class Av1PreditionModeExtensions +{ + private static readonly Av1TransformType[] IntraPreditionMode2TransformType = [ + Av1TransformType.DctDct, // DC + Av1TransformType.AdstDct, // V + Av1TransformType.DctAdst, // H + Av1TransformType.DctDct, // D45 + Av1TransformType.AdstAdst, // D135 + Av1TransformType.AdstDct, // D117 + Av1TransformType.DctAdst, // D153 + Av1TransformType.DctAdst, // D207 + Av1TransformType.AdstDct, // D63 + Av1TransformType.AdstAdst, // SMOOTH + Av1TransformType.AdstDct, // SMOOTH_V + Av1TransformType.DctAdst, // SMOOTH_H + Av1TransformType.AdstAdst, // PAETH + ]; + + public static Av1TransformType ToTransformType(this Av1PredictionMode mode) => IntraPreditionMode2TransformType[(int)mode]; +} diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs index fbd0d99856..476a85f883 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; + namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol; internal static class Av1DefaultDistributions @@ -179,4 +181,251 @@ internal static class Av1DefaultDistributions [new(12986, 15180), new(12986, 15180), new(24302, 25602)], [new(5782, 11475), new(5782, 11475), new(16803, 22759)], ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti16 => + [ + [ + [new(840, 1039, 1980, 4895), new(370, 671, 1883, 4471)], + [new(3247, 4950, 9688, 14563), new(1904, 3354, 7763, 14647)] + ], + [ + [new(2125, 2551, 5165, 8946), new(513, 765, 1859, 6339)], + [new(7637, 9498, 14259, 19108), new(2497, 4096, 8866, 16993)] + ], + [ + [new(4016, 4897, 8881, 14968), new(716, 1105, 2646, 10056)], + [new(11139, 13270, 18241, 23566), new(3192, 5032, 10297, 19755)] + ], + [ + [new(6708, 8958, 14746, 22133), new(1222, 2074, 4783, 15410)], + [new(19575, 21766, 26044, 29709), new(7297, 10767, 19273, 28194)] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti32 => + [ + [ + [new(400, 520, 977, 2102, 6542), new(210, 405, 1315, 3326, 7537)], + [new(2636, 4273, 7588, 11794, 20401), new(1786, 3179, 6902, 11357, 19054)] + ], + [ + [new(989, 1249, 2019, 4151, 10785), new(313, 441, 1099, 2917, 8562)], + [new(8394, 10352, 13932, 18855, 26014), new(2578, 4124, 8181, 13670, 24234)] + ], + [ + [new(2515, 3003, 4452, 8162, 16041), new(574, 821, 1836, 5089, 13128)], + [new(13468, 16303, 20361, 25105, 29281), new(3542, 5502, 10415, 16760, 25644)] + ], + [ + [new(4617, 5709, 8446, 13584, 23135), new(1156, 1702, 3675, 9274, 20539)], + [new(22086, 24282, 27010, 29770, 31743), new(7699, 10897, 20891, 26926, 31628)] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti64 => + [ + [ + [new(329, 498, 1101, 1784, 3265, 7758), new(335, 730, 1459, 5494, 8755, 12997)], + [new(3505, 5304, 10086, 13814, 17684, 23370), new(1563, 2700, 4876, 10911, 14706, 22480)], + ], + [ + [new(1260, 1446, 2253, 3712, 6652, 13369), new(401, 605, 1029, 2563, 5845, 12626)], + [new(8609, 10612, 14624, 18714, 22614, 29024), new(1923, 3127, 5867, 9703, 14277, 27100)] + ], + [ + [new(2374, 2772, 4583, 7276, 12288, 19706), new(497, 810, 1315, 3000, 7004, 15641)], + [new(15050, 17126, 21410, 24886, 28156, 30726), new(4034, 6290, 10235, 14982, 21214, 28491)] + ], + [ + [new(6307, 7541, 12060, 16358, 22553, 27865), new(1289, 2320, 3971, 7926, 14153, 24291)], + [new(24212, 25708, 28268, 30035, 31307, 32049), new(8726, 12378, 19409, 26450, 30038, 32462)] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti128 => + [ + [ + [new(219, 482, 1140, 2091, 3680, 6028, 12586), new(371, 699, 1254, 4830, 9479, 12562, 17497)], + [new(5245, 7456, 12880, 15852, 20033, 23932, 27608), new(2054, 3472, 5869, 14232, 18242, 20590, 26752)] + ], + [ + [new(685, 933, 1488, 2714, 4766, 8562, 19254), new(217, 352, 618, 2303, 5261, 9969, 17472)], + [new(8045, 11200, 15497, 19595, 23948, 27408, 30938), new(2310, 4160, 7471, 14997, 17931, 20768, 30240)] + ], + [ + [new(1366, 1738, 2527, 5016, 9355, 15797, 24643), new(354, 558, 944, 2760, 7287, 14037, 21779)], + [new(13627, 16246, 20173, 24429, 27948, 30415, 31863), new(6275, 9889, 14769, 23164, 27988, 30493, 32272)] + ], + [ + [new(3472, 4885, 7489, 12481, 18517, 24536, 29635), new(886, 1731, 3271, 8469, 15569, 22126, 28383)], + [new(24313, 26062, 28385, 30107, 31217, 31898, 32345), new(9165, 13282, 21150, 30286, 31894, 32571, 32712)] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti256 => + [ + [ + [ + new(310, 584, 1887, 3589, 6168, 8611, 11352, 15652), + new(998, 1850, 2998, 5604, 17341, 19888, 22899, 25583), + ], + [ + new(2520, 3240, 5952, 8870, 12577, 17558, 19954, 24168), + new(2203, 4130, 7435, 10739, 20652, 23681, 25609, 27261) + ], + ], + [ + [ + new(1448, 2109, 4151, 6263, 9329, 13260, 17944, 23300), + new(399, 1019, 1749, 3038, 10444, 15546, 22739, 27294) + ], + [ + new(6402, 8148, 12623, 15072, 18728, 22847, 26447, 29377), + new(1674, 3252, 5734, 10159, 22397, 23802, 24821, 30940) + ] + ], + [ + [ + new(3089, 3920, 6038, 9460, 14266, 19881, 25766, 29176), + new(1084, 2358, 3488, 5122, 11483, 18103, 26023, 29799) + ], + [ + new(11514, 13794, 17480, 20754, 24361, 27378, 29492, 31277), + new(6571, 9610, 15516, 21826, 29092, 30829, 31842, 32708) + ] + ], + [ + [ + new(5348, 7113, 11820, 15924, 22106, 26777, 30334, 31757), + new(2453, 4474, 6307, 8777, 16474, 22975, 29000, 31547) + ], + [ + new(23110, 24597, 27140, 28894, 30167, 30927, 31392, 32094), + new(9998, 17661, 25178, 28097, 31308, 32038, 32403, 32695) + ] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti512 => + [ + [ + [ + new(641, 983, 3707, 5430, 10234, 14958, 18788, 23412, 26061), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ], + [ + new(5095, 6446, 9996, 13354, 16017, 17986, 20919, 26129, 29140), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ] + ], + [ + [ + new(1230, 2278, 5035, 7776, 11871, 15346, 19590, 24584, 28749), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ], + [ + new(7265, 9979, 15819, 19250, 21780, 23846, 26478, 28396, 31811), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ] + ], + [ + [ + new(2624, 3936, 6480, 9686, 13979, 17726, 23267, 28410, 31078), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ], + [ + new(12015, 14769, 19588, 22052, 24222, 25812, 27300, 29219, 32114), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ] + ], + [ + [ + new(5927, 7809, 10923, 14597, 19439, 24135, 28456, 31142, 32060), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ], + [ + new(21093, 23043, 25742, 27658, 29097, 29716, 30073, 30820, 31956), + new(3277, 6554, 9830, 13107, 16384, 19661, 22938, 26214, 29491) + ] + ] + ]; + + private static Av1Distribution[][][] EndOfBlockFlagMulti1024 => + [ + [ + [ + new(393, 421, 751, 1623, 3160, 6352, 13345, 18047, 22571, 25830), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ], + [ + new(1865, 1988, 2930, 4242, 10533, 16538, 21354, 27255, 28546, 31784), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ] + ], + [ + [ + new(696, 948, 3145, 5702, 9706, 13217, 17851, 21856, 25692, 28034), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ], + [ + new(2672, 3591, 9330, 17084, 22725, 24284, 26527, 28027, 28377, 30876), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ] + ], + [ + [ + new(2784, 3831, 7041, 10521, 14847, 18844, 23155, 26682, 29229, 31045), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ], + [ + new(9577, 12466, 17739, 20750, 22061, 23215, 24601, 25483, 25843, 32056), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ] + ], + [ + [ + new(6698, 8334, 11961, 15762, 20186, 23862, 27434, 29326, 31082, 32050), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ], + [ + new(20569, 22426, 25569, 26859, 28053, 28913, 29486, 29724, 29807, 32570), + new(2979, 5958, 8937, 11916, 14895, 17873, 20852, 23831, 26810, 29789) + ] + ] + ]; + + public static Av1Distribution[][][] GetEndOfBlockFlag(int baseQIndex) + { + int qContext = GetQContext(baseQIndex); + return + [ + EndOfBlockFlagMulti16[qContext], + EndOfBlockFlagMulti32[qContext], + EndOfBlockFlagMulti64[qContext], + EndOfBlockFlagMulti128[qContext], + EndOfBlockFlagMulti256[qContext], + EndOfBlockFlagMulti512[qContext], + EndOfBlockFlagMulti1024[qContext], + ]; + } + + private static int GetQContext(int q) + { + if (q <= 20) + { + return 0; + } + + if (q <= 60) + { + return 1; + } + + if (q <= 120) + { + return 2; + } + + return 3; + } } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Distribution.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Distribution.cs index c3d33a4409..241b6ec4e1 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Distribution.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Distribution.cs @@ -54,11 +54,21 @@ internal class Av1Distribution { } + public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7) + : this([p0, p1, p2, p3, p4, p5, p6, p7, 0], 2) + { + } + public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8) : this([p0, p1, p2, p3, p4, p5, p6, p7, p8, 0], 2) { } + public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9) + : this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, 0], 2) + { + } + public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9, uint p10, uint p11) : this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, 0], 2) { diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NzMap.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NzMap.cs new file mode 100644 index 0000000000..4326370ecc --- /dev/null +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NzMap.cs @@ -0,0 +1,356 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol; + +internal static class Av1NzMap +{ + private static readonly int[] ClipMax3 = [ + 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + ]; + + // SIG_COEF_CONTEXTS_2D = 26 + private const int NzMapContext0 = 26; + private const int NzMapContext5 = NzMapContext0 + 5; + private const int NzMapContext10 = NzMapContext0 + 10; + + private static readonly int[] NzMapContextOffset1d = [ + NzMapContext0, NzMapContext5, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, + NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, + NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, + NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, + NzMapContext10, NzMapContext10, NzMapContext10, NzMapContext10, + ]; + + // The ctx offset table when TX is TX_CLASS_2D. + // TX col and row indices are clamped to 4 + private static readonly int[] NzMapContextOffset4x4 = [0, 1, 6, 6, 1, 6, 6, 21, 6, 6, 21, 21, 6, 21, 21, 21]; + + private static readonly int[] NzMapContextOffset8x8 = [ + 0, 1, 6, 6, 21, 21, 21, 21, 1, 6, 6, 21, 21, 21, 21, 21, 6, 6, 21, 21, 21, 21, + 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset16x16 = [ + 0, 1, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 1, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset32x32 = [ + 0, 1, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 1, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset8x4 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 16, 16, 6, 21, 21, 21, 21, 21, + 16, 16, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset16x8 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 6, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset16x32 = [ + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset32x16 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 16, 16, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset32x64 = [ + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset64x32 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 16, 16, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, + 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset4x16 = [ + 0, 11, 11, 11, 11, 11, 11, 11, 6, 6, 21, 21, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset16x4 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 6, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset8x32 = [ + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 6, 6, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[] NzMapContextOffset32x8 = [ + 0, 16, 6, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 16, 16, 6, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 16, 16, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + ]; + + private static readonly int[][] NzMapContextOffset = [ + NzMapContextOffset4x4, // TX_4x4 + NzMapContextOffset8x8, // TX_8x8 + NzMapContextOffset16x16, // TX_16x16 + NzMapContextOffset32x32, // TX_32x32 + NzMapContextOffset32x32, // TX_32x32 + NzMapContextOffset4x16, // TX_4x8 + NzMapContextOffset8x4, // TX_8x4 + NzMapContextOffset8x32, // TX_8x16 + NzMapContextOffset16x8, // TX_16x8 + NzMapContextOffset16x32, // TX_16x32 + NzMapContextOffset32x16, // TX_32x16 + NzMapContextOffset32x64, // TX_32x64 + NzMapContextOffset64x32, // TX_64x32 + NzMapContextOffset4x16, // TX_4x16 + NzMapContextOffset16x4, // TX_16x4 + NzMapContextOffset8x32, // TX_8x32 + NzMapContextOffset32x8, // TX_32x8 + NzMapContextOffset16x32, // TX_16x64 + NzMapContextOffset64x32, // TX_64x16 + ]; + + public static int GetNzMagnitude(Span levels, int bwl, Av1TransformClass transformClass) + { + int mag; + + // Note: AOMMIN(level, 3) is useless for decoder since level < 3. + mag = ClipMax3[levels[1]]; // { 0, 1 } + mag += ClipMax3[levels[(1 << bwl) + Av1Constants.TransformPadHorizontal]]; // { 1, 0 } + + switch (transformClass) + { + case Av1TransformClass.Class2D: + mag += ClipMax3[levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1]]; // { 1, 1 } + mag += ClipMax3[levels[2]]; // { 0, 2 } + mag += ClipMax3[levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)]]; // { 2, 0 } + break; + + case Av1TransformClass.ClassVertical: + mag += ClipMax3[levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)]]; // { 2, 0 } + mag += ClipMax3[levels[(3 << bwl) + (3 << Av1Constants.TransformPadHorizontalLog2)]]; // { 3, 0 } + mag += ClipMax3[levels[(4 << bwl) + (4 << Av1Constants.TransformPadHorizontalLog2)]]; // { 4, 0 } + break; + case Av1TransformClass.ClassHorizontal: + mag += ClipMax3[levels[2]]; // { 0, 2 } + mag += ClipMax3[levels[3]]; // { 0, 3 } + mag += ClipMax3[levels[4]]; // { 0, 4 } + break; + } + + return mag; + } + + public static int GetNzMapContextFromStats(int stats, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass) + { + // tx_class == 0(TX_CLASS_2D) + if (((int)transformClass | pos) == 0) + { + return 0; + } + + int ctx = (stats + 1) >> 1; + ctx = Math.Min(ctx, 4); + switch (transformClass) + { + case Av1TransformClass.Class2D: + // This is the algorithm to generate eb_av1_nz_map_ctx_offset[][] + // const int width = tx_size_wide[tx_size]; + // const int height = tx_size_high[tx_size]; + // if (width < height) { + // if (row < 2) return 11 + ctx; + // } else if (width > height) { + // if (col < 2) return 16 + ctx; + // } + // if (row + col < 2) return ctx + 1; + // if (row + col < 4) return 5 + ctx + 1; + // return 21 + ctx; + return ctx + NzMapContextOffset[(int)transformSize][pos]; + case Av1TransformClass.ClassHorizontal: + int row = pos >> bwl; + int col = pos - (row << bwl); + return ctx + NzMapContextOffset1d[col]; + case Av1TransformClass.ClassVertical: + int row2 = pos >> bwl; + return ctx + NzMapContextOffset1d[row2]; + default: + break; + } + + return 0; + } + + public static int GetNzMapContext(Av1TransformSize transformSize, int pos) => NzMapContextOffset[(int)transformSize][pos]; +} diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs index 7d6eb4998f..b5340ecd26 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Drawing; using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; @@ -23,9 +22,14 @@ internal ref struct Av1SymbolDecoder private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode; private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra; private readonly Av1Distribution[][] transformSize = Av1DefaultDistributions.TransformSize; + private readonly Av1Distribution[][][] endOfBlockFlag; private Av1SymbolReader reader; - public Av1SymbolDecoder(Span tileData) => this.reader = new Av1SymbolReader(tileData); + public Av1SymbolDecoder(Span tileData, int qIndex) + { + this.reader = new Av1SymbolReader(tileData); + this.endOfBlockFlag = Av1DefaultDistributions.GetEndOfBlockFlag(qIndex); + } public int ReadLiteral(int bitCount) { @@ -176,6 +180,26 @@ internal ref struct Av1SymbolDecoder return transformSize; } + public int ReadEndOfBlockFlag(Av1PlaneType planeType, Av1TransformClass transformClass, Av1TransformSize transformSize) + { + int endOfBlockContext = transformClass == Av1TransformClass.Class2D ? 0 : 1; + int endOfBlockMultiSize = transformSize.GetLog2Minus4(); + ref Av1SymbolReader r = ref this.reader; + return r.ReadSymbol(this.endOfBlockFlag[endOfBlockMultiSize][(int)planeType][endOfBlockContext]) + 1; + } + + public bool ReadTransformBlockSkip(Av1TransformSize transformSizeContext, int skipContext) => throw new NotImplementedException(); + + public bool ReadEndOfBlockExtra(Av1TransformSize transformSizeContext, Av1PlaneType planeType, int endOfBlockContext) => throw new NotImplementedException(); + + public int ReadBaseRange(Av1TransformSize transformSizeContext, Av1PlaneType planeType, int baseRangeContext) => throw new NotImplementedException(); + + public int ReadDcSign(Av1PlaneType planeType, int dcSignContext) => throw new NotImplementedException(); + + public int ReadBaseEndOfBlock(Av1TransformSize transformSizeContext, Av1PlaneType planeType, int coefficientContext) => throw new NotImplementedException(); + + public int ReadBase(int coeff_ctx, Av1TransformSize transformSizeContext, Av1PlaneType planeType) => throw new NotImplementedException(); + private static uint GetElementProbability(Av1Distribution probability, Av1PartitionType element) => probability[(int)element - 1] - probability[(int)element]; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs index 648a39d407..c9ea1487d5 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileDecoder.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System; +using System.Formats.Asn1; using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; @@ -21,16 +23,15 @@ internal class Av1TileDecoder : IAv1TileDecoder private static readonly int[][] SkipContexts = [ [1, 2, 2, 2, 3], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 6]]; + private static readonly int[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513]; + private static readonly int[] EndOfBlockOffsetBits = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + private bool[][][] blockDecoded = []; private int[][] referenceSgrXqd = []; private int[][][] referenceLrWiener = []; - private Av1ParseAboveNeighbor4x4Context aboveNeighborContext; - private Av1ParseLeftNeighbor4x4Context leftNeighborContext; + private readonly Av1ParseAboveNeighbor4x4Context aboveNeighborContext; + private readonly Av1ParseLeftNeighbor4x4Context leftNeighborContext; private int currentQuantizerIndex; - private int[][] aboveLevelContext = []; - private int[][] aboveDcContext = []; - private int[][] leftLevelContext = []; - private int[][] leftDcContext = []; private int[][] segmentIds = []; private int deltaLoopFilterResolution = -1; private bool readDeltas; @@ -76,7 +77,7 @@ internal class Av1TileDecoder : IAv1TileDecoder public void DecodeTile(Span tileData, int tileNum) { - Av1SymbolDecoder reader = new(tileData); + Av1SymbolDecoder reader = new(tileData, this.FrameInfo.QuantizationParameters.BaseQIndex); int tileColumnIndex = tileNum % this.FrameInfo.TilesInfo.TileColumnCount; int tileRowIndex = tileNum / this.FrameInfo.TilesInfo.TileColumnCount; @@ -521,6 +522,9 @@ internal class Av1TileDecoder : IAv1TileDecoder /// /// 5.11.35. Transform block syntax. /// + /// + /// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification. + /// private int TransformBlock( ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, @@ -561,7 +565,486 @@ internal class Av1TileDecoder : IAv1TileDecoder /// /// 5.11.39. Coefficients syntax. /// - private int ParseCoefficients(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, int startY, int startX, int blockRow, int blockColumn, int plane, Av1TransformBlockContext transformBlockContext, Av1TransformSize transformSize, int coefficientIndex, Av1TransformInfo transformInfo) => throw new NotImplementedException(); + /// + /// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification. + /// + private int ParseCoefficients(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, int blockRow, int blockColumn, int aboveOffset, int leftOffset, int plane, Av1TransformBlockContext transformBlockContext, Av1TransformSize transformSize, int coefficientIndex, Av1TransformInfo transformInfo) + { + Span coefficientBuffer = this.FrameBuffer.GetCoefficients(plane); + int width = transformSize.GetWidth(); + int height = transformSize.GetHeight(); + Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + ((int)transformSize.GetSquareUpSize() + 1)) >> 1); + Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1); + int culLevel = 0; + int dcValue = 0; + + int[] levelsBuffer = new int[Av1Constants.TransformPad2d]; + Span levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..]; + + bool allZero = reader.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext); + int bwl = transformSize.GetBlockWidthLog2(); + int endOfBlock; + int maxScanLine = 0; + if (allZero) + { + if (plane == 0) + { + transformInfo.Type = Av1TransformType.DctDct; + transformInfo.CodeBlockFlag = false; + } + + this.UpdateCoefficientContext(plane, partitionInfo, transformSize, blockRow, blockColumn, aboveOffset, leftOffset, culLevel); + return 0; + } + + int endOfBlockExtra = 0; + int endOfBlockPoint = 0; + + transformInfo.Type = this.ComputeTransformType(planeType, partitionInfo, transformSize, transformInfo); + Av1TransformClass transformClass = transformInfo.Type.ToClass(); + Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(transformSize, transformInfo.Type); + Span scan = scanOrder.Scan; + + endOfBlockPoint = reader.ReadEndOfBlockFlag(planeType, transformClass, transformSize); + int endOfBlockShift = EndOfBlockOffsetBits[endOfBlockPoint]; + if (endOfBlockShift > 0) + { + int endOfBlockContext = endOfBlockPoint; + bool bit = reader.ReadEndOfBlockExtra(transformSizeContext, planeType, endOfBlockContext); + if (bit) + { + endOfBlockExtra += 1 << (endOfBlockShift - 1); + } + else + { + for (int j = 1; j < endOfBlockShift; j++) + { + if (reader.ReadLiteral(1) != 0) + { + endOfBlockExtra += 1 << (endOfBlockShift - 1 - j); + } + } + } + } + + endOfBlock = RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra); + if (endOfBlock > 1) + { + Array.Fill(levelsBuffer, 0, 0, ((width + Av1Constants.TransformPadHorizontal) * (height + Av1Constants.TransformPadVertical)) + Av1Constants.TransformPadEnd); + } + + int i = endOfBlock - 1; + int pos = scan[i]; + int coefficientContext = GetLowerLevelContextEndOfBlock(bwl, height, i); + int level = reader.ReadBaseEndOfBlock(transformSizeContext, planeType, coefficientContext); + if (level > Av1Constants.BaseLevelsCount) + { + int baseRangeContext = GetBaseRangeContextEndOfBlock(pos, bwl, transformClass); + for (int idx = 0; idx < Av1Constants.CoefficientBaseRange / Av1Constants.BaseRangeSizeMinus1; idx++) + { + int coefficinetBaseRange = reader.ReadBaseRange(transformSizeContext, planeType, baseRangeContext); + level += coefficinetBaseRange; + if (coefficinetBaseRange < Av1Constants.BaseRangeSizeMinus1) + { + break; + } + } + } + + levels[GetPaddedIndex(pos, bwl)] = level; + + if (endOfBlock > 1) + { + if (transformClass == Av1TransformClass.Class2D) + { + ReadCoefficientsReverse2d(ref reader, transformSize, 1, endOfBlock - 1 - 1, scan, bwl, levels, transformSizeContext, planeType); + ReadCoefficientsReverse(ref reader, transformSize, transformInfo.Type, 0, 0, scan, bwl, levels, transformSizeContext, planeType); + } + else + { + ReadCoefficientsReverse(ref reader, transformSize, transformInfo.Type, 0, endOfBlock - 1 - 1, scan, bwl, levels, transformSizeContext, planeType); + } + } + + coefficientBuffer[this.coefficientIndex[plane]] = endOfBlock; + for (int c = 0; c < endOfBlock; c++) + { + int sign = 0; + level = levels[GetPaddedIndex(scan[c], bwl)]; + if (level != 0) + { + maxScanLine = Math.Max(maxScanLine, scan[c]); + if (c == 0) + { + sign = reader.ReadDcSign(planeType, transformBlockContext.DcSignContext); + } + else + { + sign = reader.ReadLiteral(1); + } + + if (level >= Av1Constants.CoefficientBaseRange + Av1Constants.BaseLevelsCount + 1) + { + level += ReadGolomb(ref reader); + } + + if (c == 0) + { + dcValue = sign != 0 ? -level : level; + } + + level &= 0xfffff; + culLevel += level; + } + + coefficientBuffer[c + 1] = sign != 0 ? -level : level; + } + + culLevel = Math.Min(Av1Constants.CoefficientContextMask, culLevel); + SetDcSign(ref culLevel, dcValue); + + this.UpdateCoefficientContext(plane, partitionInfo, transformSize, blockRow, blockColumn, aboveOffset, leftOffset, culLevel); + + transformInfo.CodeBlockFlag = true; + return endOfBlock; + } + + private static void ReadCoefficientsReverse2d(ref Av1SymbolDecoder reader, Av1TransformSize transformSize, int startSi, int endSi, Span scan, int bwl, Span levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType) + { + for (int c = endSi; c >= startSi; --c) + { + int pos = scan[c]; + int coeff_ctx = GetLowerLevelsContext2d(levels, pos, bwl, transformSize); + int level = reader.ReadBase(pos, transformSizeContext, planeType); + if (level > Av1Constants.BaseLevelsCount) + { + int baseRangeContext = GetBaseRangeContext2d(levels, pos, bwl); + for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1) + { + int k = reader.ReadBaseRange(transformSizeContext, planeType, baseRangeContext); + level += k; + if (k < Av1Constants.BaseRangeSizeMinus1) + { + break; + } + } + } + + levels[GetPaddedIndex(pos, bwl)] = level; + } + } + + private static int GetBaseRangeContext2d(Span levels, int c, int bwl) + { + DebugGuard.MustBeGreaterThan(c, 0, nameof(c)); + int row = c >> bwl; + int col = c - (row << bwl); + int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal; + int pos = (row * stride) + col; + int mag = + Math.Min(levels[pos + 1], Av1Constants.MaxBaseRange) + + Math.Min(levels[pos + stride], Av1Constants.MaxBaseRange) + + Math.Min(levels[pos + 1 + stride], Av1Constants.MaxBaseRange); + mag = Math.Min((mag + 1) >> 1, 6); + if ((row | col) < 2) + { + return mag + 7; + } + + return mag + 14; + } + + private static int GetLowerLevelsContext2d(Span levels, int pos, int bwl, Av1TransformSize transformSize) + { + DebugGuard.MustBeGreaterThan(pos, 0, nameof(pos)); + int mag; + levels = levels[GetPaddedIndex(pos, bwl)..]; + mag = Math.Min(levels[1], 3); // { 0, 1 } + mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal], 3); // { 1, 0 } + mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1], 3); // { 1, 1 } + mag += Math.Min(levels[2], 3); // { 0, 2 } + mag += Math.Min(levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)], 3); // { 2, 0 } + + int ctx = Math.Min((mag + 1) >> 1, 4); + return ctx + Av1NzMap.GetNzMapContext(transformSize, pos); + } + + private static void ReadCoefficientsReverse(ref Av1SymbolDecoder reader, Av1TransformSize transformSize, Av1TransformType transformType, int startSi, int endSi, Span scan, int bwl, Span levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType) + { + Av1TransformClass transformClass = transformType.ToClass(); + for (int c = endSi; c >= startSi; --c) + { + int pos = scan[c]; + int coeff_ctx = GetLowerLevelsContext(levels, pos, bwl, transformSize, transformClass); + int level = reader.ReadBase(coeff_ctx, transformSizeContext, planeType); + if (level > Av1Constants.BaseLevelsCount) + { + int baseRangeContext = GetBaseRangeContext(levels, pos, bwl, transformClass); + for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1) + { + int k = reader.ReadBaseRange(transformSizeContext, planeType, baseRangeContext); + level += k; + if (k < Av1Constants.BaseRangeSizeMinus1) + { + break; + } + } + } + + levels[GetPaddedIndex(pos, bwl)] = level; + } + } + + private static int GetBaseRangeContext(Span levels, int c, int bwl, Av1TransformClass transformClass) + { + int row = c >> bwl; + int col = c - (row << bwl); + int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal; + int pos = (row * stride) + col; + int mag = levels[pos + 1]; + mag += levels[pos + stride]; + switch (transformClass) + { + case Av1TransformClass.Class2D: + mag += levels[pos + stride + 1]; + mag = Math.Min((mag + 1) >> 1, 6); + if (c == 0) + { + return mag; + } + + if ((row < 2) && (col < 2)) + { + return mag + 7; + } + + break; + case Av1TransformClass.ClassHorizontal: + mag += levels[pos + 2]; + mag = Math.Min((mag + 1) >> 1, 6); + if (c == 0) + { + return mag; + } + + if (col == 0) + { + return mag + 7; + } + + break; + case Av1TransformClass.ClassVertical: + mag += levels[pos + (stride << 1)]; + mag = Math.Min((mag + 1) >> 1, 6); + if (c == 0) + { + return mag; + } + + if (row == 0) + { + return mag + 7; + } + + break; + default: + break; + } + + return mag + 14; + } + + private static int GetLowerLevelsContext(Span levels, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass) + { + int stats = Av1NzMap.GetNzMagnitude(levels[GetPaddedIndex(pos, bwl)..], bwl, transformClass); + return Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, transformClass); + } + + private static int ReadGolomb(ref Av1SymbolDecoder reader) + { + int x = 1; + int length = 0; + int i = 0; + + while (i == 0) + { + i = reader.ReadLiteral(1); + ++length; + if (length > 20) + { + // SVT_LOG("Invalid length in read_golomb"); + break; + } + } + + for (i = 0; i < length - 1; ++i) + { + x <<= 1; + x += reader.ReadLiteral(1); + } + + return x - 1; + } + + private static void SetDcSign(ref int culLevel, int dcValue) + { + if (dcValue < 0) + { + culLevel |= 1 << Av1Constants.CoefficientContextBits; + } + else if (dcValue > 0) + { + culLevel += 2 << Av1Constants.CoefficientContextBits; + } + } + + private static int GetPaddedIndex(int scanIndex, int bwl) + => scanIndex + ((scanIndex >> bwl) << Av1Constants.TransformPadHorizontalLog2); + + private static int GetBaseRangeContextEndOfBlock(int pos, int bwl, Av1TransformClass transformClass) + { + int row = pos >> bwl; + int col = pos - (row << bwl); + if (pos == 0) + { + return 0; + } + + if ((transformClass == Av1TransformClass.Class2D && row < 2 && col < 2) || + (transformClass == Av1TransformClass.ClassHorizontal && col == 0) || + (transformClass == Av1TransformClass.ClassVertical && row == 0)) + { + return 7; + } + + return 14; + } + + private static int GetLowerLevelContextEndOfBlock(int bwl, int height, int scanIndex) + { + if (scanIndex == 0) + { + return 0; + } + + if (scanIndex <= (height << bwl) >> 3) + { + return 1; + } + + if (scanIndex <= (height << bwl) >> 2) + { + return 2; + } + + return 3; + } + + private void UpdateCoefficientContext(int plane, Av1PartitionInfo partitionInfo, Av1TransformSize transformSize, int blockRow, int blockColumn, int aboveOffset, int leftOffset, int culLevel) + { + bool subX = this.SequenceHeader.ColorConfig.SubSamplingX; + bool subY = this.SequenceHeader.ColorConfig.SubSamplingY; + int[] aboveContexts = this.aboveNeighborContext.GetContext(plane); + int[] leftContexts = this.leftNeighborContext.GetContext(plane); + int transformSizeWide = transformSize.Get4x4WideCount(); + int transformSizeHigh = transformSize.Get4x4HighCount(); + + if (partitionInfo.ModeBlockToRightEdge < 0) + { + Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); + int blocksWide = partitionInfo.GetMaxBlockWide(planeBlockSize, subX); + int aboveContextCount = Math.Min(transformSizeWide, blocksWide - aboveOffset); + Array.Fill(aboveContexts, culLevel, 0, aboveContextCount); + Array.Fill(aboveContexts, 0, aboveContextCount, transformSizeWide - aboveContextCount); + } + else + { + Array.Fill(aboveContexts, culLevel, 0, transformSizeWide); + } + + if (partitionInfo.ModeBlockToBottomEdge < 0) + { + Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); + int blocksHigh = partitionInfo.GetMaxBlockHigh(planeBlockSize, subY); + int leftContextCount = Math.Min(transformSizeHigh, blocksHigh - leftOffset); + Array.Fill(leftContexts, culLevel, 0, leftContextCount); + Array.Fill(leftContexts, 0, leftContextCount, transformSizeWide - leftContextCount); + } + else + { + Array.Fill(leftContexts, culLevel, 0, transformSizeHigh); + } + } + + private static int RecordEndOfBlockPosition(int endOfBlockPoint, int endOfBlockExtra) + { + int endOfBlock = EndOfBlockGroupStart[endOfBlockPoint]; + if (endOfBlock > 2) + { + endOfBlock += endOfBlockExtra; + } + + return endOfBlock; + } + + private Av1TransformType ComputeTransformType(Av1PlaneType planeType, Av1PartitionInfo partitionInfo, Av1TransformSize transformSize, Av1TransformInfo transformInfo) + { + Av1TransformType transformType = Av1TransformType.DctDct; + if (this.FrameInfo.LosslessArray[partitionInfo.ModeInfo.SegmentId] || transformSize.GetSquareUpSize() > Av1TransformSize.Size32x32) + { + transformType = Av1TransformType.DctDct; + } + else + { + if (planeType == Av1PlaneType.Y) + { + transformType = transformInfo.Type; + } + else + { + // In intra mode, uv planes don't share the same prediction mode as y + // plane, so the tx_type should not be shared + transformType = ConvertIntraModeToTransformType(partitionInfo.ModeInfo, Av1PlaneType.Uv); + } + } + + Av1TransformSetType transformSetType = GetExtendedTransformSetType(transformSize, this.FrameInfo.UseReducedTransformSet); + if (!transformType.IsExtendedSetUsed(transformSetType)) + { + transformType = Av1TransformType.DctDct; + } + + return transformType; + } + + private static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool useReducedSet) + { + Av1TransformSize squareUpSize = transformSize.GetSquareUpSize(); + + if (squareUpSize >= Av1TransformSize.Size32x32) + { + return Av1TransformSetType.DctOnly; + } + + if (useReducedSet) + { + return Av1TransformSetType.Dtt4Identity; + } + + Av1TransformSize squareSize = transformSize.GetSquareSize(); + return squareSize == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct; + } + + private static Av1TransformType ConvertIntraModeToTransformType(Av1BlockModeInfo modeInfo, Av1PlaneType planeType) + { + Av1PredictionMode mode = (planeType == Av1PlaneType.Y) ? modeInfo.YMode : modeInfo.UvMode; + if (mode == Av1PredictionMode.UvChromaFromLuma) + { + mode = Av1PredictionMode.DC; + } + + return mode.ToTransformType(); + } private Av1TransformBlockContext GetTransformBlockContext(Av1TransformSize transformSize, int plane, Av1BlockSize planeBlockSize, int transformBlockUnitHighCount, int transformBlockUnitWideCount, int startY, int startX) { @@ -583,7 +1066,7 @@ internal class Av1TileDecoder : IAv1TileDecoder if (plane == 0) { - if (planeBlockSize == transformSize.GetBlockSize()) + if (planeBlockSize == transformSize.ToBlockSize()) { transformBlockContext.SkipContext = 0; } @@ -617,7 +1100,7 @@ internal class Av1TileDecoder : IAv1TileDecoder else { int contextBase = GetEntropyContext(transformSize, aboveContext, leftContext); - int contextOffset = planeBlockSize.GetPelsLog2Count() > transformSize.GetBlockSize().GetPelsLog2Count() ? 10 : 7; + int contextOffset = planeBlockSize.GetPelsLog2Count() > transformSize.ToBlockSize().GetPelsLog2Count() ? 10 : 7; transformBlockContext.SkipContext = contextBase + contextOffset; } @@ -771,30 +1254,9 @@ internal class Av1TileDecoder : IAv1TileDecoder /// /// Page 65, below 5.11.5. Decode block syntax. /// - private void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize) + private static void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize) { - int block4x4Width = blockSize.Get4x4WideCount(); - int block4x4Height = blockSize.Get4x4HighCount(); - bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; - bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY; - int endPlane = this.HasChroma(rowIndex, columnIndex, blockSize) ? 3 : 1; - this.aboveLevelContext = new int[3][]; - this.aboveDcContext = new int[3][]; - this.leftLevelContext = new int[3][]; - this.leftDcContext = new int[3][]; - for (int plane = 0; plane < endPlane; plane++) - { - int subX = plane > 0 && subsamplingX ? 1 : 0; - int subY = plane > 0 && subsamplingY ? 1 : 0; - this.aboveLevelContext[plane] = new int[(columnIndex + block4x4Width) >> subX]; - this.aboveDcContext[plane] = new int[(columnIndex + block4x4Width) >> subX]; - this.leftLevelContext[plane] = new int[(rowIndex + block4x4Height) >> subY]; - this.leftDcContext[plane] = new int[(rowIndex + block4x4Height) >> subY]; - Array.Fill(this.aboveLevelContext[plane], 0); - Array.Fill(this.aboveDcContext[plane], 0); - Array.Fill(this.leftLevelContext[plane], 0); - Array.Fill(this.leftDcContext[plane], 0); - } + // TODO: Do we still need this method. } /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseQuantizer.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseQuantizer.cs index b52473dbd9..312891c85e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseQuantizer.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseQuantizer.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Quantization; internal class Av1InverseQuantizer { - public static int InverseQuantize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, ObuPartitionInfo part, Av1BlockModeInfo mode, int[] level, int[] qCoefficients, Av1TransformMode txType, Av1TransformSize txSize, Av1Plane plane) + public static int InverseQuantize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, ObuPartitionInfo part, Av1BlockModeInfo mode, int[] level, int[] qCoefficients, Av1TransformType txType, Av1TransformSize txSize, Av1Plane plane) { Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(txSize, txType); short[] scanIndices = scanOrder.Scan; diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs index a773b5479b..94c0219a45 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs index a014ad2245..d8ff42bdb6 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization; - namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; internal static class Av1ScanOrderConstants @@ -70,6 +68,6 @@ internal static class Av1ScanOrderConstants 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]; - public static Av1ScanOrder GetScanOrder(Av1TransformSize txSize, Av1TransformMode txMode) - => ScanOrders[(int)txSize][(int)txMode]; + public static Av1ScanOrder GetScanOrder(Av1TransformSize transformSize, Av1TransformType transformType) + => ScanOrders[(int)transformSize][(int)transformType]; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformClass.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformClass.cs new file mode 100644 index 0000000000..afc0354821 --- /dev/null +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformClass.cs @@ -0,0 +1,11 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +internal enum Av1TransformClass +{ + Class2D = 0, + ClassHorizontal = 1, + ClassVertical = 2, +} diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs new file mode 100644 index 0000000000..f25d1cbf5e --- /dev/null +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +internal enum Av1TransformSetType +{ + /// + /// DCT only. + /// + DctOnly, + + /// + /// DCT + Identity only + /// + DctIdentity, + + /// + /// Discrete Trig transforms w/o flip (4) + Identity (1) + /// + Dtt4Identity, + + /// + /// Discrete Trig transforms w/o flip (4) + Identity (1) + 1D Hor/vert DCT (2) + /// + Dtt4Identity1dDct, + + /// + /// Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver DCT (2) + /// + Dtt9Identity1dDct, + + /// + /// Discrete Trig transforms w/ flip (9) + Identity (1) + 1D Hor/Ver (6) + /// + All16 +} diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs index a961be945c..53c27e8741 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs @@ -59,6 +59,82 @@ internal static class Av1TransformSizeExtensions Av1BlockSize.Block64x16, // TX_64X16 ]; + private static readonly Av1TransformSize[] SquareMap = [ + Av1TransformSize.Size4x4, // TX_4X4 + Av1TransformSize.Size8x8, // TX_8X8 + Av1TransformSize.Size16x16, // TX_16X16 + Av1TransformSize.Size32x32, // TX_32X32 + Av1TransformSize.Size64x64, // TX_64X64 + Av1TransformSize.Size4x4, // TX_4X8 + Av1TransformSize.Size4x4, // TX_8X4 + Av1TransformSize.Size8x8, // TX_8X16 + Av1TransformSize.Size8x8, // TX_16X8 + Av1TransformSize.Size16x16, // TX_16X32 + Av1TransformSize.Size16x16, // TX_32X16 + Av1TransformSize.Size32x32, // TX_32X64 + Av1TransformSize.Size32x32, // TX_64X32 + Av1TransformSize.Size4x4, // TX_4X16 + Av1TransformSize.Size4x4, // TX_16X4 + Av1TransformSize.Size8x8, // TX_8X32 + Av1TransformSize.Size8x8, // TX_32X8 + Av1TransformSize.Size16x16, // TX_16X64 + Av1TransformSize.Size16x16, // TX_64X16 + ]; + + private static readonly Av1TransformSize[] SquareUpMap = [ + Av1TransformSize.Size4x4, // TX_4X4 + Av1TransformSize.Size8x8, // TX_8X8 + Av1TransformSize.Size16x16, // TX_16X16 + Av1TransformSize.Size32x32, // TX_32X32 + Av1TransformSize.Size64x64, // TX_64X64 + Av1TransformSize.Size8x8, // TX_4X8 + Av1TransformSize.Size8x8, // TX_8X4 + Av1TransformSize.Size16x16, // TX_8X16 + Av1TransformSize.Size16x16, // TX_16X8 + Av1TransformSize.Size32x32, // TX_16X32 + Av1TransformSize.Size32x32, // TX_32X16 + Av1TransformSize.Size64x64, // TX_32X64 + Av1TransformSize.Size64x64, // TX_64X32 + Av1TransformSize.Size16x16, // TX_4X16 + Av1TransformSize.Size16x16, // TX_16X4 + Av1TransformSize.Size32x32, // TX_8X32 + Av1TransformSize.Size32x32, // TX_32X8 + Av1TransformSize.Size64x64, // TX_16X64 + Av1TransformSize.Size64x64, // TX_64X16 + ]; + + private static readonly int[] Log2Minus4 = [ + 0, // TX_4X4 + 2, // TX_8X8 + 4, // TX_16X16 + 6, // TX_32X32 + 6, // TX_64X64 + 1, // TX_4X8 + 1, // TX_8X4 + 3, // TX_8X16 + 3, // TX_16X8 + 5, // TX_16X32 + 5, // TX_32X16 + 6, // TX_32X64 + 6, // TX_64X32 + 2, // TX_4X16 + 2, // TX_16X4 + 4, // TX_8X32 + 4, // TX_32X8 + 5, // TX_16X64 + 5, // TX_64X16 + ]; + + // Transform block width in log2 + private static readonly int[] BlockWidthLog2 = [ + 2, 3, 4, 5, 6, 2, 3, 3, 4, 4, 5, 5, 6, 2, 4, 3, 5, 4, 6, + ]; + + // Transform block height in log2 + private static readonly int[] BlockHeightLog2 = [ + 2, 3, 4, 5, 6, 3, 2, 4, 3, 5, 4, 6, 5, 4, 2, 5, 3, 6, 4, + ]; + public static int GetScale(this Av1TransformSize size) { int pels = Size2d[(int)size]; @@ -79,5 +155,23 @@ internal static class Av1TransformSizeExtensions public static Av1TransformSize GetSubSize(this Av1TransformSize size) => SubTransformSize[(int)size]; - public static Av1BlockSize GetBlockSize(this Av1TransformSize transformSize) => (Av1BlockSize)BlockSize[(int)transformSize]; + public static Av1TransformSize GetSquareSize(this Av1TransformSize size) => SquareMap[(int)size]; + + public static Av1TransformSize GetSquareUpSize(this Av1TransformSize size) => SquareUpMap[(int)size]; + + public static Av1BlockSize ToBlockSize(this Av1TransformSize transformSize) => BlockSize[(int)transformSize]; + + public static int GetLog2Minus4(this Av1TransformSize size) => Log2Minus4[(int)size]; + + public static Av1TransformSize GetAdjusted(this Av1TransformSize size) => size switch + { + Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64 => Av1TransformSize.Size32x32, + Av1TransformSize.Size64x16 => Av1TransformSize.Size32x16, + Av1TransformSize.Size16x64 => Av1TransformSize.Size16x32, + _ => size + }; + + public static int GetBlockWidthLog2(this Av1TransformSize size) => BlockWidthLog2[(int)GetAdjusted(size)]; + + public static int GetBlockHeightLog2(this Av1TransformSize size) => BlockHeightLog2[(int)GetAdjusted(size)]; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs new file mode 100644 index 0000000000..48c14ba2aa --- /dev/null +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System; + +namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +internal static class Av1TransformTypeExtensions +{ + private static readonly Av1TransformClass[] Type2Class = [ + Av1TransformClass.Class2D, // DCT_DCT + Av1TransformClass.Class2D, // ADST_DCT + Av1TransformClass.Class2D, // DCT_ADST + Av1TransformClass.Class2D, // ADST_ADST + Av1TransformClass.Class2D, // FLIPADST_DCT + Av1TransformClass.Class2D, // DCT_FLIPADST + Av1TransformClass.Class2D, // FLIPADST_FLIPADST + Av1TransformClass.Class2D, // ADST_FLIPADST + Av1TransformClass.Class2D, // FLIPADST_ADST + Av1TransformClass.Class2D, // IDTX + Av1TransformClass.ClassVertical, // V_DCT + Av1TransformClass.ClassHorizontal, // H_DCT + Av1TransformClass.ClassVertical, // V_ADST + Av1TransformClass.ClassHorizontal, // H_ADST + Av1TransformClass.ClassVertical, // V_FLIPADST + Av1TransformClass.ClassHorizontal, // H_FLIPADST + ]; + + private static readonly bool[][] ExtendedTransformUsed = [ + [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], + [true, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false], + [true, true, true, true, false, false, false, false, false, true, false, false, false, false, false, false], + [true, true, true, true, false, false, false, false, false, true, true, true, false, false, false, false], + [true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false], + [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true], + ]; + + public static Av1TransformClass ToClass(this Av1TransformType transformType) => Type2Class[(int)transformType]; + + public static bool IsExtendedSetUsed(this Av1TransformType transformType, Av1TransformSetType setType) + => ExtendedTransformUsed[(int)setType][(int)transformType]; +} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs index 4a1fa85801..8e3147ec7a 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs @@ -206,7 +206,7 @@ public class SymbolTest using IMemoryOwner encoded = encoder.Exit(); - Av1SymbolDecoder decoder = new(encoded.GetSpan()); + Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0); Av1SymbolReader reader = new(encoded.GetSpan()); for (int i = 0; i < values.Length; i++) { @@ -234,7 +234,7 @@ public class SymbolTest using IMemoryOwner encoded = encoder.Exit(); - Av1SymbolDecoder decoder = new(encoded.GetSpan()); + Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0); Av1SymbolReader reader = new(encoded.GetSpan()); for (int i = 0; i < values.Length; i++) {