Browse Source

Fix issue with encoding Car.bmp

pull/1552/head
Brian Popow 6 years ago
parent
commit
90006635e8
  1. 34
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  2. 17
      src/ImageSharp/Formats/WebP/Lossless/CostManager.cs
  3. 2
      src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs
  4. 41
      src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs
  5. 18
      src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs
  6. 21
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  7. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
  8. 10
      src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs
  9. 4
      src/ImageSharp/Formats/WebP/WebPConstants.cs

34
src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs

@ -25,11 +25,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private const int WindowOffsetsSizeMax = 32; private const int WindowOffsetsSizeMax = 32;
/// <summary>
/// Minimum block size for backward references.
/// </summary>
private const int MinBlockSize = 256;
/// <summary> /// <summary>
/// The number of bits for the window size. /// The number of bits for the window size.
/// </summary> /// </summary>
@ -135,7 +130,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
uint bestBgra; uint bestBgra;
int minPos = (basePosition > windowSize) ? basePosition - windowSize : 0; int minPos = (basePosition > windowSize) ? basePosition - windowSize : 0;
int lengthMax = (maxLen < 256) ? maxLen : 256; int lengthMax = (maxLen < 256) ? maxLen : 256;
uint maxBasePosition;
pos = (int)chain[basePosition]; pos = (int)chain[basePosition];
int currLength; int currLength;
@ -193,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// We have the best match but in case the two intervals continue matching // We have the best match but in case the two intervals continue matching
// to the left, we have the best matches for the left-extended pixels. // to the left, we have the best matches for the left-extended pixels.
maxBasePosition = (uint)basePosition; var maxBasePosition = (uint)basePosition;
while (true) while (true)
{ {
p.OffsetLength[basePosition] = (bestDistance << MaxLengthBits) | (uint)bestLength; p.OffsetLength[basePosition] = (bestDistance << MaxLengthBits) | (uint)bestLength;
@ -432,15 +426,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private static void BackwardReferencesTraceBackwards(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst) private static void BackwardReferencesTraceBackwards(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst)
{ {
int distArraySize = xSize * ySize; int distArraySize = xSize * ySize;
var distArray = new short[distArraySize]; var distArray = new ushort[distArraySize];
BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray); BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray);
int chosenPathSize = TraceBackwards(distArray, distArraySize); int chosenPathSize = TraceBackwards(distArray, distArraySize);
Span<short> chosenPath = distArray.AsSpan(distArraySize - chosenPathSize); Span<ushort> chosenPath = distArray.AsSpan(distArraySize - chosenPathSize);
BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, chosenPathSize, hashChain, refsDst); BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, chosenPathSize, hashChain, refsDst);
} }
private static void BackwardReferencesHashChainDistanceOnly(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, short[] distArray) private static void BackwardReferencesHashChainDistanceOnly(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, ushort[] distArray)
{ {
int pixCount = xSize * ySize; int pixCount = xSize * ySize;
bool useColorCache = cacheBits > 0; bool useColorCache = cacheBits > 0;
@ -502,17 +496,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (i + len - 1 > reach) if (i + len - 1 > reach)
{ {
int offsetJ = 0;
int lenJ = 0; int lenJ = 0;
int j; int j;
for (j = i; j <= reach; ++j) for (j = i; j <= reach; ++j)
{ {
offset = hashChain.FindOffset(j + 1); int offsetJ = hashChain.FindOffset(j + 1);
len = hashChain.FindLength(j + 1); lenJ = hashChain.FindLength(j + 1);
if (offsetJ != offset) if (offsetJ != offset)
{ {
offset = hashChain.FindOffset(j); lenJ = hashChain.FindLength(j);
len = hashChain.FindLength(j);
break; break;
} }
} }
@ -533,14 +525,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
private static int TraceBackwards(short[] distArray, int distArraySize) private static int TraceBackwards(ushort[] distArray, int distArraySize)
{ {
int chosenPathSize = 0; int chosenPathSize = 0;
int pathPos = distArraySize; int pathPos = distArraySize;
int curPos = distArraySize - 1; int curPos = distArraySize - 1;
while (curPos >= 0) while (curPos >= 0)
{ {
short cur = distArray[curPos]; ushort cur = distArray[curPos];
pathPos--; pathPos--;
chosenPathSize++; chosenPathSize++;
distArray[pathPos] = cur; distArray[pathPos] = cur;
@ -550,7 +542,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return chosenPathSize; return chosenPathSize;
} }
private static void BackwardReferencesHashChainFollowChosenPath(Span<uint> bgra, int cacheBits, Span<short> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) private static void BackwardReferencesHashChainFollowChosenPath(Span<uint> bgra, int cacheBits, Span<ushort> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs)
{ {
bool useColorCache = cacheBits > 0; bool useColorCache = cacheBits > 0;
var colorCache = new ColorCache(); var colorCache = new ColorCache();
@ -606,7 +598,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
private static void AddSingleLiteralWithCostModel(Span<uint> bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, float[] cost, short[] distArray) private static void AddSingleLiteralWithCostModel(Span<uint> bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, float[] cost, ushort[] distArray)
{ {
double costVal = prevCost; double costVal = prevCost;
uint color = bgra[idx]; uint color = bgra[idx];
@ -965,7 +957,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (ix >= 0) if (ix >= 0)
{ {
// Color cache contains bgraLiteral // Color cache contains bgraLiteral
v = PixOrCopy.CreateCacheIdx(ix); PixOrCopy.CreateCacheIdx(ix);
} }
else else
{ {
@ -983,8 +975,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
} }
// TODO: VP8LColorCacheClear(colorCache)?
} }
private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs) private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs)

17
src/ImageSharp/Formats/WebP/Lossless/CostManager.cs

@ -14,11 +14,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{ {
private CostInterval head; private CostInterval head;
public CostManager(short[] distArray, int pixCount, CostModel costModel) public CostManager(ushort[] distArray, int pixCount, CostModel costModel)
{ {
int costCacheSize = (pixCount > BackwardReferenceEncoder.MaxLength) ? BackwardReferenceEncoder.MaxLength : pixCount; int costCacheSize = (pixCount > BackwardReferenceEncoder.MaxLength) ? BackwardReferenceEncoder.MaxLength : pixCount;
this.Intervals = new List<CostInterval>();
this.CacheIntervals = new List<CostCacheInterval>(); this.CacheIntervals = new List<CostCacheInterval>();
this.CostCache = new List<double>(); this.CostCache = new List<double>();
this.Costs = new float[pixCount]; this.Costs = new float[pixCount];
@ -85,9 +84,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
public float[] Costs { get; } public float[] Costs { get; }
public short[] DistArray { get; } public ushort[] DistArray { get; }
public List<CostInterval> Intervals { get; }
public List<CostCacheInterval> CacheIntervals { get; } public List<CostCacheInterval> CacheIntervals { get; }
@ -99,7 +96,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
public void UpdateCostAtIndex(int i, bool doCleanIntervals) public void UpdateCostAtIndex(int i, bool doCleanIntervals)
{ {
CostInterval current = this.head; CostInterval current = this.head;
using List<CostInterval>.Enumerator intervalEnumerator = this.Intervals.GetEnumerator();
while (current != null && current.Start <= i) while (current != null && current.Start <= i)
{ {
CostInterval next = current.Next; CostInterval next = current.Next;
@ -142,7 +138,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (this.Costs[j] > costTmp) if (this.Costs[j] > costTmp)
{ {
this.Costs[j] = costTmp; this.Costs[j] = costTmp;
this.DistArray[j] = (short)(k + 1); this.DistArray[j] = (ushort)(k + 1);
} }
} }
@ -150,14 +146,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
CostInterval interval = this.head; CostInterval interval = this.head;
for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; i++) for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; ++i)
{ {
// Define the intersection of the ith interval with the new one. // Define the intersection of the ith interval with the new one.
int start = position + this.CacheIntervals[i].Start; int start = position + this.CacheIntervals[i].Start;
int end = position + (this.CacheIntervals[i].End > len ? len : this.CacheIntervals[i].End); int end = position + (this.CacheIntervals[i].End > len ? len : this.CacheIntervals[i].End);
float cost = (float)(distanceCost + this.CacheIntervals[i].Cost); float cost = (float)(distanceCost + this.CacheIntervals[i].Cost);
var idx = i;
CostInterval intervalNext; CostInterval intervalNext;
for (; interval != null && interval.Start < end; interval = intervalNext) for (; interval != null && interval.Start < end; interval = intervalNext)
{ {
@ -203,7 +198,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// We have to split the old interval as it fully contains the new one. // We have to split the old interval as it fully contains the new one.
int endOriginal = interval.End; int endOriginal = interval.End;
interval.End = start; interval.End = start;
this.InsertInterval(interval, interval.Cost, idx, end, endOriginal); this.InsertInterval(interval, interval.Cost, interval.Index, end, endOriginal);
break; break;
} }
else else
@ -317,7 +312,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (this.Costs[i] > cost) if (this.Costs[i] > cost)
{ {
this.Costs[i] = cost; this.Costs[i] = cost;
this.DistArray[i] = (short)(k + 1); this.DistArray[i] = (ushort)(k + 1);
} }
} }
} }

2
src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs

@ -13,6 +13,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// <summary> /// <summary>
/// Number of combine failures per binId. /// Number of combine failures per binId.
/// </summary> /// </summary>
public short NumCombineFailures; public ushort NumCombineFailures;
} }
} }

41
src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs

@ -26,17 +26,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private const uint NonTrivialSym = 0xffffffff; private const uint NonTrivialSym = 0xffffffff;
private const short InvalidHistogramSymbol = short.MaxValue; private const ushort InvalidHistogramSymbol = ushort.MaxValue;
public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List<Vp8LHistogram> imageHisto, Vp8LHistogram tmpHisto, short[] histogramSymbols) public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List<Vp8LHistogram> imageHisto, Vp8LHistogram tmpHisto, ushort[] histogramSymbols)
{ {
int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(xSize, histoBits) : 1; int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(xSize, histoBits) : 1;
int histoYSize = histoBits > 0 ? LosslessUtils.SubSampleSize(ySize, histoBits) : 1; int histoYSize = histoBits > 0 ? LosslessUtils.SubSampleSize(ySize, histoBits) : 1;
int imageHistoRawSize = histoXSize * histoYSize; int imageHistoRawSize = histoXSize * histoYSize;
int entropyCombineNumBins = BinSize; int entropyCombineNumBins = BinSize;
var mapTmp = new short[imageHistoRawSize]; var mapTmp = new ushort[imageHistoRawSize];
var clusterMappings = new short[imageHistoRawSize]; var clusterMappings = new ushort[imageHistoRawSize];
int numUsed = imageHistoRawSize;
var origHisto = new List<Vp8LHistogram>(imageHistoRawSize); var origHisto = new List<Vp8LHistogram>(imageHistoRawSize);
for (int i = 0; i < imageHistoRawSize; i++) for (int i = 0; i < imageHistoRawSize; i++)
{ {
@ -47,13 +46,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
HistogramBuild(xSize, histoBits, refs, origHisto); HistogramBuild(xSize, histoBits, refs, origHisto);
// Copies the histograms and computes its bitCost. histogramSymbols is optimized. // Copies the histograms and computes its bitCost. histogramSymbols is optimized.
HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols); int numUsed = HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols);
numUsed = imageHisto.Count(h => h != null);
var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100); var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100);
if (entropyCombine) if (entropyCombine)
{ {
short[] binMap = mapTmp; ushort[] binMap = mapTmp;
var numClusters = numUsed; var numClusters = numUsed;
double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality); double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality);
HistogramAnalyzeEntropyBin(imageHisto, binMap); HistogramAnalyzeEntropyBin(imageHisto, binMap);
@ -128,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// Partition histograms to different entropy bins for three dominant (literal, /// Partition histograms to different entropy bins for three dominant (literal,
/// red and blue) symbol costs and compute the histogram aggregate bitCost. /// red and blue) symbol costs and compute the histogram aggregate bitCost.
/// </summary> /// </summary>
private static void HistogramAnalyzeEntropyBin(List<Vp8LHistogram> histograms, short[] binMap) private static void HistogramAnalyzeEntropyBin(List<Vp8LHistogram> histograms, ushort[] binMap)
{ {
int histoSize = histograms.Count; int histoSize = histograms.Count;
var costRange = new DominantCostRange(); var costRange = new DominantCostRange();
@ -153,13 +151,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
continue; continue;
} }
binMap[i] = (short)costRange.GetHistoBinIndex(histograms[i], NumPartitions); binMap[i] = (ushort)costRange.GetHistoBinIndex(histograms[i], NumPartitions);
} }
} }
private static void HistogramCopyAndAnalyze(List<Vp8LHistogram> origHistograms, List<Vp8LHistogram> histograms, short[] histogramSymbols) private static int HistogramCopyAndAnalyze(List<Vp8LHistogram> origHistograms, List<Vp8LHistogram> histograms, ushort[] histogramSymbols)
{ {
for (int clusterId = 0, i = 0; i < origHistograms.Count; i++) for (int clusterId = 0, i = 0; i < origHistograms.Count; ++i)
{ {
Vp8LHistogram origHistogram = origHistograms[i]; Vp8LHistogram origHistogram = origHistograms[i];
origHistogram.UpdateHistogramCost(); origHistogram.UpdateHistogramCost();
@ -174,12 +172,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
else else
{ {
histograms[i] = (Vp8LHistogram)origHistogram.DeepClone(); histograms[i] = (Vp8LHistogram)origHistogram.DeepClone();
histogramSymbols[i] = (short)clusterId++; histogramSymbols[i] = (ushort)clusterId++;
} }
} }
int numUsed = histogramSymbols.Count(h => h != InvalidHistogramSymbol);
return numUsed;
} }
private static void HistogramCombineEntropyBin(List<Vp8LHistogram> histograms, short[] clusters, short[] clusterMappings, Vp8LHistogram curCombo, short[] binMap, int numBins, double combineCostFactor) private static void HistogramCombineEntropyBin(List<Vp8LHistogram> histograms, ushort[] clusters, ushort[] clusterMappings, Vp8LHistogram curCombo, ushort[] binMap, int numBins, double combineCostFactor)
{ {
var binInfo = new HistogramBinInfo[BinSize]; var binInfo = new HistogramBinInfo[BinSize];
for (int idx = 0; idx < numBins; idx++) for (int idx = 0; idx < numBins; idx++)
@ -191,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// By default, a cluster matches itself. // By default, a cluster matches itself.
for (int idx = 0; idx < histograms.Count; idx++) for (int idx = 0; idx < histograms.Count; idx++)
{ {
clusterMappings[idx] = (short)idx; clusterMappings[idx] = (ushort)idx;
} }
var indicesToRemove = new List<int>(); var indicesToRemove = new List<int>();
@ -253,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// Given a Histogram set, the mapping of clusters 'clusterMapping' and the /// Given a Histogram set, the mapping of clusters 'clusterMapping' and the
/// current assignment of the cells in 'symbols', merge the clusters and assign the smallest possible clusters values. /// current assignment of the cells in 'symbols', merge the clusters and assign the smallest possible clusters values.
/// </summary> /// </summary>
private static void OptimizeHistogramSymbols(short[] clusterMappings, int numClusters, short[] clusterMappingsTmp, short[] symbols) private static void OptimizeHistogramSymbols(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, ushort[] symbols)
{ {
bool doContinue = true; bool doContinue = true;
@ -274,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (k != clusterMappings[i]) if (k != clusterMappings[i])
{ {
doContinue = true; doContinue = true;
clusterMappings[i] = (short)k; clusterMappings[i] = (ushort)k;
} }
} }
} }
@ -295,7 +296,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (cluster > 0 && clusterMappingsTmp[cluster] == 0) if (cluster > 0 && clusterMappingsTmp[cluster] == 0)
{ {
clusterMax++; clusterMax++;
clusterMappingsTmp[cluster] = (short)clusterMax; clusterMappingsTmp[cluster] = (ushort)clusterMax;
} }
symbols[i] = clusterMappingsTmp[cluster]; symbols[i] = clusterMappingsTmp[cluster];
@ -522,7 +523,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
private static void HistogramRemap(List<Vp8LHistogram> input, List<Vp8LHistogram> output, short[] symbols) private static void HistogramRemap(List<Vp8LHistogram> input, List<Vp8LHistogram> output, ushort[] symbols)
{ {
int inSize = input.Count; int inSize = input.Count;
int outSize = output.Count; int outSize = output.Count;
@ -549,7 +550,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
symbols[i] = (short)bestOut; symbols[i] = (ushort)bestOut;
} }
} }
else else

18
src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
public static void OptimizeHuffmanForRle(int length, bool[] goodForRle, uint[] counts) public static void OptimizeHuffmanForRle(int length, bool[] goodForRle, uint[] counts)
{ {
// 1) Let's make the Huffman code more compatible with rle encoding. // 1) Let's make the Huffman code more compatible with rle encoding.
for (; length >= 0; length--) for (; length >= 0; --length)
{ {
if (length == 0) if (length == 0)
{ {
@ -63,13 +63,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Mark any seq of non-0's that is longer as 7 as a goodForRle. // Mark any seq of non-0's that is longer as 7 as a goodForRle.
uint symbol = counts[0]; uint symbol = counts[0];
int stride = 0; int stride = 0;
for (int i = 0; i < length + 1; i++) for (int i = 0; i < length + 1; ++i)
{ {
if (i == length || counts[i] != symbol) if (i == length || counts[i] != symbol)
{ {
if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7)) if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7))
{ {
for (int k = 0; k < stride; k++) for (int k = 0; k < stride; ++k)
{ {
goodForRle[i - k - 1] = true; goodForRle[i - k - 1] = true;
} }
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
else else
{ {
stride++; ++stride;
} }
} }
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
stride = 0; stride = 0;
uint limit = counts[0]; uint limit = counts[0];
uint sum = 0; uint sum = 0;
for (int i = 0; i < length; i++) for (int i = 0; i < length + 1; ++i)
{ {
var valuesShouldBeCollapsedToStrideAverage = ValuesShouldBeCollapsedToStrideAverage((int)counts[i], (int)limit); var valuesShouldBeCollapsedToStrideAverage = ValuesShouldBeCollapsedToStrideAverage((int)counts[i], (int)limit);
if (i == length || goodForRle[i] || (i != 0 && goodForRle[i - 1]) || !valuesShouldBeCollapsedToStrideAverage) if (i == length || goodForRle[i] || (i != 0 && goodForRle[i - 1]) || !valuesShouldBeCollapsedToStrideAverage)
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
count = 0; count = 0;
} }
for (k = 0; k < stride; k++) for (k = 0; k < stride; ++k)
{ {
// We don't want to change value at counts[i], // We don't want to change value at counts[i],
// that is already belonging to the next stride. Thus - 1. // that is already belonging to the next stride. Thus - 1.
@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
stride++; ++stride;
if (i != length) if (i != length)
{ {
sum += counts[i]; sum += counts[i];
@ -165,11 +165,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
uint countMin; uint countMin;
int treeSizeOrig = 0; int treeSizeOrig = 0;
for (int i = 0; i < histogramSize; i++) for (int i = 0; i < histogramSize; ++i)
{ {
if (histogram[i] != 0) if (histogram[i] != 0)
{ {
treeSizeOrig++; ++treeSizeOrig;
} }
} }

21
src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

@ -401,7 +401,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, int quality, bool useCache, CrunchConfig config, int cacheBits, int histogramBits, int initBytePosition) private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, int quality, bool useCache, CrunchConfig config, int cacheBits, int histogramBits, int initBytePosition)
{ {
int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits); int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits);
var histogramSymbols = new short[histogramImageXySize]; var histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebPConstants.CodeLengthCodes]; var huffTree = new HuffmanTree[3 * WebPConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {
@ -412,6 +412,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{ {
if (cacheBits == 0) if (cacheBits == 0)
{ {
// TODO: not sure if this should be 10 or 11. Original code comment says "The maximum allowed limit is 11.", but the value itself is 10.
cacheBits = WebPConstants.MaxColorCacheBits; cacheBits = WebPConstants.MaxColorCacheBits;
} }
} }
@ -485,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
using IMemoryOwner<uint> histogramBgraBuffer = this.memoryAllocator.Allocate<uint>(histogramImageXySize); using IMemoryOwner<uint> histogramBgraBuffer = this.memoryAllocator.Allocate<uint>(histogramImageXySize);
Span<uint> histogramBgra = histogramBgraBuffer.GetSpan(); Span<uint> histogramBgra = histogramBgraBuffer.GetSpan();
int maxIndex = 0; int maxIndex = 0;
for (int i = 0; i < histogramImageXySize; i++) for (int i = 0; i < histogramImageXySize; ++i)
{ {
int symbolIndex = histogramSymbols[i] & 0xffff; int symbolIndex = histogramSymbols[i] & 0xffff;
histogramBgra[i] = (uint)(symbolIndex << 8); histogramBgra[i] = (uint)(symbolIndex << 8);
@ -502,7 +503,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Store Huffman codes. // Store Huffman codes.
// Find maximum number of symbols for the huffman tree-set. // Find maximum number of symbols for the huffman tree-set.
int maxTokens = 0; int maxTokens = 0;
for (int i = 0; i < 5 * histogramImage.Count; i++) for (int i = 0; i < 5 * histogramImage.Count; ++i)
{ {
HuffmanTreeCode codes = huffmanCodes[i]; HuffmanTreeCode codes = huffmanCodes[i];
if (maxTokens < codes.NumSymbols) if (maxTokens < codes.NumSymbols)
@ -517,7 +518,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
tokens[i] = new HuffmanTreeToken(); tokens[i] = new HuffmanTreeToken();
} }
for (int i = 0; i < 5 * histogramImage.Count; i++) for (int i = 0; i < 5 * histogramImage.Count; ++i)
{ {
HuffmanTreeCode codes = huffmanCodes[i]; HuffmanTreeCode codes = huffmanCodes[i];
this.StoreHuffmanCode(huffTree, tokens, codes); this.StoreHuffmanCode(huffTree, tokens, codes);
@ -531,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// TODO: Keep track of the smallest image so far. // TODO: Keep track of the smallest image so far.
if (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes()) if (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes())
{ {
// TODO : This was done in the reference by swapping references, this will be slower // TODO: This was done in the reference by swapping references, this will be slower
bitWriterBest = this.bitWriter.Clone(); bitWriterBest = this.bitWriter.Clone();
} }
} }
@ -606,7 +607,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality) private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality)
{ {
int cacheBits = 0; int cacheBits = 0;
var histogramSymbols = new short[1]; // Only one tree, one symbol. var histogramSymbols = new ushort[1]; // Only one tree, one symbol.
// TODO: Can HuffmanTreeCode be struct // TODO: Can HuffmanTreeCode be struct
var huffmanCodes = new HuffmanTreeCode[5]; var huffmanCodes = new HuffmanTreeCode[5];
@ -652,7 +653,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Find maximum number of symbols for the huffman tree-set. // Find maximum number of symbols for the huffman tree-set.
int maxTokens = 0; int maxTokens = 0;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; ++i)
{ {
HuffmanTreeCode codes = huffmanCodes[i]; HuffmanTreeCode codes = huffmanCodes[i];
if (maxTokens < codes.NumSymbols) if (maxTokens < codes.NumSymbols)
@ -662,13 +663,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
var tokens = new HuffmanTreeToken[maxTokens]; var tokens = new HuffmanTreeToken[maxTokens];
for (int i = 0; i < tokens.Length; i++) for (int i = 0; i < tokens.Length; ++i)
{ {
tokens[i] = new HuffmanTreeToken(); tokens[i] = new HuffmanTreeToken();
} }
// Store Huffman codes. // Store Huffman codes.
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; ++i)
{ {
HuffmanTreeCode codes = huffmanCodes[i]; HuffmanTreeCode codes = huffmanCodes[i];
this.StoreHuffmanCode(huffTree, tokens, codes); this.StoreHuffmanCode(huffTree, tokens, codes);
@ -849,7 +850,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, short[] histogramSymbols, HuffmanTreeCode[] huffmanCodes) private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, ushort[] histogramSymbols, HuffmanTreeCode[] huffmanCodes)
{ {
int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1; int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1;
int tileMask = (histoBits == 0) ? 0 : -(1 << histoBits); int tileMask = (histoBits == 0) ? 0 : -(1 << histoBits);

2
src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs

@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
this.Distance = new uint[WebPConstants.NumDistanceCodes]; this.Distance = new uint[WebPConstants.NumDistanceCodes];
var literalSize = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + (1 << WebPConstants.MaxColorCacheBits); var literalSize = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + (1 << WebPConstants.MaxColorCacheBits);
this.Literal = new uint[literalSize]; this.Literal = new uint[literalSize + 1];
// 5 for literal, red, blue, alpha, distance. // 5 for literal, red, blue, alpha, distance.
this.IsUsed = new bool[5]; this.IsUsed = new bool[5];

10
src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs

@ -145,7 +145,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (isColorCachePresent) if (isColorCachePresent)
{ {
colorCacheBits = (int)this.bitReader.ReadValue(4); colorCacheBits = (int)this.bitReader.ReadValue(4);
bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits;
// Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum.
// That is why 11 bits is also considered valid here.
bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= (WebPConstants.MaxColorCacheBits + 1);
if (!colorCacheBitsIsValid) if (!colorCacheBitsIsValid)
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
@ -162,11 +165,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
decoder.Metadata.ColorCache = new ColorCache(); decoder.Metadata.ColorCache = new ColorCache();
colorCacheSize = 1 << colorCacheBits; colorCacheSize = 1 << colorCacheBits;
decoder.Metadata.ColorCacheSize = colorCacheSize; decoder.Metadata.ColorCacheSize = colorCacheSize;
if (!(colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits))
{
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
}
decoder.Metadata.ColorCache.Init(colorCacheBits); decoder.Metadata.ColorCache.Init(colorCacheBits);
} }
else else

4
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -114,9 +114,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
public const int MaxPaletteSize = 256; public const int MaxPaletteSize = 256;
/// <summary> /// <summary>
/// Maximum number of color cache bits is 11. /// Maximum number of color cache bits is 10.
/// </summary> /// </summary>
public const int MaxColorCacheBits = 11; public const int MaxColorCacheBits = 10;
/// <summary> /// <summary>
/// The maximum number of allowed transforms in a VP8L bitstream. /// The maximum number of allowed transforms in a VP8L bitstream.

Loading…
Cancel
Save