Browse Source

Reconstruct Intra16/Intra4 and UV

pull/1552/head
Brian Popow 6 years ago
parent
commit
d981e91b6b
  1. 50
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
  2. 317
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  3. 45
      src/ImageSharp/Formats/WebP/Lossy/Vp8Matrix.cs
  4. 53
      src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs
  5. 40
      src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs
  6. 6
      src/ImageSharp/Formats/WebP/WebPLookupTables.cs

50
src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs

@ -455,6 +455,44 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
Dst(dst, 3, 3, l);
}
/// <summary>
/// Paragraph 14.3: Implementation of the Walsh-Hadamard transform inversion.
/// </summary>
public static void TransformWht(short[] input, short[] output)
{
var tmp = new int[16];
for (int i = 0; i < 4; ++i)
{
int iPlus4 = 4 + i;
int iPlus8 = 8 + i;
int iPlus12 = 12 + i;
int a0 = input[i] + input[iPlus12];
int a1 = input[iPlus4] + input[iPlus8];
int a2 = input[iPlus4] - input[iPlus8];
int a3 = input[i] - input[iPlus12];
tmp[i] = a0 + a1;
tmp[iPlus8] = a0 - a1;
tmp[iPlus4] = a3 + a2;
tmp[iPlus12] = a3 - a2;
}
int outputOffset = 0;
for (int i = 0; i < 4; ++i)
{
int imul4 = i * 4;
int dc = tmp[0 + imul4] + 3;
int a0 = dc + tmp[3 + imul4];
int a1 = tmp[1 + imul4] + tmp[2 + imul4];
int a2 = tmp[1 + imul4] - tmp[2 + imul4];
int a3 = dc - tmp[3 + imul4];
output[outputOffset + 0] = (short)((a0 + a1) >> 3);
output[outputOffset + 16] = (short)((a3 + a2) >> 3);
output[outputOffset + 32] = (short)((a0 - a1) >> 3);
output[outputOffset + 48] = (short)((a3 - a2) >> 3);
outputOffset += 64;
}
}
public static void TransformTwo(Span<short> src, Span<byte> dst)
{
TransformOne(src, dst);
@ -740,6 +778,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
dst[x + (y * WebPConstants.Bps)] = v;
}
[MethodImpl(InliningOptions.ShortMethod)]
public static byte Clip8B(int v)
{
return (byte)((v & ~0xff) == 0 ? v : (v < 0) ? 0 : 255);
}
// Complex In-loop filtering (Paragraph 15.3)
private static void FilterLoop24(
Span<byte> p,
@ -933,12 +977,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
return (a * 35468) >> 16;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8B(int v)
{
return (byte)((v & ~0xff) == 0 ? v : (v < 0) ? 0 : 255);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8(int v)
{

317
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs

@ -34,6 +34,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private const int YuvHalf = 1 << (YuvFix - 1);
private const int KC1 = 20091 + (1 << 16);
private const int KC2 = 35468;
private const int MaxLevel = 2047;
private const int QFix = 17;
private readonly byte[] zigzag = new byte[] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 };
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Encoder"/> class.
/// </summary>
@ -117,6 +127,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
mb[i] = new Vp8MacroBlockInfo();
}
var segmentInfos = new Vp8SegmentInfo[4];
for (int i = 0; i < 4; i++)
{
segmentInfos[i] = new Vp8SegmentInfo();
}
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Preds, this.Nz, mb, mbw, mbh);
int method = 4; // TODO: hardcoded for now
int quality = 100; // TODO: hardcoded for now
@ -126,13 +142,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// Analysis is done, proceed to actual coding.
// TODO: EncodeAlpha();
// Compute segment probabilities.
this.SetSegmentProbas(segmentInfos);
this.SetupMatrices(segmentInfos);
it.Init();
it.InitFilter();
do
{
var info = new Vp8ModeScore();
it.Import(y, u, v, yStride, uvStride, width, height);
if (!this.Decimate(it, info, method))
if (!this.Decimate(it, segmentInfos, info, method))
{
this.CodeResiduals(it);
}
@ -159,6 +178,19 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Preds.Dispose();
}
private void SetSegmentProbas(Vp8SegmentInfo[] dqm)
{
var p = new int[4];
int n;
// TODO: SetSegmentProbas
}
private void SetupMatrices(Vp8SegmentInfo[] dqm)
{
// TODO: SetupMatrices
}
private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span<byte> y, Span<byte> u, Span<byte> v, int yStride, int uvStride, int method, int quality, int[] alphas, out int uvAlpha)
{
int alpha = 0;
@ -207,7 +239,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
return bestAlpha; // Mixed susceptibility (not just luma).
}
private bool Decimate(Vp8EncIterator it, Vp8ModeScore rd, int method)
private bool Decimate(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, int method)
{
rd.InitScore();
@ -219,7 +251,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower).
// For method <= 1, we don't re-examine the decision but just go ahead with
// quantization/reconstruction.
this.RefineUsingDistortion(it, rd, method >= 2, method >= 1);
this.RefineUsingDistortion(it, segmentInfos, rd, method >= 2, method >= 1);
bool isSkipped = rd.Nz == 0;
it.SetSkip(isSkipped);
@ -228,14 +260,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
// Refine intra16/intra4 sub-modes based on distortion only (not rate).
private void RefineUsingDistortion(Vp8EncIterator it, Vp8ModeScore rd, bool tryBothModes, bool refineUvMode)
private void RefineUsingDistortion(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, bool tryBothModes, bool refineUvMode)
{
long bestScore = Vp8ModeScore.MaxCost;
int nz = 0;
int mode;
bool isI16 = tryBothModes || (it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16);
// TODO: VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment];
// Some empiric constants, of approximate order of magnitude.
int lambdaDi16 = 106;
@ -324,7 +355,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
// Reconstruct partial block inside yuv_out2 buffer
Span<byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebPLookupTables.Vp8Scan[it.I4]);
// TODO: nz |= ReconstructIntra4(it, rd.YAcLevels[it.I4], src, tmpDst, bestI4Mode) << it.I4;
nz |= this.ReconstructIntra4(it, dqm, rd.YAcLevels[it.I4], src, tmpDst, bestI4Mode) << it.I4;
}
}
while (it.RotateI4(it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc)));
@ -339,7 +370,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
else
{
// TODO: nz = ReconstructIntra16(it, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), it.Preds.GetSpan()[0]);
nz = this.ReconstructIntra16(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), it.Preds.GetSpan()[0]);
}
// ... and UV!
@ -362,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
it.SetIntraUvMode(bestMode);
}
// TODO: nz |= ReconstructUv(it, rd, it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc), it.CurrentMacroBlockInfo.UvMode);
nz |= this.ReconstructUv(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc), it.CurrentMacroBlockInfo.UvMode);
rd.Nz = (uint)nz;
rd.Score = bestScore;
@ -373,19 +404,263 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
private void ReconstructIntra16()
private int ReconstructIntra16(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span<byte> yuvOut, int mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8EncIterator.Vp8I16ModeOffsets[mode]);
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
int nz = 0;
int n;
var dcTmp = new short[16];
var tmp = new short[16][];
for (int i = 0; i < 16; i++)
{
tmp[i] = new short[16];
}
for (n = 0; n < 16; n += 2)
{
this.FTransform2(src.Slice(WebPLookupTables.Vp8Scan[n]), reference.Slice(WebPLookupTables.Vp8Scan[n]), tmp[n]);
}
this.FTransformWht(tmp[0], dcTmp);
nz |= this.QuantizeBlock(dcTmp, rd.YDcLevels, dqm.Y2) << 24;
for (n = 0; n < 16; n += 2)
{
// Zero-out the first coeff, so that: a) nz is correct below, and
// b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified.
tmp[n][0] = tmp[n + 1][0] = 0;
nz |= this.Quantize2Blocks(tmp[n], rd.YAcLevels[n], dqm.Y1) << n;
}
// Transform back.
LossyUtils.TransformWht(dcTmp, tmp[0]);
for (n = 0; n < 16; n += 2)
{
this.ITransform(reference.Slice(WebPLookupTables.Vp8Scan[n]), tmp[n], yuvOut.Slice(WebPLookupTables.Vp8Scan[n]), true);
}
return nz;
}
private int ReconstructIntra4(Vp8EncIterator it, Vp8SegmentInfo dqm, short[] levels, Span<byte> src, Span<byte> yuvOut, int mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8EncIterator.Vp8I4ModeOffsets[mode]);
var tmp = new short[16];
this.FTransform(src, reference, tmp);
var nz = this.QuantizeBlock(tmp, levels, dqm.Y1);
this.ITransform(reference, tmp, yuvOut, false);
return nz;
}
private void ReconstructIntra4()
private int ReconstructUv(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span<byte> yuvOut, int mode)
{
Span<byte> reference = it.YuvP.AsSpan(Vp8EncIterator.Vp8UvModeOffsets[mode]);
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
int nz = 0;
int n;
var tmp = new short[8][];
for (int i = 0; i < 8; i++)
{
tmp[i] = new short[16];
}
for (n = 0; n < 8; n += 2)
{
this.FTransform2(src.Slice(WebPLookupTables.Vp8ScanUv[n]), reference.Slice(WebPLookupTables.Vp8ScanUv[n]), tmp[n]);
}
/* TODO:
if (it->top_derr_ != NULL)
{
CorrectDCValues(it, &dqm->uv_, tmp, rd);
}*/
for (n = 0; n < 8; n += 2)
{
nz |= this.Quantize2Blocks(tmp[n], rd.UvLevels[n], dqm.Uv) << n;
}
for (n = 0; n < 8; n += 2)
{
this.ITransform(reference.Slice(WebPLookupTables.Vp8ScanUv[n]), tmp[n], yuvOut.Slice(WebPLookupTables.Vp8ScanUv[n]), true);
}
return nz << 16;
}
private void FTransform2(Span<byte> src, Span<byte> reference, short[] output)
{
this.FTransform(src, reference, output);
this.FTransform(src.Slice(4), reference.Slice(4), output.AsSpan(16));
}
private void ReconstructUv()
private void FTransform(Span<byte> src, Span<byte> reference, Span<short> output)
{
int i;
var tmp = new int[16];
for (i = 0; i < 4; ++i)
{
int d0 = src[0] - reference[0]; // 9bit dynamic range ([-255,255])
int d1 = src[1] - reference[1];
int d2 = src[2] - reference[2];
int d3 = src[3] - reference[3];
int a0 = d0 + d3; // 10b [-510,510]
int a1 = d1 + d2;
int a2 = d1 - d2;
int a3 = d0 - d3;
tmp[0 + (i * 4)] = (a0 + a1) * 8; // 14b [-8160,8160]
tmp[1 + (i * 4)] = ((a2 * 2217) + (a3 * 5352) + 1812) >> 9; // [-7536,7542]
tmp[2 + (i * 4)] = (a0 - a1) * 8;
tmp[3 + (i * 4)] = ((a3 * 2217) - (a2 * 5352) + 937) >> 9;
src = src.Slice(WebPConstants.Bps);
reference = reference.Slice(WebPConstants.Bps);
}
for (i = 0; i < 4; ++i)
{
int a0 = tmp[0 + i] + tmp[12 + i]; // 15b
int a1 = tmp[4 + i] + tmp[8 + i];
int a2 = tmp[4 + i] - tmp[8 + i];
int a3 = tmp[0 + i] - tmp[12 + i];
output[0 + i] = (short)((a0 + a1 + 7) >> 4); // 12b
output[4 + i] = (short)((((a2 * 2217) + (a3 * 5352) + 12000) >> 16) + (a3 != 0 ? 1 : 0));
output[8 + i] = (short)((a0 - a1 + 7) >> 4);
output[12 + i] = (short)(((a3 * 2217) - (a2 * 5352) + 51000) >> 16);
}
}
private void FTransformWht(Span<short> input, Span<short> output)
{
var tmp = new int[16];
int i;
for (i = 0; i < 4; ++i)
{
int a0 = input[0 * 16] + input[2 * 16]; // 13b
int a1 = input[1 * 16] + input[3 * 16];
int a2 = input[1 * 16] - input[3 * 16];
int a3 = input[0 * 16] - input[2 * 16];
tmp[0 + (i * 4)] = a0 + a1; // 14b
tmp[1 + (i * 4)] = a3 + a2;
tmp[2 + (i * 4)] = a3 - a2;
tmp[3 + (i * 4)] = a0 - a1;
input = input.Slice(64);
}
for (i = 0; i < 4; ++i)
{
int a0 = tmp[0 + i] + tmp[8 + i]; // 15b
int a1 = tmp[4 + i] + tmp[12 + i];
int a2 = tmp[4 + i] - tmp[12 + i];
int a3 = tmp[0 + i] - tmp[8 + i];
int b0 = a0 + a1; // 16b
int b1 = a3 + a2;
int b2 = a3 - a2;
int b3 = a0 - a1;
output[ 0 + i] = (short)(b0 >> 1); // 15b
output[ 4 + i] = (short)(b1 >> 1);
output[ 8 + i] = (short)(b2 >> 1);
output[12 + i] = (short)(b3 >> 1);
}
}
private int Quantize2Blocks(Span<short> input, Span<short> output, Vp8Matrix mtx)
{
int nz;
nz = this.QuantizeBlock(input, output, mtx) << 0;
nz |= this.QuantizeBlock(input.Slice(1 * 16), output.Slice(1 * 16), mtx) << 1;
return nz;
}
private int QuantizeBlock(Span<short> input, Span<short> output, Vp8Matrix mtx)
{
int last = -1;
int n;
for (n = 0; n < 16; ++n)
{
int j = zigzag[n];
bool sign = input[j] < 0;
uint coeff = (uint)((sign ? -input[j] : input[j]) + mtx.Sharpen[j]);
if (coeff > mtx.ZThresh[j])
{
uint Q = (uint)mtx.Q[j];
uint iQ = (uint)mtx.IQ[j];
uint B = mtx.Bias[j];
int level = this.QuantDiv(coeff, iQ, B);
if (level > MaxLevel)
{
level = MaxLevel;
}
if (sign)
{
level = -level;
}
input[j] = (short)(level * (int)Q);
output[n] = (short)level;
if (level != 0)
{
last = n;
}
}
else
{
output[n] = 0;
input[j] = 0;
}
}
return (last >= 0) ? 1 : 0;
}
private void ITransform(Span<byte> reference, short[] input, Span<byte> dst, bool doTwo)
{
this.ITransformOne(reference, input, dst);
if (doTwo)
{
this.ITransformOne(reference.Slice(4), input.AsSpan(16), dst.Slice(4));
}
}
private void ITransformOne(Span<byte> reference, Span<short> input, Span<byte> dst)
{
int i;
var C = new int[4 * 4];
Span<int> tmp = C.AsSpan();
for (i = 0; i < 4; ++i)
{
// vertical pass.
int a = input[0] + input[8];
int b = input[0] - input[8];
int c = this.Mul(input[4], KC2) - this.Mul(input[12], KC1);
int d = this.Mul(input[4], KC1) + this.Mul(input[12], KC2);
tmp[0] = a + d;
tmp[1] = b + c;
tmp[2] = b - c;
tmp[3] = a - d;
tmp = tmp.Slice(4);
input = input.Slice(1);
}
tmp = C.AsSpan();
for (i = 0; i < 4; ++i)
{
// horizontal pass.
int dc = tmp[0] + 4;
int a = dc + tmp[8];
int b = dc - tmp[8];
int c = this.Mul(tmp[4], KC2) - this.Mul(tmp[12], KC1);
int d = this.Mul(tmp[4], KC1) + this.Mul(tmp[12], KC2);
this.Store(dst, reference, 0, i, (byte)(a + d));
this.Store(dst, reference, 1, i, (byte)(b + c));
this.Store(dst, reference, 2, i, (byte)(b - c));
this.Store(dst, reference, 3, i, (byte)(a - d));
tmp = tmp.Slice(1);
}
}
private void ConvertRgbToYuv<TPixel>(Image<TPixel> image)
@ -742,5 +1017,23 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
return true;
}
[MethodImpl(InliningOptions.ShortMethod)]
private int QuantDiv(uint n, uint iQ, uint b)
{
return (int)(((n * iQ) + b) >> QFix);
}
[MethodImpl(InliningOptions.ShortMethod)]
private void Store(Span<byte> dst, Span<byte> reference, int x, int y, byte v)
{
dst[x + (y * WebPConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebPConstants.Bps)] + (v >> 3));
}
[MethodImpl(InliningOptions.ShortMethod)]
private int Mul(int a, int b)
{
return (a * b) >> 16;
}
}
}

45
src/ImageSharp/Formats/WebP/Lossy/Vp8Matrix.cs

@ -0,0 +1,45 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal class Vp8Matrix
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Matrix"/> class.
/// </summary>
public Vp8Matrix()
{
this.Q = new short[16];
this.IQ = new short[16];
this.Bias = new uint[16];
this.ZThresh = new uint[16];
this.Sharpen = new short[16];
}
/// <summary>
/// quantizer steps.
/// </summary>
public short[] Q { get; }
/// <summary>
/// reciprocals, fixed point.
/// </summary>
public short[] IQ { get; }
/// <summary>
/// rounding bias.
/// </summary>
public uint[] Bias { get; }
/// <summary>
/// value below which a coefficient is zeroed.
/// </summary>
public uint[] ZThresh { get; }
/// <summary>
/// frequency boosters for slight sharpening.
/// </summary>
public short[] Sharpen { get; }
}
}

53
src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal class Vp8SegmentInfo
{
/// <summary>
/// quantization matrix y1.
/// </summary>
public Vp8Matrix Y1 { get; set; }
/// <summary>
/// quantization matrix y2.
/// </summary>
public Vp8Matrix Y2 { get; set; }
/// <summary>
/// quantization matrix uv.
/// </summary>
public Vp8Matrix Uv { get; set; }
/// <summary>
/// quant-susceptibility, range [-127,127]. Zero is neutral. Lower values indicate a lower risk of blurriness.
/// </summary>
public int Alpha { get; set; }
/// <summary>
/// filter-susceptibility, range [0,255].
/// </summary>
public int Beta { get; set; }
/// <summary>
/// final segment quantizer.
/// </summary>
public int Quant { get; set; }
/// <summary>
/// final in-loop filtering strength.
/// </summary>
public int FStrength { get; set; }
/// <summary>
/// max edge delta (for filtering strength).
/// </summary>
public int MaxEdge { get; set; }
/// <summary>
/// penalty for using Intra4.
/// </summary>
public long I4Penalty { get; set; }
}
}

40
src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs

@ -890,7 +890,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
if (nz > 1)
{
// More than just the DC -> perform the full transform.
this.TransformWht(dc, dst);
LossyUtils.TransformWht(dc, dst);
}
else
{
@ -1078,44 +1078,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
return v;
}
/// <summary>
/// Paragraph 14.3: Implementation of the Walsh-Hadamard transform inversion.
/// </summary>
private void TransformWht(short[] input, short[] output)
{
var tmp = new int[16];
for (int i = 0; i < 4; ++i)
{
int iPlus4 = 4 + i;
int iPlus8 = 8 + i;
int iPlus12 = 12 + i;
int a0 = input[i] + input[iPlus12];
int a1 = input[iPlus4] + input[iPlus8];
int a2 = input[iPlus4] - input[iPlus8];
int a3 = input[i] - input[iPlus12];
tmp[i] = a0 + a1;
tmp[iPlus8] = a0 - a1;
tmp[iPlus4] = a3 + a2;
tmp[iPlus12] = a3 - a2;
}
int outputOffset = 0;
for (int i = 0; i < 4; ++i)
{
int imul4 = i * 4;
int dc = tmp[0 + imul4] + 3;
int a0 = dc + tmp[3 + imul4];
int a1 = tmp[1 + imul4] + tmp[2 + imul4];
int a2 = tmp[1 + imul4] - tmp[2 + imul4];
int a3 = dc - tmp[3 + imul4];
output[outputOffset + 0] = (short)((a0 + a1) >> 3);
output[outputOffset + 16] = (short)((a3 + a2) >> 3);
output[outputOffset + 32] = (short)((a0 - a1) >> 3);
output[outputOffset + 48] = (short)((a3 - a2) >> 3);
outputOffset += 64;
}
}
private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba)
{
var vp8SegmentHeader = new Vp8SegmentHeader

6
src/ImageSharp/Formats/WebP/WebPLookupTables.cs

@ -45,6 +45,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
0 + (12 * WebPConstants.Bps), 4 + (12 * WebPConstants.Bps), 8 + (12 * WebPConstants.Bps), 12 + (12 * WebPConstants.Bps),
};
public static readonly short[] Vp8ScanUv =
{
0 + (0 * WebPConstants.Bps), 4 + (0 * WebPConstants.Bps), 0 + (4 * WebPConstants.Bps), 4 + (4 * WebPConstants.Bps), // U
8 + (0 * WebPConstants.Bps), 12 + (0 * WebPConstants.Bps), 8 + (4 * WebPConstants.Bps), 12 + (4 * WebPConstants.Bps) // V
};
public static readonly short[,][] Vp8FixedCostsI4 = new short[10, 10][];
/// <summary>

Loading…
Cancel
Save