mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 331 additions and 0 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
@ -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…
Reference in new issue