Browse Source

Add PickBestUv

pull/1552/head
Brian Popow 5 years ago
parent
commit
96bb666522
  1. 26
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
  2. 5
      src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs
  3. 28
      src/ImageSharp/Formats/WebP/Lossy/Vp8Costs.cs
  4. 42
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  5. 50
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs
  6. 143
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  7. 5
      src/ImageSharp/Formats/WebP/Lossy/Vp8ProbaArray.cs
  8. 22
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
  9. 4
      src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs

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

@ -480,18 +480,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int[] tmp = new int[16];
// horizontal pass.
int inputOffset = 0;
for (int i = 0; i < 4; ++i)
{
int a0 = input[0] + input[2];
int a1 = input[1] + input[3];
int a2 = input[1] - input[3];
int a3 = input[0] - input[2];
int inputOffsetPlusOne = inputOffset + 1;
int inputOffsetPlusTwo = inputOffset + 2;
int inputOffsetPlusThree = inputOffset + 3;
int a0 = input[inputOffset] + input[inputOffsetPlusTwo];
int a1 = input[inputOffsetPlusOne] + input[inputOffsetPlusThree];
int a2 = input[inputOffsetPlusOne] - input[inputOffsetPlusThree];
int a3 = input[inputOffset] - input[inputOffsetPlusTwo];
tmp[0 + (i * 4)] = a0 + a1;
tmp[1 + (i * 4)] = a3 + a2;
tmp[2 + (i * 4)] = a3 - a2;
tmp[3 + (i * 4)] = a0 - a1;
input = input.Slice(WebpConstants.Bps);
inputOffset += WebpConstants.Bps;
}
// vertical pass
@ -549,6 +553,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// an input in [-2048, 2047] interval. We then need to add a dst value in the [0, 255] range.
// In the worst case scenario, the input to clip_8b() can be as large as [-60713, 60968].
tmpOffset = 0;
int dstOffset = 0;
for (int i = 0; i < 4; ++i)
{
// horizontal pass
@ -560,12 +565,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int b = dc - tmp[tmpOffsetPlus8];
int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]);
int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]);
Store(dst, 0, 0, a + d);
Store(dst, 1, 0, b + c);
Store(dst, 2, 0, b - c);
Store(dst, 3, 0, a - d);
Store(dst.Slice(dstOffset), 0, 0, a + d);
Store(dst.Slice(dstOffset), 1, 0, b + c);
Store(dst.Slice(dstOffset), 2, 0, b - c);
Store(dst.Slice(dstOffset), 3, 0, a - d);
tmpOffset++;
dst = dst.Slice(WebpConstants.Bps);
dstOffset += WebpConstants.Bps;
}
}

5
src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs

@ -8,10 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary>
/// Initializes a new instance of the <see cref="Vp8CostArray"/> class.
/// </summary>
public Vp8CostArray()
{
this.Costs = new ushort[WebpConstants.NumCtx * (67 + 1)];
}
public Vp8CostArray() => this.Costs = new ushort[WebpConstants.NumCtx * (67 + 1)];
public ushort[] Costs { get; }
}

28
src/ImageSharp/Formats/WebP/Lossy/Vp8Costs.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal class Vp8Costs
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Costs"/> class.
/// </summary>
public Vp8Costs()
{
this.Costs = new Vp8CostArray[WebpConstants.NumCtx];
for (int i = 0; i < WebpConstants.NumCtx; i++)
{
this.Costs[i] = new Vp8CostArray();
}
}
/// <summary>
/// Gets the Costs.
/// </summary>
public Vp8CostArray[] Costs { get; }
}
}

42
src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs

@ -77,6 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.LeftNz = new int[9];
this.I4Boundary = new byte[37];
this.BitCount = new long[4, 3];
this.Scratch = new byte[WebpConstants.Bps * 16];
// To match the C initial values of the reference implementation, initialize all with 204.
byte defaultInitVal = 204;
@ -86,6 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.YuvP.AsSpan().Fill(defaultInitVal);
this.YLeft.AsSpan().Fill(defaultInitVal);
this.UvLeft.AsSpan().Fill(defaultInitVal);
this.Scratch.AsSpan().Fill(defaultInitVal);
this.Reset();
}
@ -210,6 +212,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
public int CountDown { get; set; }
/// <summary>
/// Gets the scratch buffer.
/// </summary>
public byte[] Scratch { get; }
public Vp8MacroBlockInfo CurrentMacroBlockInfo => this.Mb[this.currentMbIdx];
private Vp8MacroBlockInfo[] Mb { get; }
@ -459,14 +466,39 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int x = this.I4 & 3;
int y = this.I4 >> 2;
var res = new Vp8Residual();
int R = 0;
int ctx;
int r = 0;
res.Init(0, 3, proba);
ctx = this.TopNz[x] + this.LeftNz[y];
int ctx = this.TopNz[x] + this.LeftNz[y];
res.SetCoeffs(levels);
R += res.GetResidualCost(ctx);
return R;
r += res.GetResidualCost(ctx);
return r;
}
public int GetCostUv(Vp8ModeScore rd, Vp8EncProba proba)
{
var res = new Vp8Residual();
int r = 0;
// re-import the non-zero context.
this.NzToBytes();
res.Init(0, 2, proba);
for (int ch = 0; ch <= 2; ch += 2)
{
for (int y = 0; y < 2; ++y)
{
for (int x = 0; x < 2; ++x)
{
int ctx = this.TopNz[4 + ch + x] + this.LeftNz[4 + ch + y];
res.SetCoeffs(rd.UvLevels.AsSpan((ch * 2) + x + (y * 2)));
r += res.GetResidualCost(ctx);
this.TopNz[4 + ch + x] = this.LeftNz[4 + ch + y] = (res.Last >= 0) ? 1 : 0;
}
}
}
return r;
}
public void SetIntraUvMode(int mode) => this.CurrentMacroBlockInfo.UvMode = mode;

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Formats.WebP.Lossy;
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
@ -45,23 +46,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
this.LevelCost = new Vp8CostArray[WebpConstants.NumTypes][];
this.LevelCost = new Vp8Costs[WebpConstants.NumTypes][];
for (int i = 0; i < this.LevelCost.Length; i++)
{
this.LevelCost[i] = new Vp8CostArray[WebpConstants.NumBands];
this.LevelCost[i] = new Vp8Costs[WebpConstants.NumBands];
for (int j = 0; j < this.LevelCost[i].Length; j++)
{
this.LevelCost[i][j] = new Vp8CostArray();
this.LevelCost[i][j] = new Vp8Costs();
}
}
this.RemappedCosts = new Vp8CostArray[WebpConstants.NumTypes][];
this.RemappedCosts = new Vp8Costs[WebpConstants.NumTypes][];
for (int i = 0; i < this.RemappedCosts.Length; i++)
{
this.RemappedCosts[i] = new Vp8CostArray[16];
this.RemappedCosts[i] = new Vp8Costs[16];
for (int j = 0; j < this.RemappedCosts[i].Length; j++)
{
this.RemappedCosts[i][j] = new Vp8CostArray();
this.RemappedCosts[i][j] = new Vp8Costs();
}
}
@ -102,9 +103,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public Vp8Stats[][] Stats { get; }
public Vp8CostArray[][] LevelCost { get; }
public Vp8Costs[][] LevelCost { get; }
public Vp8CostArray[][] RemappedCosts { get; }
public Vp8Costs[][] RemappedCosts { get; }
/// <summary>
/// Gets or sets the number of skipped blocks.
@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
if (!this.Dirty)
{
return; // nothing to do.
return; // Nothing to do.
}
for (int ctype = 0; ctype < WebpConstants.NumTypes; ++ctype)
@ -130,17 +131,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int ctx = 0; ctx < WebpConstants.NumCtx; ++ctx)
{
Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx];
Span<ushort> table = this.LevelCost[ctype][band].Costs.AsSpan(ctx * MaxVariableLevel);
Vp8CostArray table = this.LevelCost[ctype][band].Costs[ctx];
int cost0 = (ctx > 0) ? LossyUtils.Vp8BitCost(1, p.Probabilities[0]) : 0;
int costBase = LossyUtils.Vp8BitCost(1, p.Probabilities[1]) + cost0;
int v;
table[0] = (ushort)(LossyUtils.Vp8BitCost(0, p.Probabilities[1]) + cost0);
table.Costs[0] = (ushort)(LossyUtils.Vp8BitCost(0, p.Probabilities[1]) + cost0);
for (v = 1; v <= MaxVariableLevel; ++v)
{
table[v] = (ushort)(costBase + VariableLevelCost(v, p.Probabilities));
table.Costs[v] = (ushort)(costBase + VariableLevelCost(v, p.Probabilities));
}
// Starting at level 67 and up, the variable part of the cost is actually constant.
// Starting at level 67 and up, the variable part of the cost is actually constant
}
}
@ -148,9 +149,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
for (int ctx = 0; ctx < WebpConstants.NumCtx; ++ctx)
{
Span<ushort> dst = this.RemappedCosts[ctype][n].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
Span<ushort> src = this.LevelCost[ctype][WebpConstants.Vp8EncBands[n]].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
src.CopyTo(dst);
Vp8CostArray dst = this.RemappedCosts[ctype][n].Costs[ctx];
Vp8CostArray src = this.LevelCost[ctype][WebpConstants.Vp8EncBands[n]].Costs[ctx];
src.Costs.CopyTo(dst.Costs.AsSpan());
}
}
}
@ -170,7 +171,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
for (int p = 0; p < WebpConstants.NumProbas; ++p)
{
var stats = this.Stats[t][b].Stats[c].Stats[p];
uint stats = this.Stats[t][b].Stats[c].Stats[p];
int nb = (int)((stats >> 0) & 0xffff);
int total = (int)((stats >> 16) & 0xffff);
int updateProba = WebpLookupTables.CoeffsUpdateProba[t, b, c, p];
@ -234,10 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private static int CalcSkipProba(long nb, long total)
{
return (int)(total != 0 ? (total - nb) * 255 / total : 255);
}
private static int CalcSkipProba(long nb, long total) => (int)(total != 0 ? (total - nb) * 255 / total : 255);
private static int VariableLevelCost(int level, Span<byte> probas)
{
@ -260,15 +258,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Collect statistics and deduce probabilities for next coding pass.
// Return the total bit-cost for coding the probability updates.
private static int CalcTokenProba(int nb, int total)
{
return nb != 0 ? (255 - (nb * 255 / total)) : 255;
}
private static int CalcTokenProba(int nb, int total) => nb != 0 ? (255 - (nb * 255 / total)) : 255;
// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
private static int BranchCost(int nb, int total, int proba)
{
return (nb * LossyUtils.Vp8BitCost(1, (byte)proba)) + ((total - nb) * LossyUtils.Vp8BitCost(0, (byte)proba));
}
private static int BranchCost(int nb, int total, int proba) => (nb * LossyUtils.Vp8BitCost(1, (byte)proba)) + ((total - nb) * LossyUtils.Vp8BitCost(0, (byte)proba));
}
}

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

@ -63,6 +63,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private readonly byte[] averageBytesPerMb = { 50, 24, 16, 9, 7, 5, 3, 2 };
private readonly ushort[] weightY = { 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 };
private const int NumMbSegments = 4;
private const int NumBModes = 10;
@ -268,8 +270,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
private int MbHeaderLimit { get; }
private readonly ushort[] weightY = { 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 };
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
@ -452,7 +452,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
it.SaveBoundary();
}
while (it.Next());
while (it.Next() && --nbMbs > 0);
sizeP0 += this.SegmentHeader.Size;
if (stats.DoSizeSearch)
@ -706,13 +706,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Initialize segments' filtering
this.SetupFilterStrength();
this.SetupMatrices(dqm);
this.SetupMatrices(dqm, snsStrength);
}
private void SetupFilterStrength()
{
int filterSharpness = 0; // TODO: filterSharpness is hardcoded
var filterType = 1; // TODO: filterType is hardcoded
int filterType = 1; // TODO: filterType is hardcoded
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
int level0 = 5 * FilterStrength;
@ -787,8 +787,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
proba.NbSkip = 0;
}
private void SetupMatrices(Vp8SegmentInfo[] dqm)
private void SetupMatrices(Vp8SegmentInfo[] dqm, int snsStrength)
{
int tlambdaScale = (this.method >= 4) ? snsStrength : 0;
for (int i = 0; i < dqm.Length; ++i)
{
Vp8SegmentInfo m = dqm[i];
@ -808,8 +809,26 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
m.Uv.Q[1] = WebpLookupTables.AcTable[Clip(q + this.DqUvAc, 0, 127)];
int qi4 = m.Y1.Expand(0);
m.Y2.Expand(1); // qi16
m.Uv.Expand(2); // quv
int qi16 = m.Y2.Expand(1);
int quv = m.Uv.Expand(2);
m.I4Penalty = 1000 * qi4 * qi4;
m.LambdaI16 = 3 * qi16 * qi16;
m.LambdaI4 = (3 * qi4 * qi4) >> 7;
m.LambdaUv = (3 * quv * quv) >> 6;
m.LambdaMode = (1 * qi4 * qi4) >> 7;
m.TLambda = (tlambdaScale * qi4) >> 5;
// none of these constants should be < 1.
m.LambdaI16 = m.LambdaI16 < 1 ? 1 : m.LambdaI16;
m.LambdaI4 = m.LambdaI4 < 1 ? 1 : m.LambdaI4;
m.LambdaUv = m.LambdaUv < 1 ? 1 : m.LambdaUv;
m.LambdaMode = m.LambdaMode < 1 ? 1 : m.LambdaMode;
m.TLambda = m.TLambda < 1 ? 1 : m.TLambda;
m.MinDisto = 20 * m.Y1.Q[0];
m.MaxEdge = 0;
m.I4Penalty = 1000 * qi4 * qi4;
}
@ -864,15 +883,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
it.MakeLuma16Preds();
it.MakeChroma8Preds();
if (rdOpt > Vp8RdLevel.RdOptNone)
// TODO: disabled picking best mode because its still bugged.
// if (rdOpt > Vp8RdLevel.RdOptNone)
if (false)
{
this.PickBestIntra16(it, rd);
this.PickBestIntra16(it, ref rd);
if (this.method >= 2)
{
this.PickBestIntra4(it, rd);
this.PickBestIntra4(it, ref rd);
}
this.PickBestUv(it, rd);
this.PickBestUv(it, ref rd);
}
else
{
@ -889,7 +910,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return isSkipped;
}
private void PickBestIntra16(Vp8EncIterator it, Vp8ModeScore rd)
private void PickBestIntra16(Vp8EncIterator it, ref Vp8ModeScore rd)
{
const int numBlocks = 16;
Vp8SegmentInfo dqm = this.SegmentInfos[it.CurrentMacroBlockInfo.Segment];
@ -913,7 +934,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Measure RD-score.
rdCur.D = Vp8Sse16X16(src, tmpDst);
rdCur.SD = tlambda != 0 ? Mult8B(tlambda, Vp8Disto16x16(src, tmpDst, this.weightY)) : 0;
rdCur.SD = tlambda != 0 ? Mult8B(tlambda, Vp8Disto16X16(src, tmpDst, this.weightY)) : 0;
rdCur.H = WebpConstants.Vp8FixedCostsI16[mode];
rdCur.R = this.GetCostLuma16(it, rdCur);
@ -959,16 +980,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private bool PickBestIntra4(Vp8EncIterator it, Vp8ModeScore rd)
private bool PickBestIntra4(Vp8EncIterator it, ref Vp8ModeScore rd)
{
Vp8SegmentInfo dqm = this.SegmentInfos[it.CurrentMacroBlockInfo.Segment];
int lambda = dqm.LambdaI16;
int lambda = dqm.LambdaI4;
int tlambda = dqm.TLambda;
Span<byte> src0 = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
Span<byte> bestBlocks = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc);
int totalHeaderBits = 0;
var rdBest = new Vp8ModeScore();
IMemoryOwner<byte> scratchBuffer = this.memoryAllocator.Allocate<byte>(512);
if (this.maxI4HeaderBits == 0)
{
@ -988,7 +1008,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> src = src0.Slice(WebpLookupTables.Vp8Scan[it.I4]);
short[] modeCosts = it.GetCostModeI4(rd.ModesI4);
Span<byte> bestBlock = bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]);
Span<byte> tmpDst = scratchBuffer.GetSpan();
Span<byte> tmpDst = it.Scratch.AsSpan();
tmpDst.Fill(0);
rdi4.InitScore();
it.MakeIntra4Preds();
@ -1002,7 +1023,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Compute RD-score.
rdTmp.D = Vp8Sse4X4(src, tmpDst);
rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, Vp8Disto4x4(src, tmpDst, this.weightY)) : 0;
rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, Vp8Disto4X4(src, tmpDst, this.weightY)) : 0;
rdTmp.H = modeCosts[mode];
// Add flatness penalty, to avoid flat area to be mispredicted by a complex mode.
@ -1033,11 +1054,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> tmp = tmpDst;
tmpDst = bestBlock;
bestBlock = tmp;
tmpLevels.AsSpan(0, rdBest.YAcLevels[it.I4]).CopyTo(rdBest.YAcLevels);
tmpLevels.AsSpan().CopyTo(rdBest.YAcLevels);
}
}
rdi4.SetRdScore(lambda);
rdi4.SetRdScore(dqm.LambdaMode);
rdBest.AddScore(rdi4);
if (rdBest.Score >= rd.Score)
{
@ -1050,11 +1071,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return false;
}
// Copy selected samples if not in the right place already.
if (bestBlock != bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]))
{
Vp8Copy4x4(bestBlock, bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]));
}
// Copy selected samples to the right place.
Vp8Copy4X4(bestBlock, bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]));
rd.ModesI4[it.I4] = (byte)bestMode;
it.TopNz[it.I4 & 3] = it.LeftNz[it.I4 >> 2] = rdi4.Nz != 0 ? 1 : 0;
@ -1071,9 +1089,56 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return true;
}
private void PickBestUv(Vp8EncIterator it, Vp8ModeScore rd)
private void PickBestUv(Vp8EncIterator it, ref Vp8ModeScore rd)
{
const int numBlocks = 8;
Vp8SegmentInfo dqm = this.SegmentInfos[it.CurrentMacroBlockInfo.Segment];
int lambda = dqm.LambdaUv;
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc);
Span<byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.UOffEnc);
Span<byte> dst0 = it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc);
Span<byte> dst = dst0;
var rdBest = new Vp8ModeScore();
int mode;
rd.ModeUv = -1;
rdBest.InitScore();
for (mode = 0; mode < NumPredModes; ++mode)
{
var rdUv = new Vp8ModeScore();
// Reconstruct
rdUv.Nz = (uint)this.ReconstructUv(it, dqm, rdUv, tmpDst, mode);
// Compute RD-score
rdUv.D = Vp8Sse16X8(src, tmpDst);
rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas.
rdUv.H = WebpConstants.Vp8FixedCostsUv[mode];
rdUv.R = it.GetCostUv(rdUv, this.Proba);
if (mode > 0 && IsFlat(rdUv.UvLevels, numBlocks, WebpConstants.FlatnessLimitIUv))
{
rdUv.R += WebpConstants.FlatnessPenality * numBlocks;
}
rdUv.SetRdScore(lambda);
if (mode == 0 || rdUv.Score < rdBest.Score)
{
rdBest.CopyScore(rdUv);
rd.ModeUv = mode;
rdUv.UvLevels.CopyTo(rd.UvLevels.AsSpan());
Span<byte> tmp = dst;
dst = tmpDst;
tmpDst = tmp;
}
}
it.SetIntraUvMode(rd.ModeUv);
rd.AddScore(rdBest);
if (dst != dst0)
{
// copy 16x8 block if needed.
Vp8Copy16X8(dst, dst0);
}
}
// TODO: move to Vp8EncIterator
@ -1358,7 +1423,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
int ctx = it.TopNz[4 + ch + x] + it.LeftNz[4 + ch + y];
residual.SetCoeffs(rd.UvLevels.AsSpan(16 * ((ch * 2) + x + (y * 2)), 16));
var res = residual.RecordCoeffs(ctx);
int res = residual.RecordCoeffs(ctx);
it.TopNz[4 + ch + x] = res;
it.LeftNz[4 + ch + y] = res;
}
@ -1552,7 +1617,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void Vp8Copy4x4(Span<byte> src, Span<byte> dst) => Copy(src, dst, 4, 4);
private static void Vp8Copy4X4(Span<byte> src, Span<byte> dst) => Copy(src, dst, 4, 4);
[MethodImpl(InliningOptions.ShortMethod)]
private static void Vp8Copy16X8(Span<byte> src, Span<byte> dst) => Copy(src, dst, 16, 8);
[MethodImpl(InliningOptions.ShortMethod)]
private static void Copy(Span<byte> src, Span<byte> dst, int w, int h)
@ -1566,23 +1634,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8Disto16x16(Span<byte> a, Span<byte> b, Span<ushort> w)
private static int Vp8Disto16X16(Span<byte> a, Span<byte> b, Span<ushort> w)
{
int D = 0;
int x, y;
for (y = 0; y < 16 * WebpConstants.Bps; y += 4 * WebpConstants.Bps)
int d = 0;
for (int y = 0; y < 16 * WebpConstants.Bps; y += 4 * WebpConstants.Bps)
{
for (x = 0; x < 16; x += 4)
for (int x = 0; x < 16; x += 4)
{
D += Vp8Disto4x4(a.Slice(x + y), b.Slice(x + y), w);
d += Vp8Disto4X4(a.Slice(x + y), b.Slice(x + y), w);
}
}
return D;
return d;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8Disto4x4(Span<byte> a, Span<byte> b, Span<ushort> w)
private static int Vp8Disto4X4(Span<byte> a, Span<byte> b, Span<ushort> w)
{
int sum1 = LossyUtils.TTransform(a, w);
int sum2 = LossyUtils.TTransform(b, w);
@ -1605,7 +1672,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
levels = levels.Slice(16, 16);
levels = levels.Slice(16);
}
return true;

5
src/ImageSharp/Formats/WebP/Lossy/Vp8ProbaArray.cs

@ -11,10 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary>
/// Initializes a new instance of the <see cref="Vp8ProbaArray"/> class.
/// </summary>
public Vp8ProbaArray()
{
this.Probabilities = new byte[WebpConstants.NumProbas];
}
public Vp8ProbaArray() => this.Probabilities = new byte[WebpConstants.NumProbas];
/// <summary>
/// Gets the probabilities.

22
src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Formats.WebP.Lossy;
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
@ -22,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public Vp8Stats[] Stats { get; set; }
public ushort[] Costs { get; set; }
public Vp8Costs[] Costs { get; set; }
public void Init(int first, int coeffType, Vp8EncProba prob)
{
@ -30,10 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.CoeffType = coeffType;
this.Prob = prob.Coeffs[this.CoeffType];
this.Stats = prob.Stats[this.CoeffType];
this.Costs = new ushort[WebpConstants.NumCtx * (WebpConstants.MaxVariableLevel + 1)];
// TODO:
// res->costs = enc->proba_.remapped_costs_[coeff_type];
this.Costs = prob.RemappedCosts[this.CoeffType];
}
public void SetCoeffs(Span<short> coeffs)
@ -117,8 +115,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
int n = this.First;
int p0 = this.Prob[n].Probabilities[ctx0].Probabilities[0];
ushort[] costs = this.Costs;
Span<ushort> t = costs.AsSpan(n * ctx0);
Vp8Costs[] costs = this.Costs;
Vp8CostArray t = costs[n].Costs[ctx0];
// bitCost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
// (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
@ -135,19 +133,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
v = Math.Abs(this.Coeffs[n]);
int ctx = (v >= 2) ? 2 : v;
cost += LevelCost(t, v);
t[0] = costs[(n + 1) * ctx];
cost += LevelCost(t.Costs, v);
t = costs[n + 1].Costs[ctx];
}
// Last coefficient is always non-zero
v = Math.Abs(this.Coeffs[n]);
cost += LevelCost(t, v);
cost += LevelCost(t.Costs, v);
if (n < 15)
{
int b = WebpConstants.Vp8EncBands[n + 1];
int ctx = (v == 1) ? 1 : 2;
int last_p0 = this.Prob[b].Probabilities[ctx].Probabilities[0];
cost += LossyUtils.Vp8BitCost(0, (byte)last_p0);
int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0];
cost += LossyUtils.Vp8BitCost(0, (byte)lastP0);
}
return cost;

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

@ -59,8 +59,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int LambdaI16 { get; set; }
public int LambdaI4 { get; set; }
public int TLambda { get; set; }
public int LambdaUv { get; set; }
public int LambdaMode { get; set; }
public void StoreMaxDelta(Span<short> dcs)

Loading…
Cancel
Save