Browse Source

Implement AC strategy

Implementation of ac_strategy.h and ac_strategy.c
pull/3153/head
winscripter 2 days ago
parent
commit
1ac6140535
  1. 186
      src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategy.cs
  2. 114
      src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategyImage.cs
  3. 31
      src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategyRow.cs

186
src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategy.cs

@ -0,0 +1,186 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static SixLabors.ImageSharp.Formats.Jxl.JxlFrameDimensions;
#pragma warning disable SA1405 // Debug.Assert should provide message text
namespace SixLabors.ImageSharp.Formats.Jxl.Ac;
[StructLayout(LayoutKind.Sequential, Pack = 8)]
internal struct JxlAcStrategy
{
public const int MaximumCoefficientBlocks = 32;
public const int MaximumBlockDimension = BlockDimensions * MaximumCoefficientBlocks;
public const int MaximumCoefficientArea = MaximumBlockDimension * MaximumBlockDimension;
public const int NumberOfValidStrategies = 27;
private static readonly int MultiblockBits =
GetTypeBit(JxlAcStrategyType.DCT16X16) | GetTypeBit(JxlAcStrategyType.DCT32X32) |
GetTypeBit(JxlAcStrategyType.DCT16X8) | GetTypeBit(JxlAcStrategyType.DCT8X16) |
GetTypeBit(JxlAcStrategyType.DCT32X8) | GetTypeBit(JxlAcStrategyType.DCT8X32) |
GetTypeBit(JxlAcStrategyType.DCT16X32) | GetTypeBit(JxlAcStrategyType.DCT32X16) |
GetTypeBit(JxlAcStrategyType.DCT32X64) | GetTypeBit(JxlAcStrategyType.DCT64X32) |
GetTypeBit(JxlAcStrategyType.DCT64X64) | GetTypeBit(JxlAcStrategyType.DCT64X128) |
GetTypeBit(JxlAcStrategyType.DCT128X64) |
GetTypeBit(JxlAcStrategyType.DCT128X128) |
GetTypeBit(JxlAcStrategyType.DCT128X256) |
GetTypeBit(JxlAcStrategyType.DCT256X128) |
GetTypeBit(JxlAcStrategyType.DCT256X256);
private readonly bool isFirst;
public JxlAcStrategy(JxlAcStrategyType strategy, bool isFirst)
{
this.Strategy = strategy;
this.isFirst = isFirst;
Debug.Assert(this.IsMultiblock);
}
public JxlAcStrategy(JxlAcStrategyType strategy)
: this(strategy, true)
{
}
public JxlAcStrategy(int rawStrategy)
: this((JxlAcStrategyType)rawStrategy)
{
}
private static ReadOnlySpan<byte> CoveredBlocksXLookup =>
[
1, 1, 1, 1, 2, 4, 1, 2, 1,
4, 2, 4, 1, 1, 1, 1, 1, 1,
8, 4, 8, 16, 8, 16, 32, 16, 32
];
private static ReadOnlySpan<byte> CoveredBlocksYLookup =>
[
1, 1, 1, 1, 2, 4, 2, 1, 4,
1, 4, 2, 1, 1, 1, 1, 1, 1,
8, 8, 4, 16, 16, 8, 32, 32, 16
];
private static ReadOnlySpan<byte> Log2CoveredBlocksLookup =>
[
0, 0, 0, 0, 2, 4, 1, 1, 2,
2, 3, 3, 0, 0, 0, 0, 0, 0,
6, 5, 5, 8, 7, 7, 10, 9, 9
];
public readonly bool IsMultiblock => ((1 << (int)this.Strategy) & MultiblockBits) != 0;
public readonly int RawStrategy => (int)this.Strategy;
public readonly int CoveredBlocksX => CoveredBlocksXLookup[(int)this.Strategy];
public readonly int CoveredBlocksY => CoveredBlocksYLookup[(int)this.Strategy];
public readonly int Log2CoveredBlocks => Log2CoveredBlocksLookup[(int)this.Strategy];
public readonly JxlAcStrategyType Strategy { get; }
public void ComputeNaturalCoefficientOrder(ref int order) => CoefficientOrderAndLookup(this, false, ref order);
public void ComputeNaturalCoefficientOrderLookup(ref int lookup) => CoefficientOrderAndLookup(this, true, ref lookup);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetTypeBit(JxlAcStrategyType type) => 1 << (int)type;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsRawStrategyValid(int rawStrategy) => rawStrategy is < NumberOfValidStrategies and >= 0;
private static void CoefficientOrderAndLookup(JxlAcStrategy strategy, bool isLookup, Span<int> output)
{
// TODO: CoefficientLayout
// TODO: CeilLog2Nonzero
int cx = strategy.CoveredBlocksX;
int cy = strategy.CoveredBlocksY;
CoefficientLayout(ref cx, ref cy);
int xs = cx / cy;
int xsm = xs - 1;
int xss = CeilLog2Nonzero(xs);
int cur = cx * cy;
for (int i = 0; i < cx * BlockDimensions; i++)
{
for (int j = 0; j <= i; j++)
{
int x = j;
int y = i - j;
if ((i & 1) == 0)
{
// swap
(x, y) = (y, x);
}
if ((y & xsm) != 0)
{
continue;
}
y >>= xss;
int value = 0;
if (x < cx && y < cy)
{
value = (y * cx) + x;
}
else
{
value = cur++;
}
if (isLookup)
{
output[((y * cx) * BlockDimensions) + x] = value;
}
else
{
output[value] = ((y * cx) * BlockDimensions) + x;
}
}
}
for (int ip = (cx * BlockDimensions) - 1; ip > 0; ip--)
{
int i = ip - 1;
for (int j = 0; j <= i; j++)
{
int x = (cx * BlockDimensions) - 1 - (i - j);
int y = (cx * BlockDimensions) - 1 - j;
if ((i & 1) != 0)
{
// swap
(x, y) = (y, x);
}
if ((y & xsm) != 0)
{
continue;
}
y >>= xss;
int value = cur++;
if (isLookup)
{
output[((y * cx) * BlockDimensions) + x] = value;
}
else
{
output[value] = ((y * cx) * BlockDimensions) + x;
}
}
}
}
}

114
src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategyImage.cs

@ -0,0 +1,114 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Jxl.Ac;
internal sealed class JxlAcStrategyImage
{
private const byte Invalid = byte.MaxValue; // 255
private JxlImageB? layers;
private readonly Memory<byte> row;
private readonly int stride;
public JxlMemoryManager MemoryManager => this.layers.MemoryManager;
public int XSize => this.layers.XSize;
public int YSize => this.layers.YSize;
public int PixelsPerRow => this.layers.PixelsPerRow;
public JxlAcStrategyRow GetRow(int y, int xPrefix = 0)
{
ReadOnlyMemory<byte> layerRow = this.layers.GetRow(y);
ReadOnlyMemory<byte> row = layerRow[xPrefix..];
return new JxlAcStrategyRow(row);
}
public static JxlAcStrategyImage Create(JxlMemoryManager memoryManager, int xSize, int ySize)
{
JxlAcStrategyImage image = new()
{
layers = JxlImageB.Create(memoryManager, xSize, ySize)
};
image.row = image.layers.GetRow(0);
image.stride = image.layers.PixelsPerRow;
return image;
}
public int CountBlocks(JxlAcStrategyType type)
{
int value = 0;
int compare = ((int)type << 1) | 1;
for (int y = 0; y < this.layers.YSize; y++)
{
ReadOnlySpan<byte> row = this.layers.GetRowSpan(y);
for (int x = 0; x < this.layers.XSize; x++)
{
if (row[x] == compare)
{
value++;
}
}
}
return value;
}
public JxlAcStrategyRow GetRow(in Rectangle rect, int y) => this.GetRow(rect.Y + y, rect.X);
public bool IsValid(int x, int y) => this.row.Span[(y * this.stride) + x] != Invalid;
public bool SetNoBoundsChecks(int x, int y, JxlAcStrategyType type, bool check = true)
{
JxlAcStrategy strategy = new(type);
Span<byte> rowSpan = this.row.Span;
int rawType = (int)type;
int rawTypeTimes2 = rawType << 1;
for (int iy = 0; iy < strategy.CoveredBlocksX; iy++)
{
for (int ix = 0; ix < strategy.CoveredBlocksX; ix++)
{
int pos = ((y + iy) * this.stride) + x + ix;
if (check && rowSpan[pos] != Invalid)
{
Debug.Fail("Invalid AC strategy. Blocks overlap.");
return false;
}
rowSpan[pos] = (byte)(rawTypeTimes2 | ((iy | ix) == 0 ? 1 : 0));
}
}
return true;
}
public bool Set(int x, int y, JxlAcStrategyType type)
{
#if DEBUG
JxlAcStrategy strategy = new(type);
Debug.Assert(y + strategy.CoveredBlocksY <= this.layers.YSize, "Invalid range");
Debug.Assert(x + strategy.CoveredBlocksX <= this.layers.XSize, "Invalid range");
#endif
return this.SetNoBoundsChecks(x, y, type, check: false);
}
public void FillDct8(in Rectangle rect) => this.FillPlane(((int)JxlAcStrategyType.DCT << 1) | 1, this.layers, in rect);
public void FillDct8() => this.FillDct8(in this.layers.GetRectangle());
public void FillInvalid() => this.FillImage(Invalid, this.layers);
}

31
src/ImageSharp/Formats/Jxl/Ac/JxlAcStrategyRow.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jxl.Ac;
internal sealed class JxlAcStrategyRow
{
private readonly ReadOnlyMemory<byte> row;
public JxlAcStrategyRow(ReadOnlyMemory<byte> row) => this.row = row;
public JxlAcStrategy this[int x]
{
get
{
ReadOnlySpan<byte> span = this.row.Span;
Debug.Assert(x * 8 < span.Length, "Too many bytes of memory were requested");
ref byte first = ref MemoryMarshal.GetReference(span);
JxlAcStrategyType strategy = (JxlAcStrategyType)(Unsafe.Add(ref Unsafe.As<byte, int>(ref first), x) >> 1);
bool isFirst = Unsafe.Add(ref first, x) != 0;
return new JxlAcStrategy(strategy, isFirst);
}
}
}
Loading…
Cancel
Save