diff --git a/src/ImageSharp/Formats/Jxl/Ac/JxlAcContext.cs b/src/ImageSharp/Formats/Jxl/Ac/JxlAcContext.cs new file mode 100644 index 0000000000..98f86534b2 --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Ac/JxlAcContext.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +#pragma warning disable SA1405 // Debug.Assert should provide message text + +namespace SixLabors.ImageSharp.Formats.Jxl.Ac; + +/// +/// AC context +/// +internal static class JxlAcContext +{ + public const int DctOrderContextStart = 0; + public const int NonZeroBuckets = 37; + public const int ZeroDensityContextCount = 458; + public const int ZeroDensityContextLimit = 474; + + public static ReadOnlySpan CoefficientFrequencyContext => + [ + 0xBAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, + 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, + ]; + + public static ReadOnlySpan CoefficientNumNonzeroContext => + [ + 0xBAD, 0, 31, 62, 62, 93, 93, 93, 93, 123, 123, 123, 123, + 152, 152, 152, 152, 152, 152, 152, 152, 180, 180, 180, 180, 180, + 180, 180, 180, 180, 180, 180, 180, 206, 206, 206, 206, 206, 206, + 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, + 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, + ]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ZeroDensityContext(int nonZeroesLeft, int k, int coveredBlocks, int log2CoveredBlocks, int prev) + { + Debug.Assert((1 << log2CoveredBlocks) == coveredBlocks); + + nonZeroesLeft = (nonZeroesLeft + coveredBlocks - 1) >> log2CoveredBlocks; + k >>= log2CoveredBlocks; + + Debug.Assert(k > 0); + Debug.Assert(k < 64); + Debug.Assert(nonZeroesLeft > 0); + Debug.Assert(nonZeroesLeft < 64); + + return ((CoefficientNumNonzeroContext[nonZeroesLeft] + CoefficientFrequencyContext[k]) * 2) + prev; + } +} diff --git a/src/ImageSharp/Formats/Jxl/Ac/JxlBlockContextMap.cs b/src/ImageSharp/Formats/Jxl/Ac/JxlBlockContextMap.cs new file mode 100644 index 0000000000..3b7e75605f --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Ac/JxlBlockContextMap.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats.Jxl.Coefficients; + +namespace SixLabors.ImageSharp.Formats.Jxl.Ac; + +internal sealed class JxlBlockContextMap +{ + public JxlBlockContextMap() + { + DefaultContextMap.CopyTo(this.ContextMap); + this.ContextCount = this.ContextMap.Max() + 1; + this.DcContextCount = 1; + } + + private static ReadOnlySpan DefaultContextMap => + [ + 0, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 6, 6, + 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14, + 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14, + ]; + + public List[] DcThresholds { get; } = [[], [], []]; + + public List QfThresholds { get; } = []; + + public byte[] ContextMap { get; } = new byte[DefaultContextMap.Length]; + + public int ContextCount { get; set; } + + public int DcContextCount { get; set; } + + public int AcContextCount => (this.ContextCount * JxlAcContext.NonZeroBuckets) + JxlAcContext.ZeroDensityContextCount; + + public int Context(int dcIndex, uint qf, int ord, int c) + { + int qfIndex = 0; + for (int i = 0; i < this.QfThresholds.Count; i++) + { + uint t = this.QfThresholds[i]; + + if (qf > t) + { + qfIndex++; + } + } + + int idx = c < 2 ? c ^ 1 : 2; + idx = (idx * JxlForwardCoefficientOrder.OrderCount) + ord; + idx = (idx * (this.QfThresholds.Count + 1)) + qfIndex; + idx = (idx * this.DcContextCount) + dcIndex; + return this.ContextMap[idx]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ZeroDensityContextOffset(int blockContext) => + (this.ContextCount * JxlAcContext.NonZeroBuckets) + JxlAcContext.ZeroDensityContextCount + blockContext; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int NonZeroContext(int nonZeroes, int blockContext) + { + if (nonZeroes >= 64) + { + nonZeroes = 64; + } + + int ctx = nonZeroes < 8 ? nonZeroes : (4 + (nonZeroes / 2)); + return (ctx * this.ContextCount) + blockContext; + } +} diff --git a/src/ImageSharp/Formats/Jxl/Coefficients/JxlForwardCoefficientOrder.cs b/src/ImageSharp/Formats/Jxl/Coefficients/JxlForwardCoefficientOrder.cs new file mode 100644 index 0000000000..04cacb176a --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Coefficients/JxlForwardCoefficientOrder.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jxl.Coefficients; + +internal static class JxlForwardCoefficientOrder +{ + public const byte OrderCount = 13; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CoefficientRows(int rows, int columns) => rows < columns ? rows : columns; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CoefficientColumns(int rows, int columns) => rows < columns ? columns : rows; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CoefficientLayout(ref int rows, ref int columns) + { + rows = CoefficientRows(rows, columns); + columns = CoefficientColumns(rows, columns); + } +} diff --git a/src/ImageSharp/Formats/Jxl/JxlFormat.cs b/src/ImageSharp/Formats/Jxl/JxlFormat.cs index d5dfa802b1..00e7b66eb5 100644 --- a/src/ImageSharp/Formats/Jxl/JxlFormat.cs +++ b/src/ImageSharp/Formats/Jxl/JxlFormat.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; - namespace SixLabors.ImageSharp.Formats.Jxl; internal class JxlFormat : IImageFormat