Browse Source

Add transforms decoder, some ANS encoder work

pull/3153/head
winscripter 3 weeks ago
parent
commit
fdba3ee042
  1. 2
      src/ImageSharp/Formats/Jxl/IO/Jpeg/JpegWriter.cs
  2. 57
      src/ImageSharp/Formats/Jxl/Processing/Dct/JxlDctScales.cs
  3. 1215
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlTransformsDecoder.cs
  4. 256
      src/ImageSharp/Formats/Jxl/Processing/Encoder/JxlAcStrategyEncoder.cs

2
src/ImageSharp/Formats/Jxl/IO/Jpeg/JpegWriter.cs

@ -377,7 +377,7 @@ internal class JpegWriter
huffTable = state.DcHuffTable[index]; huffTable = state.DcHuffTable[index];
} }
huffTable.InitDepths(127); huffTable.InitializeDepths(127);
if (!BuildJpegHuffmanCodeTable(huff, huffTable)) if (!BuildJpegHuffmanCodeTable(huff, huffTable))
{ {

57
src/ImageSharp/Formats/Jxl/Processing/Dct/JxlDctScales.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Dct; namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Dct;
/// <summary> /// <summary>
@ -385,4 +387,59 @@ internal static class JxlDctScales
256 => Multipliers256, 256 => Multipliers256,
_ => [] _ => []
}; };
[MethodImpl(InliningOptions.HotPath | InliningOptions.AlwaysInline)]
public static ReadOnlySpan<float> GetResampleScales(int x, int y)
{
DebugGuard.IsTrue(x is 1 or 2 or 4 or 8 or 16 or 32 or 64 or 128 or 256, "x is invalid");
if (x == 1)
{
DebugGuard.IsTrue(y == 8, "Only y=8 resample scale for x=1 exists");
return ResampleScales1_8;
}
else if (x == 2)
{
DebugGuard.IsTrue(y == 16, "Only y=16 resample scale for x=2 exists");
return ResampleScales2_16;
}
else if (x == 4)
{
DebugGuard.IsTrue(y == 32, "Only y=32 resample scale for x=4 exists");
return ResampleScales4_32;
}
else if (x == 8)
{
DebugGuard.IsTrue(y is 1 or 64, "Only y=1,64 resample scale for x=8 exists");
return y == 1 ? ResampleScales8_1 : ResampleScales8_64;
}
else if (x == 16)
{
DebugGuard.IsTrue(y is 2 or 128, "Only y=2,128 resample scale for x=16 exists");
return y == 2 ? ResampleScales16_2 : ResampleScales16_128;
}
else if (x == 32)
{
DebugGuard.IsTrue(y is 4 or 256, "Only y=4,256 resample scale for x=32 exists");
return y == 4 ? ResampleScales32_4 : ResampleScales32_256;
}
else if (x == 64)
{
DebugGuard.IsTrue(y == 8, "Only y=8 resample scale for x=64 exists");
return ResampleScales64_8;
}
else if (x == 128)
{
DebugGuard.IsTrue(y == 16, "Only y=16 resample scale for x=128 exists");
return ResampleScales128_16;
}
else if (x == 256)
{
DebugGuard.IsTrue(y == 32, "Only y=32 resample scale for x=256 exists");
return ResampleScales256_32;
}
else
{
return [];
}
}
} }

1215
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlTransformsDecoder.cs

File diff suppressed because it is too large

256
src/ImageSharp/Formats/Jxl/Processing/Encoder/JxlAcStrategyEncoder.cs

@ -0,0 +1,256 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Jxl.Processing.AcStrategy;
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Encoder;
internal static class JxlAcStrategyEncoder
{
private static readonly byte[][] TypeColors =
[
[0xFF, 0xFF, 0x00], // DCT8 | yellow
[0xFF, 0x80, 0x80], // HORNUSS | vivid tangerine
[0xFF, 0x80, 0x80], // DCT2x2 | vivid tangerine
[0xFF, 0x80, 0x80], // DCT4x4 | vivid tangerine
[0x80, 0xFF, 0x00], // DCT16x16 | chartreuse
[0x00, 0xC0, 0x00], // DCT32x32 | waystone green
[0xC0, 0xFF, 0x00], // DCT16x8 | lime
[0xC0, 0xFF, 0x00], // DCT8x16 | lime
[0x00, 0xFF, 0x00], // DCT32x8 | green
[0x00, 0xFF, 0x00], // DCT8x32 | green
[0x00, 0xFF, 0x00], // DCT32x16 | green
[0x00, 0xFF, 0x00], // DCT16x32 | green
[0xFF, 0x80, 0x00], // DCT4x8 | orange juice
[0xFF, 0x80, 0x00], // DCT8x4 | orange juice
[0xFF, 0xFF, 0x80], // AFV0 | butter
[0xFF, 0xFF, 0x80], // AFV1 | butter
[0xFF, 0xFF, 0x80], // AFV2 | butter
[0xFF, 0xFF, 0x80], // AFV3 | butter
[0x00, 0xC0, 0xFF], // DCT64x64 | capri
[0x00, 0xFF, 0xFF], // DCT64x32 | aqua
[0x00, 0xFF, 0xFF], // DCT32x64 | aqua
[0x00, 0x40, 0xFF], // DCT128x128 | rare blue
[0x00, 0x80, 0xFF], // DCT128x64 | magic ink
[0x00, 0x80, 0xFF], // DCT64x128 | magic ink
[0x00, 0x00, 0xC0], // DCT256x256 | keese blue
[0x00, 0x00, 0xFF], // DCT256x128 | blue
[0x00, 0x00, 0xFF], // DCT128x256 | blue
[0x00, 0x00, 0x00] // invalid | black
];
#pragma warning disable // Indentation warnings only
private static readonly byte[][] Mask =
[
[
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
], // DCT8
[
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 1, 0, 0, 1, 0, 0, //
0, 0, 1, 0, 0, 1, 0, 0, //
0, 0, 1, 1, 1, 1, 0, 0, //
0, 0, 1, 0, 0, 1, 0, 0, //
0, 0, 1, 0, 0, 1, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
], // HORNUSS
[
1, 1, 1, 1, 1, 1, 1, 1, //
1, 0, 1, 0, 1, 0, 1, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
1, 0, 1, 0, 1, 0, 1, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
1, 0, 1, 0, 1, 0, 1, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
1, 0, 1, 0, 1, 0, 1, 0, //
], // 2x2
[
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
], // 4x4
[ ], // DCT16x16 (unused)
[ ], // DCT32x32 (unused)
[ ], // DCT16x8 (unused)
[ ], // DCT8x16 (unused)
[ ], // DCT32x8 (unused)
[ ], // DCT8x32 (unused)
[ ], // DCT32x16 (unused)
[ ], // DCT16x32 (unused)
[
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
], // DCT4x8
[
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
0, 0, 0, 0, 1, 0, 0, 0, //
], // DCT8x4
[
1, 1, 1, 1, 1, 0, 0, 0, //
1, 1, 1, 1, 0, 0, 0, 0, //
1, 1, 1, 0, 0, 0, 0, 0, //
1, 1, 0, 0, 0, 0, 0, 0, //
1, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
], // AFV0
[
0, 0, 0, 0, 1, 1, 1, 1, //
0, 0, 0, 0, 0, 1, 1, 1, //
0, 0, 0, 0, 0, 0, 1, 1, //
0, 0, 0, 0, 0, 0, 0, 1, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
], // AFV1
[
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
1, 0, 0, 0, 0, 0, 0, 0, //
1, 1, 0, 0, 0, 0, 0, 0, //
1, 1, 1, 0, 0, 0, 0, 0, //
1, 1, 1, 1, 0, 0, 0, 0, //
], // AFV2
[
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 0, //
0, 0, 0, 0, 0, 0, 0, 1, //
0, 0, 0, 0, 0, 0, 1, 1, //
0, 0, 0, 0, 0, 1, 1, 1, //
], // AFV3
[ ] // invalid
];
#pragma warning restore
public static ReadOnlySpan<byte> TypeColor(int rawStrategy)
{
DebugGuard.IsTrue(JxlAcStrategy.IsRawStrategyValid(rawStrategy), $"Raw strategy must be a valid strategy: {rawStrategy}");
rawStrategy = Math.Clamp(rawStrategy, 0, JxlAcStrategy.NumberOfValidStrategies);
return TypeColors[rawStrategy];
}
public static ReadOnlySpan<byte> TypeMask(int rawStrategy)
{
DebugGuard.IsTrue(JxlAcStrategy.IsRawStrategyValid(rawStrategy), $"Raw strategy must be a valid strategy: {rawStrategy}");
rawStrategy = Math.Clamp(rawStrategy, 0, JxlAcStrategy.NumberOfValidStrategies);
return Mask[rawStrategy];
}
public static bool MultiBlockTransformCrossesHorizontalBoundary(JxlAcStrategyImage acStrategyImage, int startX, int y, int endX)
{
if (startX >= acStrategyImage.XSize || y >= acStrategyImage.YSize)
{
return false;
}
if (y % 8 == 0)
{
// Nothing crosses 64x64 boundaries, and the memory on the other side
// of the 64x64 block may still uninitialized.
return false;
}
endX = Math.Min(endX, acStrategyImage.XSize);
// The first multiblock might be before the start_x, let's adjust it
// to point to the first IsFirstBlock() == true block we find by backward
// tracing.
JxlAcStrategyRow row = acStrategyImage.GetRow(y);
int startXLimit = startX & ~7;
while (startX != startXLimit && !row[startX].IsFirstBlock)
{
startX--;
}
for (int x = startX; x < endX;)
{
if (row[x].IsFirstBlock)
{
x += row[x].CoveredBlocksX;
}
else
{
return true;
}
}
return false;
}
public static bool MultiBlockTransformCrossesVerticalBoundary(JxlAcStrategyImage acStrategyImage, int x, int startY, int endY)
{
if (x >= acStrategyImage.XSize || startY >= acStrategyImage.YSize)
{
return false;
}
if (x % 8 == 0)
{
// Nothing crosses 64x64 boundaries, and the memory on the other side
// of the 64x64 block may still uninitialized.
return false;
}
endY = Math.Min(endY, acStrategyImage.YSize);
// The first multiblock might be before the start_y, let's adjust it
// to point to the first IsFirstBlock() == true block we find by backward
// tracing.
int startYLimit = startY & ~7;
while (startY != startYLimit && !acStrategyImage.GetRow(startY)[x].IsFirstBlock)
{
startY--;
}
for (int y = startY; y < endY;)
{
JxlAcStrategyRow row = acStrategyImage.GetRow(y);
if (row[x].IsFirstBlock)
{
y += row[x].CoveredBlocksY;
}
else
{
return true;
}
}
return false;
}
}
Loading…
Cancel
Save