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