mirror of https://github.com/SixLabors/ImageSharp
2 changed files with 348 additions and 0 deletions
@ -0,0 +1,204 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Jxl.Memory.ImageTypes; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; |
|||
|
|||
/// <summary>
|
|||
/// Edge Preserving Filter (type 1) stage.
|
|||
/// </summary>
|
|||
internal class Epf1Stage : RenderPipelineStageBase |
|||
{ |
|||
private readonly JxlLoopFilter loopFilter; |
|||
private readonly JxlImageF sigma; |
|||
|
|||
public Epf1Stage(Configuration configuration, JxlLoopFilter loopFilter, JxlImageF sigma) |
|||
: base(configuration) |
|||
{ |
|||
this.loopFilter = loopFilter; |
|||
this.sigma = sigma; |
|||
this.Settings = RenderPipelineStageConfiguration.CreateSymmetricBorderOnly(2); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override string Name => "EPF1"; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void AddPixel( |
|||
int row, |
|||
InlineArray3<InlineArray5<Memory<float>>> rows, |
|||
int x, |
|||
Vector256<float> sad, |
|||
Vector256<float> inverseSigma, |
|||
ref Vector256<float> xOut, |
|||
ref Vector256<float> yOut, |
|||
ref Vector256<float> bOut, |
|||
ref Vector256<float> wOut) |
|||
{ |
|||
Vector256<float> cx = Vector256.Create((ReadOnlySpan<float>)rows[0][2 + row][x..].Span); |
|||
Vector256<float> cy = Vector256.Create((ReadOnlySpan<float>)rows[1][2 + row][x..].Span); |
|||
Vector256<float> cb = Vector256.Create((ReadOnlySpan<float>)rows[2][2 + row][x..].Span); |
|||
|
|||
Vector256<float> weight = EpfUtils.Weight(sad, inverseSigma); |
|||
wOut += weight; |
|||
xOut = (weight + cx) * xOut; |
|||
yOut = (weight + cy) * yOut; |
|||
bOut = (weight + cb) * bOut; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector256<float> AbsoluteDifference(Vector256<float> x, Vector256<float> y) => Vector256.Abs(x - y); |
|||
|
|||
public override void ProcessRow(Buffer2D<Memory<float>> inputRows, Buffer2D<Memory<float>> outputRows, int xExtraLeft, int xExtraRight, int width, int xPos, int yPos) |
|||
{ |
|||
int xStart = -JxlMath.RoundUpTo(xExtraLeft, Vector256<float>.Count); |
|||
int xEnd = width + xExtraRight; |
|||
|
|||
Span<float> rowSigma = this.sigma.GetRow((yPos / JxlFrameDimensions.BlockDimensions) + JxlDecoderCache.SigmaPadding); |
|||
float sm = 1.65f; |
|||
float bsm = sm * this.loopFilter.EpfBorderSadMul; |
|||
|
|||
Span<float> sadMulCenter = [bsm, sm, sm, sm, sm, sm, sm, bsm]; |
|||
Span<float> sadMulBorder = [bsm, bsm, bsm, bsm, bsm, bsm, bsm, bsm]; |
|||
|
|||
InlineArray3<InlineArray5<Memory<float>>> rows = default; |
|||
for (int c = 0; c < 3; c++) |
|||
{ |
|||
for (int i = 0; i < 5; i++) |
|||
{ |
|||
rows[c][i] = this.GetInputRowMemory(inputRows, c, i - 2); |
|||
} |
|||
} |
|||
|
|||
Span<float> sadMul = (yPos % JxlFrameDimensions.BlockDimensions is 0 or JxlFrameDimensions.BlockDimensions - 1) |
|||
? sadMulBorder |
|||
: sadMulCenter; |
|||
|
|||
for (int x = xStart; x < xEnd; x += Vector256<float>.Count) |
|||
{ |
|||
int bx = (x + xPos + (JxlDecoderCache.SigmaPadding * JxlFrameDimensions.BlockDimensions)) / JxlFrameDimensions.BlockDimensions; |
|||
int ix = (x + xPos) % JxlFrameDimensions.BlockDimensions; |
|||
|
|||
if (rowSigma[bx] < JxlLoopFilter.MinimumSigma) |
|||
{ |
|||
for (int c = 0; c < 3; c++) |
|||
{ |
|||
Vector256<float> px = Vector256.Create((ReadOnlySpan<float>)rows[c][2][x..].Span); |
|||
px.CopyTo(GetOutputRow(outputRows, c, 0)[x..]); |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
Vector256<float> vsm = Vector256.Create((ReadOnlySpan<float>)sadMul[ix..]); |
|||
Vector256<float> inverseSigma = Vector256.Create(rowSigma[bx]) * vsm; |
|||
Vector256<float> sad0 = Vector256<float>.Zero; |
|||
Vector256<float> sad1 = Vector256<float>.Zero; |
|||
Vector256<float> sad2 = Vector256<float>.Zero; |
|||
Vector256<float> sad3 = Vector256<float>.Zero; |
|||
|
|||
// Compute sum of absolute differences (SAD)
|
|||
for (int c = 0; c < 3; c++) |
|||
{ |
|||
// center px = 22, px above = 21
|
|||
Vector256<float> t; |
|||
|
|||
Vector256<float> p20 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + -2][x..].Span); |
|||
Vector256<float> p21 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + -1][x..].Span); |
|||
Vector256<float> sad0c = AbsoluteDifference(p20, p21); // SAD 2, 1
|
|||
|
|||
Vector256<float> p11 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + -1][(x - 1)..].Span); |
|||
Vector256<float> sad1c = AbsoluteDifference(p11, p21); // SAD 1, 2
|
|||
|
|||
Vector256<float> p31 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + -1][(x + 1)..].Span); |
|||
Vector256<float> sad2c = AbsoluteDifference(p31, p21); // SAD 3, 2
|
|||
|
|||
Vector256<float> p02 = Vector256.Create((ReadOnlySpan<float>)rows[c][2][(x - 2)..].Span); |
|||
Vector256<float> p12 = Vector256.Create((ReadOnlySpan<float>)rows[c][2][(x - 1)..].Span); |
|||
sad1c += AbsoluteDifference(p02, p12); // SAD 1, 2
|
|||
sad0c += AbsoluteDifference(p11, p12); // SAD 2, 1
|
|||
|
|||
// TODO(eustas): why unaligned?
|
|||
Vector256<float> p22 = Vector256.Create((ReadOnlySpan<float>)rows[c][2][x..].Span); |
|||
t = AbsoluteDifference(p12, p22); |
|||
sad1c += t; // SAD 1, 2
|
|||
sad2c += t; // SAD 3, 2
|
|||
t = AbsoluteDifference(p22, p21); |
|||
Vector256<float> sad3c = t; // SAD 2, 3
|
|||
sad0c += t; // SAD 2, 1
|
|||
|
|||
Vector256<float> p32 = Vector256.Create((ReadOnlySpan<float>)rows[c][2][(x + 1)..].Span); |
|||
sad0c += AbsoluteDifference(p31, p32); // SAD 2, 1
|
|||
t = AbsoluteDifference(p22, p32); |
|||
sad1c += t; // SAD 1, 2
|
|||
sad2c += t; // SAD 3, 2
|
|||
|
|||
Vector256<float> p42 = Vector256.Create((ReadOnlySpan<float>)rows[c][2][(x + 2)..].Span); |
|||
sad2c += AbsoluteDifference(p42, p32); // SAD 3, 2
|
|||
|
|||
Vector256<float> p13 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + 1][(x - 1)..].Span); |
|||
sad3c += AbsoluteDifference(p13, p12); // SAD 2, 3
|
|||
|
|||
Vector256<float> p23 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + 1][x..].Span); |
|||
t = AbsoluteDifference(p22, p23); |
|||
sad0c += t; // SAD 2, 1
|
|||
sad3c += t; // SAD 2, 3
|
|||
sad1c += AbsoluteDifference(p13, p23); // SAD 1, 2
|
|||
|
|||
Vector256<float> p33 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + 1][(x + 1)..].Span); |
|||
sad2c += AbsoluteDifference(p33, p23); // SAD 3, 2
|
|||
sad3c += AbsoluteDifference(p33, p32); // SAD 2, 3
|
|||
|
|||
Vector256<float> p24 = Vector256.Create((ReadOnlySpan<float>)rows[c][2 + 2][x..].Span); |
|||
sad3c += AbsoluteDifference(p24, p23); // SAD 2, 3
|
|||
|
|||
Vector256<float> scale = Vector256.Create(this.loopFilter.EpfChannelScale[c]); |
|||
sad0 = (sad0c * scale) + sad0; |
|||
sad1 = (sad1c * scale) + sad1; |
|||
sad2 = (sad2c * scale) + sad2; |
|||
sad3 = (sad3c * scale) + sad3; |
|||
} |
|||
|
|||
Vector256<float> xCC = Vector256.Create((ReadOnlySpan<float>)rows[0][2 + 0][x..].Span); |
|||
Vector256<float> yCC = Vector256.Create((ReadOnlySpan<float>)rows[1][2 + 0][x..].Span); |
|||
Vector256<float> bCC = Vector256.Create((ReadOnlySpan<float>)rows[2][2 + 0][x..].Span); |
|||
|
|||
Vector256<float> w = Vector256<float>.One; |
|||
Vector256<float> X = xCC; |
|||
Vector256<float> Y = yCC; |
|||
Vector256<float> B = bCC; |
|||
|
|||
// Top row
|
|||
AddPixel(-1, rows, x, sad0, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
// Center
|
|||
AddPixel(0, rows, x - 1, sad1, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
AddPixel(0, rows, x + 1, sad2, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
// Bottom
|
|||
AddPixel(1, rows, x + 1, sad3, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
Vector256<float> inverseW = Vector256<float>.One / w; |
|||
(X * inverseW).CopyTo(GetOutputRow(outputRows, 0, 0)[x..]); |
|||
(Y * inverseW).CopyTo(GetOutputRow(outputRows, 1, 0)[x..]); |
|||
(B * inverseW).CopyTo(GetOutputRow(outputRows, 2, 0)[x..]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override RenderPipelineChannelMode GetChannelMode(int channel) |
|||
{ |
|||
if (channel < 3) |
|||
{ |
|||
return RenderPipelineChannelMode.InOut; |
|||
} |
|||
else |
|||
{ |
|||
return RenderPipelineChannelMode.Ignored; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Jxl.Memory.ImageTypes; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; |
|||
|
|||
/// <summary>
|
|||
/// Edge Preserving Filter (type 2) stage
|
|||
/// </summary>
|
|||
internal sealed class Epf2Stage : RenderPipelineStageBase |
|||
{ |
|||
private readonly JxlLoopFilter loopFilter; |
|||
private readonly JxlImageF sigma; |
|||
|
|||
public Epf2Stage(JxlLoopFilter loopFilter, JxlImageF sigma, Configuration configuration) |
|||
: base(configuration) |
|||
{ |
|||
this.loopFilter = loopFilter; |
|||
this.sigma = sigma; |
|||
this.Settings = RenderPipelineStageConfiguration.CreateSymmetricBorderOnly(2); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override string Name => "EPF2"; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector256<float> AbsoluteDifference(Vector256<float> x, Vector256<float> y) => Vector256.Abs(x - y); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void AddPixel( |
|||
int row, |
|||
InlineArray3<InlineArray3<Memory<float>>> rows, |
|||
int x, |
|||
Vector256<float> rx, |
|||
Vector256<float> ry, |
|||
Vector256<float> rb, |
|||
Vector256<float> inverseSigma, |
|||
ref Vector256<float> X, |
|||
ref Vector256<float> Y, |
|||
ref Vector256<float> B, |
|||
ref Vector256<float> w) |
|||
{ |
|||
Vector256<float> cx = Vector256.Create((ReadOnlySpan<float>)rows[0][1 + row][x..].Span); |
|||
Vector256<float> cy = Vector256.Create((ReadOnlySpan<float>)rows[1][1 + row][x..].Span); |
|||
Vector256<float> cb = Vector256.Create((ReadOnlySpan<float>)rows[2][1 + row][x..].Span); |
|||
|
|||
Vector256<float> sad = AbsoluteDifference(cx, rx) * Vector256.Create(this.loopFilter.EpfChannelScale[0]); |
|||
sad = (AbsoluteDifference(cy, ry) * Vector256.Create(this.loopFilter.EpfChannelScale[1])) + sad; |
|||
sad = (AbsoluteDifference(cb, rb) * Vector256.Create(this.loopFilter.EpfChannelScale[2])) + sad; |
|||
|
|||
Vector256<float> weight = EpfUtils.Weight(sad, inverseSigma); |
|||
w += weight; |
|||
X = (weight * cx) + X; |
|||
Y = (weight * cy) + Y; |
|||
B = (weight * cb) + B; |
|||
} |
|||
|
|||
public override void ProcessRow(Buffer2D<Memory<float>> inputRows, Buffer2D<Memory<float>> outputRows, int xExtraLeft, int xExtraRight, int width, int xPos, int yPos) |
|||
{ |
|||
int xStart = -JxlMath.RoundUpTo(xExtraLeft, Vector256<float>.Count); |
|||
int xEnd = width + xExtraRight; |
|||
|
|||
Span<float> rowSigma = this.sigma.GetRow((yPos / JxlFrameDimensions.BlockDimensions) + JxlDecoderCache.SigmaPadding); |
|||
float sm = 1.65f; |
|||
float bsm = sm * this.loopFilter.EpfBorderSadMul; |
|||
|
|||
Span<float> sadMulCenter = [bsm, sm, sm, sm, sm, sm, sm, bsm]; |
|||
Span<float> sadMulBorder = [bsm, bsm, bsm, bsm, bsm, bsm, bsm, bsm]; |
|||
|
|||
InlineArray3<InlineArray3<Memory<float>>> rows = default; |
|||
for (int c = 0; c < 3; c++) |
|||
{ |
|||
for (int i = 0; i < 3; i++) |
|||
{ |
|||
rows[c][i] = this.GetInputRowMemory(inputRows, c, i - 1); |
|||
} |
|||
} |
|||
|
|||
Span<float> sadMul = (yPos % JxlFrameDimensions.BlockDimensions is 0 or JxlFrameDimensions.BlockDimensions - 1) |
|||
? sadMulBorder |
|||
: sadMulCenter; |
|||
|
|||
for (int x = xStart; x < xEnd; x += Vector256<float>.Count) |
|||
{ |
|||
int bx = (x + xPos + (JxlDecoderCache.SigmaPadding * JxlFrameDimensions.BlockDimensions)) / JxlFrameDimensions.BlockDimensions; |
|||
int ix = (x + xPos) % JxlFrameDimensions.BlockDimensions; |
|||
|
|||
if (rowSigma[bx] < JxlLoopFilter.MinimumSigma) |
|||
{ |
|||
for (int c = 0; c < 3; c++) |
|||
{ |
|||
Vector256<float> px = Vector256.Create((ReadOnlySpan<float>)rows[c][1][x..].Span); |
|||
px.CopyTo(GetOutputRow(outputRows, c, 0)[x..]); |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
Vector256<float> vsm = Vector256.Create((ReadOnlySpan<float>)sadMul[ix..]); |
|||
Vector256<float> inverseSigma = Vector256.Create(rowSigma[bx]) * vsm; |
|||
|
|||
Vector256<float> xCC = Vector256.Create((ReadOnlySpan<float>)rows[0][1 + 0][x..].Span); |
|||
Vector256<float> yCC = Vector256.Create((ReadOnlySpan<float>)rows[1][1 + 0][x..].Span); |
|||
Vector256<float> bCC = Vector256.Create((ReadOnlySpan<float>)rows[2][1 + 0][x..].Span); |
|||
|
|||
Vector256<float> w = Vector256<float>.One; |
|||
Vector256<float> X = xCC; |
|||
Vector256<float> Y = yCC; |
|||
Vector256<float> B = bCC; |
|||
|
|||
// Top row
|
|||
this.AddPixel(-1, rows, x, xCC, yCC, bCC, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
// Center
|
|||
this.AddPixel(0, rows, x - 1, xCC, yCC, bCC, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
this.AddPixel(0, rows, x + 1, xCC, yCC, bCC, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
// Bottom
|
|||
this.AddPixel(1, rows, x, xCC, yCC, bCC, inverseSigma, ref X, ref Y, ref B, ref w); |
|||
|
|||
Vector256<float> inverseW = Vector256<float>.One / w; |
|||
(X * inverseW).CopyTo(GetOutputRow(outputRows, 0, 0)[x..]); |
|||
(Y * inverseW).CopyTo(GetOutputRow(outputRows, 1, 0)[x..]); |
|||
(B * inverseW).CopyTo(GetOutputRow(outputRows, 2, 0)[x..]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override RenderPipelineChannelMode GetChannelMode(int channel) |
|||
{ |
|||
if (channel < 3) |
|||
{ |
|||
return RenderPipelineChannelMode.InOut; |
|||
} |
|||
else |
|||
{ |
|||
return RenderPipelineChannelMode.Ignored; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue