mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Currently, there's a VarLenUint8/VarLenUint16 as well as histogram parsing implementation. I will additionally have to implement parsing of ANS codes, uint config and LZ77 parameters.pull/3153/head
3 changed files with 263 additions and 4 deletions
@ -0,0 +1,256 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Buffers; |
||||
|
using System.Diagnostics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.IO; |
||||
|
|
||||
|
internal static class JxlAnsReader |
||||
|
{ |
||||
|
// Prefer jagged arrays over multidimensional arrays
|
||||
|
// for performance. Collection expressions help represent
|
||||
|
// jagged arrays easily.
|
||||
|
private static readonly byte[][] HuffmanLookup = |
||||
|
[ |
||||
|
[3, 10], [7, 12], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [5, 0], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [6, 11], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [5, 0], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [7, 13], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [5, 0], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [6, 11], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
[3, 10], [5, 0], [3, 7], [4, 3], [3, 6], [3, 8], [3, 9], [4, 5], |
||||
|
[3, 10], [4, 4], [3, 7], [4, 1], [3, 6], [3, 8], [3, 9], [4, 2], |
||||
|
]; |
||||
|
|
||||
|
public static uint DecodeVariableLengthUint8(JxlBitReader reader) |
||||
|
{ |
||||
|
if (reader.ReadBoolean()) |
||||
|
{ |
||||
|
uint bitCount = reader.ReadBits32(3u); |
||||
|
|
||||
|
return bitCount == 0 |
||||
|
? 1u |
||||
|
: (reader.ReadBits32(bitCount) + (1u << (int)bitCount)); |
||||
|
} |
||||
|
|
||||
|
return 0u; |
||||
|
} |
||||
|
|
||||
|
public static uint DecodeVariableLengthUint16(JxlBitReader reader) |
||||
|
{ |
||||
|
if (reader.ReadBoolean()) |
||||
|
{ |
||||
|
uint bitCount = reader.ReadBits32(4u); |
||||
|
|
||||
|
return bitCount == 0 |
||||
|
? 1u |
||||
|
: (reader.ReadBits32(bitCount) + (1u << (int)bitCount)); |
||||
|
} |
||||
|
|
||||
|
return 0u; |
||||
|
} |
||||
|
|
||||
|
// NOTE: this method returns null on failure.
|
||||
|
// If the return value is a valid IMemoryOwner object,
|
||||
|
// then it succeeded.
|
||||
|
public static IMemoryOwner<uint>? ReadHistogram(Configuration configuration, int precisionBits, JxlBitReader reader) |
||||
|
{ |
||||
|
int range = 1 << precisionBits; |
||||
|
bool isSimpleCode = reader.ReadBoolean(); |
||||
|
|
||||
|
IMemoryOwner<uint> counts; |
||||
|
|
||||
|
if (isSimpleCode) |
||||
|
{ |
||||
|
Span<uint> symbols = stackalloc uint[2]; |
||||
|
symbols.Clear(); |
||||
|
|
||||
|
uint maxSymbol = 0u; |
||||
|
uint symCount = reader.ReadBits32(1u) + 1u; |
||||
|
for (uint i = 0; i < symCount; i++) |
||||
|
{ |
||||
|
uint symbol = DecodeVariableLengthUint8(reader); |
||||
|
if (symbol > maxSymbol) |
||||
|
{ |
||||
|
maxSymbol = symbol; |
||||
|
} |
||||
|
|
||||
|
symbols[(int)i] = symbol; |
||||
|
} |
||||
|
|
||||
|
// Up to 256 items
|
||||
|
counts = configuration.MemoryAllocator.Allocate<uint>((int)maxSymbol + 1); |
||||
|
Span<uint> countsSpan = counts.Memory.Span; |
||||
|
|
||||
|
if (symCount == 1) |
||||
|
{ |
||||
|
countsSpan[(int)symbols[0]] = (uint)range; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (symbols[0] == symbols[1]) |
||||
|
{ |
||||
|
Debug.Fail("Corrupt data"); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
countsSpan[(int)symbols[0]] = reader.ReadBits32((uint)precisionBits); |
||||
|
countsSpan[(int)symbols[1]] = (uint)range - countsSpan[(int)symbols[0]]; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
bool isFlat = reader.ReadBoolean(); |
||||
|
|
||||
|
if (isFlat) |
||||
|
{ |
||||
|
uint alphabetSize = DecodeVariableLengthUint8(reader) + 1u; |
||||
|
if (alphabetSize <= range) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
counts = JxlAnsHelper.CreateFlatHistogram(configuration, (int)alphabetSize, range); |
||||
|
return counts; |
||||
|
} |
||||
|
|
||||
|
int upperBoundLog = FloorLog2Nonzero(JxlAnsConstants.AnsLogTableSize + 1); |
||||
|
int log = 0; |
||||
|
|
||||
|
for (; log < upperBoundLog; log++) |
||||
|
{ |
||||
|
bool logIncrementBit = reader.ReadBoolean(); |
||||
|
|
||||
|
if (!logIncrementBit) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
uint shift = (reader.ReadBits32((uint)log) | (1u << log)) - 1u; |
||||
|
|
||||
|
if (shift > JxlAnsConstants.AnsLogTableSize + 1) |
||||
|
{ |
||||
|
Debug.Fail("Invalid shift"); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
uint length = DecodeVariableLengthUint8(reader) + 3u; |
||||
|
|
||||
|
counts = configuration.MemoryAllocator.Allocate<uint>((int)length); |
||||
|
Span<uint> countsSpan = counts.Memory.Span; |
||||
|
|
||||
|
uint totalCount = 0; |
||||
|
|
||||
|
// The length variable can represent up to 258 elements,
|
||||
|
// so it'd be more beneficial to allocate on the stack
|
||||
|
// than pool an array.
|
||||
|
Span<int> logCounts = stackalloc int[(int)length]; |
||||
|
|
||||
|
// stackalloc doesn't zero-init, so clear just in case.
|
||||
|
logCounts.Clear(); |
||||
|
|
||||
|
int omitLog = -1; |
||||
|
int omitPos = -1; |
||||
|
|
||||
|
// See comments for logCounts definition
|
||||
|
Span<int> same = stackalloc int[(int)length]; |
||||
|
same.Clear(); |
||||
|
|
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
uint index = reader.PeekBits32(7); |
||||
|
reader.SkipBits32(HuffmanLookup[index][0]); |
||||
|
logCounts[i] = HuffmanLookup[index][1] - 1; |
||||
|
|
||||
|
if (logCounts[i] == JxlAnsConstants.AnsLogTableSize) |
||||
|
{ |
||||
|
uint rleLength = DecodeVariableLengthUint8(reader); |
||||
|
same[i] = (int)rleLength + 5; |
||||
|
i += (int)rleLength + 3; |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (logCounts[i] > omitLog) |
||||
|
{ |
||||
|
omitLog = logCounts[i]; |
||||
|
omitPos = i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (omitPos < 0) |
||||
|
{ |
||||
|
Debug.Fail("The histogram is corrupt or invalid."); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (omitPos + 1 < length && logCounts[omitPos + 1] == JxlAnsConstants.AnsLogTableSize) |
||||
|
{ |
||||
|
Debug.Fail("The histogram is corrupt or invalid."); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
int previous = 0; |
||||
|
int sameCount = 0; |
||||
|
|
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
if (same[i] > 0) |
||||
|
{ |
||||
|
sameCount = same[i] - 1; |
||||
|
previous = i > 0 ? (int)countsSpan[i - 1] : 0; |
||||
|
} |
||||
|
|
||||
|
if (sameCount > 0) |
||||
|
{ |
||||
|
countsSpan[i] = (uint)previous; |
||||
|
sameCount--; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int code = logCounts[i]; |
||||
|
|
||||
|
if (i == omitPos || code < 0) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
else if (shift == 0 || code == 0) |
||||
|
{ |
||||
|
countsSpan[i] = 1u << code; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int bitCount = JxlAnsHelper.GetPopulationCountPrecision(code, (int)shift); |
||||
|
countsSpan[i] = (1u << code) + (reader.ReadBits32((uint)bitCount) << (code - bitCount)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
totalCount += countsSpan[i]; |
||||
|
} |
||||
|
|
||||
|
countsSpan[omitPos] = (uint)range - totalCount; |
||||
|
|
||||
|
if (countsSpan[omitPos] <= 0) |
||||
|
{ |
||||
|
Debug.Fail("The histogram count is incorrect."); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return counts; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue