mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 209 additions and 0 deletions
@ -0,0 +1,33 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Encoder.ProgressiveSplit; |
||||
|
|
||||
|
internal struct JxlPassDefinition |
||||
|
{ |
||||
|
public JxlPassDefinition(int numCoefficients, int shift, int suitableForDownsamplingOfAtLeast) |
||||
|
{ |
||||
|
this.NumCoefficients = numCoefficients; |
||||
|
this.Shift = shift; |
||||
|
this.SuitableForDownsamplingOfAtLeast = suitableForDownsamplingOfAtLeast; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the side of the square of the coefficients that should be kept in
|
||||
|
/// each 8x8 block. Must be > 1, and at most 8. Should be in non-decreasing
|
||||
|
/// order.
|
||||
|
/// </summary>
|
||||
|
public int NumCoefficients { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets how much to shift the encoded values by, with rounding.
|
||||
|
/// </summary>
|
||||
|
public int Shift { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value where, if specified indicates that if the required downsampling factor
|
||||
|
/// is sufficiently high, then it is fine to stop decoding after this pass.
|
||||
|
/// By default, passes are not marked as being suitable for any downsampling.
|
||||
|
/// </summary>
|
||||
|
public int SuitableForDownsamplingOfAtLeast { get; set; } |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Encoder.ProgressiveSplit; |
||||
|
|
||||
|
internal struct JxlProgressiveMode |
||||
|
{ |
||||
|
public int NumPasses = 1; |
||||
|
public InlineArray11<JxlPassDefinition> Passes; |
||||
|
|
||||
|
public JxlProgressiveMode() |
||||
|
{ |
||||
|
Span<JxlPassDefinition> passesSpan = this.Passes; |
||||
|
JxlPassDefinition definitionToFill = new(numCoefficients: 8, shift: 0, suitableForDownsamplingOfAtLeast: 1); |
||||
|
passesSpan.Fill(definitionToFill); |
||||
|
} |
||||
|
|
||||
|
public JxlProgressiveMode(Span<JxlPassDefinition> passes) |
||||
|
{ |
||||
|
int nump = passes.Length; |
||||
|
DebugGuard.MustBeLessThanOrEqualTo(nump, JxlShared.MaximumNumberOfPasses, nameof(nump)); |
||||
|
|
||||
|
this.NumPasses = nump; |
||||
|
JxlPassDefinition previousPass = new(1, 0, int.MaxValue); |
||||
|
int lastDownsamplingFactor = int.MaxValue; |
||||
|
|
||||
|
for (int i = 0; i < nump; i++) |
||||
|
{ |
||||
|
ref JxlPassDefinition p = ref passes[i]; |
||||
|
|
||||
|
if (!(p.NumCoefficients > previousPass.NumCoefficients || |
||||
|
(p.NumCoefficients == previousPass.NumCoefficients && |
||||
|
p.Shift < previousPass.Shift))) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The pass is invalid"); |
||||
|
} |
||||
|
|
||||
|
if (!(p.SuitableForDownsamplingOfAtLeast == int.MaxValue || |
||||
|
p.SuitableForDownsamplingOfAtLeast <= lastDownsamplingFactor)) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The pass is invalid"); |
||||
|
} |
||||
|
|
||||
|
if (p.SuitableForDownsamplingOfAtLeast != int.MaxValue) |
||||
|
{ |
||||
|
lastDownsamplingFactor = p.SuitableForDownsamplingOfAtLeast; |
||||
|
} |
||||
|
|
||||
|
previousPass = passes[i] = p; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using SixLabors.ImageSharp.Formats.Jxl.IO.FrameHeader; |
||||
|
using SixLabors.ImageSharp.Formats.Jxl.Processing.AcStrategy; |
||||
|
using SixLabors.ImageSharp.Formats.Jxl.Processing.Primitives; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Encoder.ProgressiveSplit; |
||||
|
|
||||
|
internal struct JxlProgressiveSplitter |
||||
|
{ |
||||
|
private JxlProgressiveMode progressiveMode; |
||||
|
|
||||
|
public void SetProgressiveMode(JxlProgressiveMode mode) => this.progressiveMode = mode; |
||||
|
|
||||
|
public readonly int GetNumPasses() => this.progressiveMode.NumPasses; |
||||
|
|
||||
|
public readonly void InitializePasses(JxlPasses passes) |
||||
|
{ |
||||
|
passes.NumberOfPasses = (uint)this.GetNumPasses(); |
||||
|
passes.NumberOfDownsamples = 0u; |
||||
|
|
||||
|
if (passes.NumberOfPasses == 0) |
||||
|
{ |
||||
|
throw new InvalidOperationException("At least one pass must be present"); |
||||
|
} |
||||
|
|
||||
|
passes.Shift[(int)passes.NumberOfPasses - 1] = 0; |
||||
|
|
||||
|
if (passes.NumberOfPasses == 1) |
||||
|
{ |
||||
|
// Done. Arrays are empty.
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < this.progressiveMode.NumPasses - 1; ++i) |
||||
|
{ |
||||
|
int minDownsamplingFactor = this.progressiveMode.Passes[i].SuitableForDownsamplingOfAtLeast; |
||||
|
passes.Shift[i] = (uint)this.progressiveMode.Passes[i].Shift; |
||||
|
|
||||
|
if (minDownsamplingFactor is > 1 and not int.MaxValue) |
||||
|
{ |
||||
|
passes.Downsample[(int)passes.NumberOfDownsamples] = (uint)minDownsamplingFactor; |
||||
|
passes.LastPass[(int)passes.NumberOfDownsamples] = (uint)i; |
||||
|
|
||||
|
if (this.progressiveMode.Passes[i + 1].SuitableForDownsamplingOfAtLeast < minDownsamplingFactor) |
||||
|
{ |
||||
|
passes.NumberOfDownsamples++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public readonly void SplitAcCoefficients<T>(Span<T> block, JxlAcStrategy acs, int bx, int by, Span<InlineArray11<T>> output) |
||||
|
where T : unmanaged, INumber<T>, IShiftOperators<T, T, T> |
||||
|
{ |
||||
|
int size = acs.CoveredBlocksX * acs.CoveredBlocksY * JxlFrameDimensions.DctBlockSize; |
||||
|
|
||||
|
if (this.progressiveMode.NumPasses == 1) |
||||
|
{ |
||||
|
block.CopyTo(output[0]); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int nCoeffsAllDoneFromEarlierPasses = 1; |
||||
|
int previousPassShift = 0; |
||||
|
|
||||
|
for (int passNumber = 0; passNumber < this.progressiveMode.NumPasses; passNumber++) |
||||
|
{ |
||||
|
MemoryMarshal.Cast<InlineArray11<T>, T>(output[passNumber..]).Slice(0, size).Clear(); |
||||
|
|
||||
|
int passShift = this.progressiveMode.Passes[passNumber].Shift; |
||||
|
int frameNCoeffs = this.progressiveMode.Passes[passNumber].NumCoefficients; |
||||
|
int xsize = acs.CoveredBlocksX; |
||||
|
int ysize = acs.CoveredBlocksY; |
||||
|
|
||||
|
JxlForwardCoefficientOrder.CoefficientLayout(ref ysize, ref xsize); |
||||
|
|
||||
|
for (int y = 0; y < ysize * frameNCoeffs; y++) |
||||
|
{ |
||||
|
for (int x = 0; x < xsize * frameNCoeffs; x++) |
||||
|
{ |
||||
|
int pos = (y * xsize * JxlFrameDimensions.BlockDimensions) + x; |
||||
|
|
||||
|
if (x < xsize * nCoeffsAllDoneFromEarlierPasses && y < ysize * nCoeffsAllDoneFromEarlierPasses) |
||||
|
{ |
||||
|
// This coefficient was already included in an earlier pass,
|
||||
|
// which included a genuinely smaller set of coefficients.
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
T v = block[pos]; |
||||
|
|
||||
|
if (previousPassShift != 0) |
||||
|
{ |
||||
|
T previousV = ShiftRightRound0(v, previousPassShift) * T.CreateSaturating(1 << previousPassShift); |
||||
|
v -= previousV; |
||||
|
} |
||||
|
|
||||
|
output[passNumber][pos] = ShiftRightRound0(v, passShift); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (this.progressiveMode.Passes[passNumber].Shift == 0) |
||||
|
{ |
||||
|
nCoeffsAllDoneFromEarlierPasses = frameNCoeffs; |
||||
|
} |
||||
|
|
||||
|
previousPassShift = this.progressiveMode.Passes[passNumber].Shift; |
||||
|
} |
||||
|
|
||||
|
static T ShiftRightRound0(T v, int shift) |
||||
|
{ |
||||
|
T oneIfNegative = v >> T.CreateSaturating(31); |
||||
|
T add = (oneIfNegative << T.CreateSaturating(shift)) - oneIfNegative; |
||||
|
return (v + add) >> T.CreateSaturating(shift); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue