Browse Source

Cleanup

pull/1552/head
Brian Popow 5 years ago
parent
commit
70b616a566
  1. 13
      src/ImageSharp/Formats/WebP/AlphaDecoder.cs
  2. 2
      src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs
  3. 18
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  4. 2
      src/ImageSharp/Formats/WebP/HistoIx.cs
  5. 21
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  6. 20
      src/ImageSharp/Formats/WebP/Lossless/ColorCache.cs
  7. 2
      src/ImageSharp/Formats/WebP/Lossless/CostManager.cs
  8. 5
      src/ImageSharp/Formats/WebP/Lossless/CostModel.cs
  9. 34
      src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs
  10. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
  11. 31
      src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs
  12. 38
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  13. 7
      src/ImageSharp/Formats/WebP/Lossless/NearLosslessEnc.cs
  14. 60
      src/ImageSharp/Formats/WebP/Lossless/PixOrCopy.cs
  15. 18
      src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
  16. 10
      src/ImageSharp/Formats/WebP/Lossless/Vp8LBackwardRefs.cs
  17. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LBitEntropy.cs
  18. 54
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  19. 14
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
  20. 4
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
  21. 51
      src/ImageSharp/Formats/WebP/Lossless/WebpLosslessDecoder.cs
  22. 18
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
  23. 4
      src/ImageSharp/Formats/WebP/Lossy/PassStats.cs
  24. 8
      src/ImageSharp/Formats/WebP/Lossy/QuantEnc.cs
  25. 8
      src/ImageSharp/Formats/WebP/Lossy/Vp8Decoder.cs
  26. 16
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  27. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs
  28. 31
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  29. 89
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoding.cs
  30. 6
      src/ImageSharp/Formats/WebP/Lossy/Vp8Histogram.cs
  31. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8Matrix.cs
  32. 12
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
  33. 4
      src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs
  34. 5
      src/ImageSharp/Formats/WebP/Lossy/Vp8StatsArray.cs
  35. 42
      src/ImageSharp/Formats/WebP/Lossy/WebpLossyDecoder.cs
  36. 4
      src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs
  37. 2
      src/ImageSharp/Formats/WebP/WebpConstants.cs
  38. 8
      src/ImageSharp/Formats/WebP/WebpDecoderCore.cs
  39. 5
      src/ImageSharp/Formats/WebP/WebpFeatures.cs
  40. 22
      src/ImageSharp/Formats/WebP/WebpImageFormatDetector.cs
  41. 6
      src/ImageSharp/Formats/WebP/WebpLookupTables.cs

13
src/ImageSharp/Formats/WebP/AlphaDecoder.cs

@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
if (this.Compressed == false) if (this.Compressed == false)
{ {
Span<byte> dataSpan = this.Data.Memory.Span; Span<byte> dataSpan = this.Data.Memory.Span;
var pixelCount = this.Width * this.Height; int pixelCount = this.Width * this.Height;
if (dataSpan.Length < pixelCount) if (dataSpan.Length < pixelCount)
{ {
WebpThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); WebpThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk");
@ -222,8 +222,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
{ {
// For vertical and gradient filtering, we need to decode the part above the // For vertical and gradient filtering, we need to decode the part above the
// cropTop row, in order to have the correct spatial predictors. // cropTop row, in order to have the correct spatial predictors.
int topRow = (this.AlphaFilterType == WebpAlphaFilterType.None || this.AlphaFilterType == WebpAlphaFilterType.Horizontal) ? 0 : this.LastRow; int topRow = this.AlphaFilterType == WebpAlphaFilterType.None || this.AlphaFilterType == WebpAlphaFilterType.Horizontal ? 0 : this.LastRow;
int firstRow = (this.LastRow < topRow) ? topRow : this.LastRow; int firstRow = this.LastRow < topRow ? topRow : this.LastRow;
if (lastRow > firstRow) if (lastRow > firstRow)
{ {
// Special method for paletted alpha data. // Special method for paletted alpha data.
@ -402,16 +402,13 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static byte GetAlphaValue(int val) private static byte GetAlphaValue(int val) => (byte)((val >> 8) & 0xff);
{
return (byte)((val >> 8) & 0xff);
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GradientPredictor(byte a, byte b, byte c) private static int GradientPredictor(byte a, byte b, byte c)
{ {
int g = a + b - c; int g = a + b - c;
return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit return (g & ~0xff) == 0 ? g : g < 0 ? 0 : 255; // clip to 8bit.
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

2
src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs

@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitReader
private void InitBitreader(uint size, int pos = 0) private void InitBitreader(uint size, int pos = 0)
{ {
var posPlusSize = pos + size; long posPlusSize = pos + size;
this.range = 255 - 1; this.range = 255 - 1;
this.value = 0; this.value = 0;
this.bits = -8; // to load the very first 8 bits. this.bits = -8; // to load the very first 8 bits.

18
src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs

@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
tab = WebpConstants.Cat6; tab = WebpConstants.Cat6;
} }
var tabIdx = 0; int tabIdx = 0;
while (mask != 0) while (mask != 0)
{ {
this.PutBit(v & mask, tab[tabIdx++]); this.PutBit(v & mask, tab[tabIdx++]);
@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="extraSize">The extra size in bytes needed.</param> /// <param name="extraSize">The extra size in bytes needed.</param>
public override void BitWriterResize(int extraSize) public override void BitWriterResize(int extraSize)
{ {
var neededSize = this.pos + extraSize; long neededSize = this.pos + extraSize;
if (neededSize <= this.maxPos) if (neededSize <= this.maxPos)
{ {
return; return;
@ -204,9 +204,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <inheritdoc/> /// <inheritdoc/>
public override void Finish() public override void Finish()
{ {
this.PutBits(0, 9 - this.nbBits); this.PutBits(0, 9 - this.nbBits);
this.nbBits = 0; // pad with zeroes. this.nbBits = 0; // pad with zeroes.
this.Flush(); this.Flush();
} }
public void PutSegment(int s, Span<byte> p) public void PutSegment(int s, Span<byte> p)
@ -352,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
if (value < 0) if (value < 0)
{ {
var valueToWrite = ((-value) << 1) | 1; int valueToWrite = (-value << 1) | 1;
this.PutBits((uint)valueToWrite, nbBits + 1); this.PutBits((uint)valueToWrite, nbBits + 1);
} }
else else
@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
// overflow -> propagate carry over pending 0xff's // overflow -> propagate carry over pending 0xff's
if (pos > 0) if (pos > 0)
{ {
this.Buffer[pos - 1]++; this.Buffer[pos - 1]++;
} }
} }
@ -497,7 +497,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
{ {
for (int s = 0; s < 3; ++s) for (int s = 0; s < 3; ++s)
{ {
if (bitWriter.PutBitUniform((proba.Segments[s] != 255) ? 1 : 0) != 0) if (bitWriter.PutBitUniform(proba.Segments[s] != 255 ? 1 : 0) != 0)
{ {
bitWriter.PutBits(proba.Segments[s], 8); bitWriter.PutBits(proba.Segments[s], 8);
} }
@ -564,7 +564,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
if (bitWriter.PutBitUniform(probas.UseSkipProba ? 1 : 0) != 0) if (bitWriter.PutBitUniform(probas.UseSkipProba ? 1 : 0) != 0)
{ {
bitWriter.PutBits(probas.SkipProba, 8); bitWriter.PutBits(probas.SkipProba, 8);
} }
} }

2
src/ImageSharp/Formats/WebP/HistoIx.cs

@ -31,6 +31,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
HistoPalette, HistoPalette,
HistoTotal, // Must be last. HistoTotal
} }
} }

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

@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Improve on simple LZ77 but only for high quality (TraceBackwards is costly). // Improve on simple LZ77 but only for high quality (TraceBackwards is costly).
if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25) if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25)
{ {
Vp8LHashChain hashChainTmp = (lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard) ? hashChain : hashChainBox; Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox;
BackwardReferencesTraceBackwards(width, height, bgra, cacheBits, hashChainTmp, best, worst); BackwardReferencesTraceBackwards(width, height, bgra, cacheBits, hashChainTmp, best, worst);
var histo = new Vp8LHistogram(worst, cacheBits); var histo = new Vp8LHistogram(worst, cacheBits);
double bitCostTrace = histo.EstimateBits(); double bitCostTrace = histo.EstimateBits();
@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <returns>Best cache size.</returns> /// <returns>Best cache size.</returns>
private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, int quality, Vp8LBackwardRefs refs, int bestCacheBits) private static int CalculateBestCacheSize(ReadOnlySpan<uint> bgra, int quality, Vp8LBackwardRefs refs, int bestCacheBits)
{ {
int cacheBitsMax = (quality <= 25) ? 0 : bestCacheBits; int cacheBitsMax = quality <= 25 ? 0 : bestCacheBits;
if (cacheBitsMax == 0) if (cacheBitsMax == 0)
{ {
// Local color cache is disabled. // Local color cache is disabled.
@ -256,12 +256,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
int pixCount = xSize * ySize; int pixCount = xSize * ySize;
bool useColorCache = cacheBits > 0; bool useColorCache = cacheBits > 0;
int literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + ((cacheBits > 0) ? (1 << cacheBits) : 0); int literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (cacheBits > 0 ? 1 << cacheBits : 0);
var costModel = new CostModel(literalArraySize); var costModel = new CostModel(literalArraySize);
int offsetPrev = -1; int offsetPrev = -1;
int lenPrev = -1; int lenPrev = -1;
double offsetCost = -1; double offsetCost = -1;
int firstOffsetIsConstant = -1; // initialized with 'impossible' value int firstOffsetIsConstant = -1; // initialized with 'impossible' value.
int reach = 0; int reach = 0;
var colorCache = new ColorCache(); var colorCache = new ColorCache();
@ -273,8 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
costModel.Build(xSize, cacheBits, refs); costModel.Build(xSize, cacheBits, refs);
var costManager = new CostManager(distArray, pixCount, costModel); var costManager = new CostManager(distArray, pixCount, costModel);
// We loop one pixel at a time, but store all currently best points to // We loop one pixel at a time, but store all currently best points to non-processed locations from this point.
// non-processed locations from this point.
distArray[0] = 0; distArray[0] = 0;
// Add first pixel as literal. // Add first pixel as literal.
@ -474,10 +473,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
int lenIni = len; int lenIni = len;
int maxReach = 0; int maxReach = 0;
int jMax = (i + lenIni >= pixCount) ? pixCount - 1 : i + lenIni; int jMax = i + lenIni >= pixCount ? pixCount - 1 : i + lenIni;
// Only start from what we have not checked already. // Only start from what we have not checked already.
iLastCheck = (i > iLastCheck) ? i : iLastCheck; iLastCheck = i > iLastCheck ? i : iLastCheck;
// We know the best match for the current pixel but we try to find the // We know the best match for the current pixel but we try to find the
// best matches for the current pixel AND the next one combined. // best matches for the current pixel AND the next one combined.
@ -640,7 +639,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
// Figure out if we should use the offset/length from the previous pixel // Figure out if we should use the offset/length from the previous pixel
// as an initial guess and therefore only inspect the offsets in windowOffsetsNew[]. // as an initial guess and therefore only inspect the offsets in windowOffsetsNew[].
bool usePrev = (bestLengthPrev > 1) && (bestLengthPrev < MaxLength); bool usePrev = bestLengthPrev > 1 && bestLengthPrev < MaxLength;
int numInd = usePrev ? windowOffsetsNewSize : windowOffsetsSize; int numInd = usePrev ? windowOffsetsNewSize : windowOffsetsSize;
bestLength = usePrev ? bestLengthPrev - 1 : 0; bestLength = usePrev ? bestLengthPrev - 1 : 0;
bestOffset = usePrev ? bestOffsetPrev : 0; bestOffset = usePrev ? bestOffsetPrev : 0;
@ -663,7 +662,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int countsJ = counts[j]; int countsJ = counts[j];
if (countsJOffset != countsJ) if (countsJOffset != countsJ)
{ {
currLength += (countsJOffset < countsJ) ? countsJOffset : countsJ; currLength += countsJOffset < countsJ ? countsJOffset : countsJ;
break; break;
} }
@ -728,7 +727,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
int maxLen = LosslessUtils.MaxFindCopyLength(pixelCount - i); int maxLen = LosslessUtils.MaxFindCopyLength(pixelCount - i);
int rleLen = LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen); int rleLen = LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen);
int prevRowLen = (i < xSize) ? 0 : LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen); int prevRowLen = i < xSize ? 0 : LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen);
if (rleLen >= prevRowLen && rleLen >= MinLength) if (rleLen >= prevRowLen && rleLen >= MinLength)
{ {
refs.Add(PixOrCopy.CreateCopy(1, (ushort)rleLen)); refs.Add(PixOrCopy.CreateCopy(1, (ushort)rleLen));

20
src/ImageSharp/Formats/WebP/Lossless/ColorCache.cs

@ -52,10 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary> /// </summary>
/// <param name="key">The key to lookup.</param> /// <param name="key">The key to lookup.</param>
/// <returns>The color for the key.</returns> /// <returns>The color for the key.</returns>
public uint Lookup(int key) public uint Lookup(int key) => this.Colors[key];
{
return this.Colors[key];
}
/// <summary> /// <summary>
/// Returns the index of the given color. /// Returns the index of the given color.
@ -73,24 +70,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary> /// </summary>
/// <param name="bgra">The color.</param> /// <param name="bgra">The color.</param>
/// <returns>The index for the color.</returns> /// <returns>The index for the color.</returns>
public int GetIndex(uint bgra) public int GetIndex(uint bgra) => HashPix(bgra, this.HashShift);
{
return HashPix(bgra, this.HashShift);
}
/// <summary> /// <summary>
/// Adds a new color to the cache. /// Adds a new color to the cache.
/// </summary> /// </summary>
/// <param name="key">The key.</param> /// <param name="key">The key.</param>
/// <param name="bgra">The color to add.</param> /// <param name="bgra">The color to add.</param>
public void Set(uint key, uint bgra) public void Set(uint key, uint bgra) => this.Colors[key] = bgra;
{
this.Colors[key] = bgra;
}
public static int HashPix(uint argb, int shift) public static int HashPix(uint argb, int shift) => (int)((argb * HashMul) >> shift);
{
return (int)((argb * HashMul) >> shift);
}
} }
} }

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

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public CostManager(ushort[] 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.CacheIntervals = new List<CostCacheInterval>(); this.CacheIntervals = new List<CostCacheInterval>();
this.CostCache = new List<double>(); this.CostCache = new List<double>();

5
src/ImageSharp/Formats/WebP/Lossless/CostModel.cs

@ -70,10 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return this.Literal[literalIdx]; return this.Literal[literalIdx];
} }
public double GetLiteralCost(uint v) public double GetLiteralCost(uint v) => this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff];
{
return this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff];
}
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output) private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output)
{ {

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

@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
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 ushort[imageHistoRawSize]; ushort[] mapTmp = new ushort[imageHistoRawSize];
var clusterMappings = new ushort[imageHistoRawSize]; ushort[] clusterMappings = new ushort[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++)
{ {
@ -49,11 +49,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Copies the histograms and computes its bitCost. histogramSymbols is optimized. // Copies the histograms and computes its bitCost. histogramSymbols is optimized.
int numUsed = HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols); int numUsed = HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols);
var entropyCombine = (numUsed > entropyCombineNumBins * 2) && (quality < 100); bool entropyCombine = numUsed > entropyCombineNumBins * 2 && quality < 100;
if (entropyCombine) if (entropyCombine)
{ {
ushort[] binMap = mapTmp; ushort[] binMap = mapTmp;
var numClusters = numUsed; int numClusters = numUsed;
double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality); double combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality);
HistogramAnalyzeEntropyBin(imageHisto, binMap); HistogramAnalyzeEntropyBin(imageHisto, binMap);
@ -218,7 +218,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// For some images, 'tryCombine' turns out to be false for a lot of // For some images, 'tryCombine' turns out to be false for a lot of
// histogram pairs. In that case, we fallback to combining // histogram pairs. In that case, we fallback to combining
// histograms as usual to avoid increasing the header size. // histograms as usual to avoid increasing the header size.
bool tryCombine = (curCombo.TrivialSymbol != NonTrivialSym) || ((histograms[idx].TrivialSymbol == NonTrivialSym) && (histograms[first].TrivialSymbol == NonTrivialSym)); bool tryCombine = curCombo.TrivialSymbol != NonTrivialSym || (histograms[idx].TrivialSymbol == NonTrivialSym && histograms[first].TrivialSymbol == NonTrivialSym);
int maxCombineFailures = 32; int maxCombineFailures = 32;
if (tryCombine || binInfo[binId].NumCombineFailures >= maxCombineFailures) if (tryCombine || binInfo[binId].NumCombineFailures >= maxCombineFailures)
{ {
@ -275,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
// Create a mapping from a cluster id to its minimal version. // Create a mapping from a cluster id to its minimal version.
var clusterMax = 0; int clusterMax = 0;
clusterMappingsTmp.AsSpan().Fill(0); clusterMappingsTmp.AsSpan().Fill(0);
// Re-map the ids. // Re-map the ids.
@ -305,7 +305,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
uint seed = 1; uint seed = 1;
int triesWithNoSuccess = 0; int triesWithNoSuccess = 0;
var numUsed = histograms.Count(h => h != null); int numUsed = histograms.Count(h => h != null);
int outerIters = numUsed; int outerIters = numUsed;
int numTriesNoSuccess = outerIters / 2; int numTriesNoSuccess = outerIters / 2;
@ -320,7 +320,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int maxSize = 9; int maxSize = 9;
// Fill the initial mapping. // Fill the initial mapping.
var mappings = new int[histograms.Count]; int[] mappings = new int[histograms.Count];
for (int j = 0, iter = 0; iter < histograms.Count; iter++) for (int j = 0, iter = 0; iter < histograms.Count; iter++)
{ {
if (histograms[iter] == null) if (histograms[iter] == null)
@ -334,7 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Collapse similar histograms. // Collapse similar histograms.
for (int iter = 0; iter < outerIters && numUsed >= minClusterSize && ++triesWithNoSuccess < numTriesNoSuccess; iter++) for (int iter = 0; iter < outerIters && numUsed >= minClusterSize && ++triesWithNoSuccess < numTriesNoSuccess; iter++)
{ {
double bestCost = (histoPriorityList.Count == 0) ? 0.0d : histoPriorityList[0].CostDiff; double bestCost = histoPriorityList.Count == 0 ? 0.0d : histoPriorityList[0].CostDiff;
int numTries = numUsed / 2; int numTries = numUsed / 2;
uint randRange = (uint)((numUsed - 1) * numUsed); uint randRange = (uint)((numUsed - 1) * numUsed);
@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
idx2 = mappings[idx2]; idx2 = mappings[idx2];
// Calculate cost reduction on combination. // Calculate cost reduction on combination.
var currCost = HistoPriorityListPush(histoPriorityList, maxSize, histograms, idx1, idx2, bestCost); double currCost = HistoPriorityListPush(histoPriorityList, maxSize, histograms, idx1, idx2, bestCost);
// Found a better pair? // Found a better pair?
if (currCost < 0) if (currCost < 0)
@ -374,10 +374,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
// Get the best histograms. // Get the best histograms.
var bestIdx1 = histoPriorityList[0].Idx1; int bestIdx1 = histoPriorityList[0].Idx1;
var bestIdx2 = histoPriorityList[0].Idx2; int bestIdx2 = histoPriorityList[0].Idx2;
var mappingIndex = Array.IndexOf(mappings, bestIdx2); int mappingIndex = Array.IndexOf(mappings, bestIdx2);
Span<int> src = mappings.AsSpan(mappingIndex + 1, numUsed - mappingIndex - 1); Span<int> src = mappings.AsSpan(mappingIndex + 1, numUsed - mappingIndex - 1);
Span<int> dst = mappings.AsSpan(mappingIndex); Span<int> dst = mappings.AsSpan(mappingIndex);
src.CopyTo(dst); src.CopyTo(dst);
@ -554,7 +554,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
// Recompute each output. // Recompute each output.
var paletteCodeBits = output.First().PaletteCodeBits; int paletteCodeBits = output.First().PaletteCodeBits;
output.Clear(); output.Clear();
for (int i = 0; i < outSize; i++) for (int i = 0; i < outSize; i++)
{ {
@ -620,7 +620,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
double sumCost = h1.BitCost + h2.BitCost; double sumCost = h1.BitCost + h2.BitCost;
pair.CostCombo = 0.0d; pair.CostCombo = 0.0d;
h1.GetCombinedHistogramEntropy(h2, sumCost + threshold, costInitial: pair.CostCombo, out var cost); h1.GetCombinedHistogramEntropy(h2, sumCost + threshold, costInitial: pair.CostCombo, out double cost);
pair.CostCombo = cost; pair.CostCombo = cost;
pair.CostDiff = pair.CostCombo - sumCost; pair.CostDiff = pair.CostCombo - sumCost;
} }
@ -633,7 +633,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (pair.CostDiff < histoList[0].CostDiff) if (pair.CostDiff < histoList[0].CostDiff)
{ {
// Replace the best pair. // Replace the best pair.
var oldIdx = histoList.IndexOf(pair); int oldIdx = histoList.IndexOf(pair);
histoList[oldIdx] = histoList[0]; histoList[oldIdx] = histoList[0];
histoList[0] = pair; histoList[0] = pair;
} }
@ -642,7 +642,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static void HistogramAdd(Vp8LHistogram a, Vp8LHistogram b, Vp8LHistogram output) private static void HistogramAdd(Vp8LHistogram a, Vp8LHistogram b, Vp8LHistogram output)
{ {
a.Add(b, output); a.Add(b, output);
output.TrivialSymbol = (a.TrivialSymbol == b.TrivialSymbol) ? a.TrivialSymbol : NonTrivialSym; output.TrivialSymbol = a.TrivialSymbol == b.TrivialSymbol ? a.TrivialSymbol : NonTrivialSym;
} }
private static double GetCombineCostFactor(int histoSize, int quality) private static double GetCombineCostFactor(int histoSize, int quality)

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

@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
else else
{ {
return (t1.Value < t2.Value) ? -1 : 1; return t1.Value < t2.Value ? -1 : 1;
} }
} }

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

@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint sum = 0; uint sum = 0;
for (int i = 0; i < length + 1; ++i) for (int i = 0; i < length + 1; ++i)
{ {
var valuesShouldBeCollapsedToStrideAverage = ValuesShouldBeCollapsedToStrideAverage((int)counts[i], (int)limit); bool 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)
{ {
if (stride >= 4 || (stride >= 3 && sum == 0)) if (stride >= 4 || (stride >= 3 && sum == 0))
@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
if (histogram[j] != 0) if (histogram[j] != 0)
{ {
uint count = (histogram[j] < countMin) ? countMin : histogram[j]; uint count = histogram[j] < countMin ? countMin : histogram[j];
tree[idx].TotalCount = (int)count; tree[idx].TotalCount = (int)count;
tree[idx].Value = j; tree[idx].Value = j;
tree[idx].PoolIndexLeft = -1; tree[idx].PoolIndexLeft = -1;
@ -229,9 +229,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
var endIdx = k + 1; int endIdx = k + 1;
var num = treeSize - k; int num = treeSize - k;
var startIdx = endIdx + num - 1; int startIdx = endIdx + num - 1;
for (int i = startIdx; i >= endIdx; i--) for (int i = startIdx; i >= endIdx; i--)
{ {
tree[i] = (HuffmanTree)tree[i - 1].DeepClone(); tree[i] = (HuffmanTree)tree[i - 1].DeepClone();
@ -241,7 +241,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
tree[k].Value = -1; tree[k].Value = -1;
tree[k].PoolIndexLeft = treePoolSize - 1; tree[k].PoolIndexLeft = treePoolSize - 1;
tree[k].PoolIndexRight = treePoolSize - 2; tree[k].PoolIndexRight = treePoolSize - 2;
treeSize = treeSize + 1; treeSize++;
} }
SetBitDepths(tree, treePool, bitDepths, 0); SetBitDepths(tree, treePool, bitDepths, 0);
@ -284,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
k++; k++;
} }
var runs = k - i; int runs = k - i;
if (value == 0) if (value == 0)
{ {
tokenPos += CodeRepeatedZeros(runs, tokensArray.AsSpan(tokenPos)); tokenPos += CodeRepeatedZeros(runs, tokensArray.AsSpan(tokenPos));
@ -308,17 +308,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Guard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize)); Guard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize));
// sorted[codeLengthsSize] is a pre-allocated array for sorting symbols by code length. // sorted[codeLengthsSize] is a pre-allocated array for sorting symbols by code length.
var sorted = new int[codeLengthsSize]; int[] sorted = new int[codeLengthsSize];
int totalSize = 1 << rootBits; // total size root table + 2nd level table. int totalSize = 1 << rootBits; // total size root table + 2nd level table.
int len; // current code length. int len; // current code length.
int symbol; // symbol index in original or sorted table. int symbol; // symbol index in original or sorted table.
var counts = new int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. int[] counts = new int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length.
var offsets = new int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. int[] offsets = new int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length.
// Build histogram of code lengths. // Build histogram of code lengths.
for (symbol = 0; symbol < codeLengthsSize; ++symbol) for (symbol = 0; symbol < codeLengthsSize; ++symbol)
{ {
var codeLengthOfSymbol = codeLengths[symbol]; int codeLengthOfSymbol = codeLengths[symbol];
if (codeLengthOfSymbol > WebpConstants.MaxAllowedCodeLength) if (codeLengthOfSymbol > WebpConstants.MaxAllowedCodeLength)
{ {
return 0; return 0;
@ -338,7 +338,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
for (len = 1; len < WebpConstants.MaxAllowedCodeLength; ++len) for (len = 1; len < WebpConstants.MaxAllowedCodeLength; ++len)
{ {
int codesOfLength = counts[len]; int codesOfLength = counts[len];
if (codesOfLength > (1 << len)) if (codesOfLength > 1 << len)
{ {
return 0; return 0;
} }
@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Fill in root table. // Fill in root table.
for (len = 1, step = 2; len <= rootBits; ++len, step <<= 1) for (len = 1, step = 2; len <= rootBits; ++len, step <<= 1)
{ {
var countsLen = counts[len]; int countsLen = counts[len];
numOpen <<= 1; numOpen <<= 1;
numNodes += numOpen; numNodes += numOpen;
numOpen -= counts[len]; numOpen -= counts[len];
@ -652,9 +652,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <summary> /// <summary>
/// Heuristics for selecting the stride ranges to collapse. /// Heuristics for selecting the stride ranges to collapse.
/// </summary> /// </summary>
private static bool ValuesShouldBeCollapsedToStrideAverage(int a, int b) private static bool ValuesShouldBeCollapsedToStrideAverage(int a, int b) => Math.Abs(a - b) < 4;
{
return Math.Abs(a - b) < 4;
}
} }
} }

38
src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs

@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int MaxFindCopyLength(int len) => (len < BackwardReferenceEncoder.MaxLength) ? len : BackwardReferenceEncoder.MaxLength; public static int MaxFindCopyLength(int len) => len < BackwardReferenceEncoder.MaxLength ? len : BackwardReferenceEncoder.MaxLength;
public static int PrefixEncodeBits(int distance, ref int extraBits) public static int PrefixEncodeBits(int distance, ref int extraBits)
{ {
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i; int i;
for (i = 0; i + 8 <= numPixels; i += 8) for (i = 0; i + 8 <= numPixels; i += 8)
{ {
var idx = p + i; uint* idx = p + i;
Vector256<byte> input = Avx.LoadVector256((ushort*)idx).AsByte(); Vector256<byte> input = Avx.LoadVector256((ushort*)idx).AsByte();
Vector256<byte> in0g0g = Avx2.Shuffle(input, mask); Vector256<byte> in0g0g = Avx2.Shuffle(input, mask);
Vector256<byte> output = Avx2.Add(input, in0g0g); Vector256<byte> output = Avx2.Add(input, in0g0g);
@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i; int i;
for (i = 0; i + 4 <= numPixels; i += 4) for (i = 0; i + 4 <= numPixels; i += 4)
{ {
var idx = p + i; uint* idx = p + i;
Vector128<byte> input = Sse2.LoadVector128((ushort*)idx).AsByte(); Vector128<byte> input = Sse2.LoadVector128((ushort*)idx).AsByte();
Vector128<byte> in0g0g = Ssse3.Shuffle(input, mask); Vector128<byte> in0g0g = Ssse3.Shuffle(input, mask);
Vector128<byte> output = Sse2.Add(input, in0g0g); Vector128<byte> output = Sse2.Add(input, in0g0g);
@ -146,14 +146,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
var mask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); byte mask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0);
int numPixels = pixelData.Length; int numPixels = pixelData.Length;
fixed (uint* p = pixelData) fixed (uint* p = pixelData)
{ {
int i; int i;
for (i = 0; i + 4 <= numPixels; i += 4) for (i = 0; i + 4 <= numPixels; i += 4)
{ {
var idx = p + i; uint* idx = p + i;
Vector128<ushort> input = Sse2.LoadVector128((ushort*)idx); Vector128<ushort> input = Sse2.LoadVector128((ushort*)idx);
Vector128<ushort> a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g Vector128<ushort> a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g
Vector128<ushort> b = Sse2.ShuffleLow(a, mask); Vector128<ushort> b = Sse2.ShuffleLow(a, mask);
@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i; int i;
for (i = 0; i + 8 <= numPixels; i += 8) for (i = 0; i + 8 <= numPixels; i += 8)
{ {
var idx = p + i; uint* idx = p + i;
Vector256<byte> input = Avx.LoadVector256((ushort*)idx).AsByte(); Vector256<byte> input = Avx.LoadVector256((ushort*)idx).AsByte();
Vector256<byte> in0g0g = Avx2.Shuffle(input, mask); Vector256<byte> in0g0g = Avx2.Shuffle(input, mask);
Vector256<byte> output = Avx2.Subtract(input, in0g0g); Vector256<byte> output = Avx2.Subtract(input, in0g0g);
@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i; int i;
for (i = 0; i + 4 <= numPixels; i += 4) for (i = 0; i + 4 <= numPixels; i += 4)
{ {
var idx = p + i; uint* idx = p + i;
Vector128<byte> input = Sse2.LoadVector128((ushort*)idx).AsByte(); Vector128<byte> input = Sse2.LoadVector128((ushort*)idx).AsByte();
Vector128<byte> in0g0g = Ssse3.Shuffle(input, mask); Vector128<byte> in0g0g = Ssse3.Shuffle(input, mask);
Vector128<byte> output = Sse2.Subtract(input, in0g0g); Vector128<byte> output = Sse2.Subtract(input, in0g0g);
@ -238,14 +238,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
{ {
var mask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); byte mask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0);
int numPixels = pixelData.Length; int numPixels = pixelData.Length;
fixed (uint* p = pixelData) fixed (uint* p = pixelData)
{ {
int i; int i;
for (i = 0; i + 4 <= numPixels; i += 4) for (i = 0; i + 4 <= numPixels; i += 4)
{ {
var idx = p + i; uint* idx = p + i;
Vector128<ushort> input = Sse2.LoadVector128((ushort*)idx); Vector128<ushort> input = Sse2.LoadVector128((ushort*)idx);
Vector128<ushort> a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g Vector128<ushort> a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g
Vector128<ushort> b = Sse2.ShuffleLow(a, mask); Vector128<ushort> b = Sse2.ShuffleLow(a, mask);
@ -299,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int countMask = pixelsPerByte - 1; int countMask = pixelsPerByte - 1;
int bitMask = (1 << bitsPerPixel) - 1; int bitMask = (1 << bitsPerPixel) - 1;
var decodedPixelData = new uint[width * height]; uint[] decodedPixelData = new uint[width * height];
int pixelDataPos = 0; int pixelDataPos = 0;
for (int y = 0; y < height; ++y) for (int y = 0; y < height; ++y)
{ {
@ -401,13 +401,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
var maskalphagreen = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); var maskalphagreen = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var maskredblue = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); var maskredblue = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
var shufflemask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); byte shufflemask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0);
int idx; int idx;
fixed (uint* src = data) fixed (uint* src = data)
{ {
for (idx = 0; idx + 4 <= numPixels; idx += 4) for (idx = 0; idx + 4 <= numPixels; idx += 4)
{ {
var pos = src + idx; uint* pos = src + idx;
Vector128<uint> input = Sse2.LoadVector128(pos); Vector128<uint> input = Sse2.LoadVector128(pos);
Vector128<byte> a = Sse2.And(input.AsByte(), maskalphagreen); Vector128<byte> a = Sse2.And(input.AsByte(), maskalphagreen);
Vector128<short> b = Sse2.ShuffleLow(a.AsInt16(), shufflemask); Vector128<short> b = Sse2.ShuffleLow(a.AsInt16(), shufflemask);
@ -466,13 +466,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vector128<int> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128<int> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
var maskalphagreen = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); var maskalphagreen = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var shufflemask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); byte shufflemask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0);
fixed (uint* src = pixelData) fixed (uint* src = pixelData)
{ {
int idx; int idx;
for (idx = 0; idx + 4 <= pixelData.Length; idx += 4) for (idx = 0; idx + 4 <= pixelData.Length; idx += 4)
{ {
var pos = src + idx; uint* pos = src + idx;
Vector128<uint> input = Sse2.LoadVector128(pos); Vector128<uint> input = Sse2.LoadVector128(pos);
Vector128<byte> a = Sse2.And(input.AsByte(), maskalphagreen); Vector128<byte> a = Sse2.And(input.AsByte(), maskalphagreen);
Vector128<short> b = Sse2.ShuffleLow(a.AsInt16(), shufflemask); Vector128<short> b = Sse2.ShuffleLow(a.AsInt16(), shufflemask);
@ -754,13 +754,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <summary> /// <summary>
/// Fast calculation of log2(v) for integer input. /// Fast calculation of log2(v) for integer input.
/// </summary> /// </summary>
public static float FastLog2(uint v) => (v < LogLookupIdxMax) ? WebpLookupTables.Log2Table[v] : FastLog2Slow(v); public static float FastLog2(uint v) => v < LogLookupIdxMax ? WebpLookupTables.Log2Table[v] : FastLog2Slow(v);
/// <summary> /// <summary>
/// Fast calculation of v * log2(v) for integer input. /// Fast calculation of v * log2(v) for integer input.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static float FastSLog2(uint v) => (v < LogLookupIdxMax) ? WebpLookupTables.SLog2Table[v] : FastSLog2Slow(v); public static float FastSLog2(uint v) => v < LogLookupIdxMax ? WebpLookupTables.SLog2Table[v] : FastSLog2Slow(v);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void ColorCodeToMultipliers(uint colorCode, ref Vp8LMultipliers m) public static void ColorCodeToMultipliers(uint colorCode, ref Vp8LMultipliers m)
@ -803,7 +803,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// The correction factor: log(1 + d) ~ d; for very small d values, so // The correction factor: log(1 + d) ~ d; for very small d values, so
// log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v
// LOG_2_RECIPROCAL ~ 23/16 // LOG_2_RECIPROCAL ~ 23/16
var correction = (int)((23 * (origV & (y - 1))) >> 4); int correction = (int)((23 * (origV & (y - 1))) >> 4);
return (vF * (WebpLookupTables.Log2Table[v] + logCnt)) + correction; return (vF * (WebpLookupTables.Log2Table[v] + logCnt)) + correction;
} }
else else
@ -855,7 +855,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int highestBit = WebpCommonUtils.BitsLog2Floor((uint)--distance); int highestBit = WebpCommonUtils.BitsLog2Floor((uint)--distance);
int secondHighestBit = (distance >> (highestBit - 1)) & 1; int secondHighestBit = (distance >> (highestBit - 1)) & 1;
extraBits = highestBit - 1; extraBits = highestBit - 1;
var code = (2 * highestBit) + secondHighestBit; int code = (2 * highestBit) + secondHighestBit;
return code; return code;
} }
@ -1252,7 +1252,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Sub3((int)((a >> 16) & 0xff), (int)((b >> 16) & 0xff), (int)((c >> 16) & 0xff)) + Sub3((int)((a >> 16) & 0xff), (int)((b >> 16) & 0xff), (int)((c >> 16) & 0xff)) +
Sub3((int)((a >> 8) & 0xff), (int)((b >> 8) & 0xff), (int)((c >> 8) & 0xff)) + Sub3((int)((a >> 8) & 0xff), (int)((b >> 8) & 0xff), (int)((c >> 8) & 0xff)) +
Sub3((int)(a & 0xff), (int)(b & 0xff), (int)(c & 0xff)); Sub3((int)(a & 0xff), (int)(b & 0xff), (int)(c & 0xff));
return (paMinusPb <= 0) ? a : b; return paMinusPb <= 0 ? a : b;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

7
src/ImageSharp/Formats/WebP/Lossless/NearLosslessEnc.cs

@ -102,14 +102,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return biased & ~mask; return biased & ~mask;
} }
private static bool IsSmooth(Span<uint> prevRow, Span<uint> currRow, Span<uint> nextRow, int ix, int limit) private static bool IsSmooth(Span<uint> prevRow, Span<uint> currRow, Span<uint> nextRow, int ix, int limit) =>
{ IsNear(currRow[ix], currRow[ix - 1], limit) && // Check that all pixels in 4-connected neighborhood are smooth.
// Check that all pixels in 4-connected neighborhood are smooth.
return IsNear(currRow[ix], currRow[ix - 1], limit) &&
IsNear(currRow[ix], currRow[ix + 1], limit) && IsNear(currRow[ix], currRow[ix + 1], limit) &&
IsNear(currRow[ix], prevRow[ix], limit) && IsNear(currRow[ix], prevRow[ix], limit) &&
IsNear(currRow[ix], nextRow[ix], limit); IsNear(currRow[ix], nextRow[ix], limit);
}
// Checks if distance between corresponding channel values of pixels a and b is within the given limit. // Checks if distance between corresponding channel values of pixels a and b is within the given limit.
private static bool IsNear(uint a, uint b, int limit) private static bool IsNear(uint a, uint b, int limit)

60
src/ImageSharp/Formats/WebP/Lossless/PixOrCopy.cs

@ -14,69 +14,41 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public uint BgraOrDistance { get; set; } public uint BgraOrDistance { get; set; }
public static PixOrCopy CreateCacheIdx(int idx) public static PixOrCopy CreateCacheIdx(int idx) =>
{ new PixOrCopy()
return new PixOrCopy()
{ {
Mode = PixOrCopyMode.CacheIdx, Mode = PixOrCopyMode.CacheIdx,
BgraOrDistance = (uint)idx, BgraOrDistance = (uint)idx,
Len = 1 Len = 1
}; };
}
public static PixOrCopy CreateLiteral(uint bgra) public static PixOrCopy CreateLiteral(uint bgra) =>
{ new PixOrCopy()
return new PixOrCopy()
{ {
Mode = PixOrCopyMode.Literal, Mode = PixOrCopyMode.Literal,
BgraOrDistance = bgra, BgraOrDistance = bgra,
Len = 1 Len = 1
}; };
}
public static PixOrCopy CreateCopy(uint distance, ushort len) public static PixOrCopy CreateCopy(uint distance, ushort len) => new PixOrCopy()
{ {
return new PixOrCopy() Mode = PixOrCopyMode.Copy,
{ BgraOrDistance = distance,
Mode = PixOrCopyMode.Copy, Len = len
BgraOrDistance = distance, };
Len = len
};
}
public uint Literal(int component) public uint Literal(int component) => (this.BgraOrDistance >> (component * 8)) & 0xff;
{
return (this.BgraOrDistance >> (component * 8)) & 0xff;
}
public uint CacheIdx() public uint CacheIdx() => this.BgraOrDistance;
{
return this.BgraOrDistance;
}
public ushort Length() public ushort Length() => this.Len;
{
return this.Len;
}
public uint Distance() public uint Distance() => this.BgraOrDistance;
{
return this.BgraOrDistance;
}
public bool IsLiteral() public bool IsLiteral() => this.Mode == PixOrCopyMode.Literal;
{
return this.Mode == PixOrCopyMode.Literal;
}
public bool IsCacheIdx() public bool IsCacheIdx() => this.Mode == PixOrCopyMode.CacheIdx;
{
return this.Mode == PixOrCopyMode.CacheIdx;
}
public bool IsCopy() public bool IsCopy() => this.Mode == PixOrCopyMode.Copy;
{
return this.Mode == PixOrCopyMode.Copy;
}
} }
} }

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

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality); int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality);
// TODO: Can we optimize this? // TODO: Can we optimize this?
var histo = new int[4][]; int[][] histo = new int[4][];
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
histo[i] = new int[256]; histo[i] = new int[256];
@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int maxX = GetMin(tileSize, width - startX); int maxX = GetMin(tileSize, width - startX);
// Whether there exist columns just outside the tile. // Whether there exist columns just outside the tile.
int haveLeft = (startX > 0) ? 1 : 0; int haveLeft = startX > 0 ? 1 : 0;
// Position and size of the strip covering the tile and adjacent columns if they exist. // Position and size of the strip covering the tile and adjacent columns if they exist.
int contextStartX = startX - haveLeft; int contextStartX = startX - haveLeft;
@ -210,8 +210,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int tilesPerRow = LosslessUtils.SubSampleSize(width, bits); int tilesPerRow = LosslessUtils.SubSampleSize(width, bits);
// Prediction modes of the left and above neighbor tiles. // Prediction modes of the left and above neighbor tiles.
int leftMode = (int)((tileX > 0) ? (modes[(tileY * tilesPerRow) + tileX - 1] >> 8) & 0xff : 0xff); int leftMode = (int)(tileX > 0 ? (modes[(tileY * tilesPerRow) + tileX - 1] >> 8) & 0xff : 0xff);
int aboveMode = (int)((tileY > 0) ? (modes[((tileY - 1) * tilesPerRow) + tileX] >> 8) & 0xff : 0xff); int aboveMode = (int)(tileY > 0 ? (modes[((tileY - 1) * tilesPerRow) + tileX] >> 8) & 0xff : 0xff);
// The width of upper_row and current_row is one pixel larger than image width // The width of upper_row and current_row is one pixel larger than image width
// to allow the top right pixel to point to the leftmost pixel of the next row // to allow the top right pixel to point to the leftmost pixel of the next row
@ -260,7 +260,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// the image (wrapping to the leftmost pixel of the next row if it does // the image (wrapping to the leftmost pixel of the next row if it does
// not exist in the currentRow). // not exist in the currentRow).
int offset = (y * width) + contextStartX; int offset = (y * width) + contextStartX;
Span<uint> src = argb.Slice(offset, maxX + haveLeft + ((y + 1) < height ? 1 : 0)); Span<uint> src = argb.Slice(offset, maxX + haveLeft + (y + 1 < height ? 1 : 0));
Span<uint> dst = currentRow.Slice(contextStartX); Span<uint> dst = currentRow.Slice(contextStartX);
src.CopyTo(dst); src.CopyTo(dst);
@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint residual; uint residual;
if (y == 0) if (y == 0)
{ {
predict = (x == 0) ? WebpConstants.ArgbBlack : currentRow[x - 1]; // Left. predict = x == 0 ? WebpConstants.ArgbBlack : currentRow[x - 1]; // Left.
} }
else if (x == 0) else if (x == 0)
{ {
@ -478,7 +478,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
quantization >>= 1; quantization >>= 1;
} }
if ((value >> 24) == 0 || (value >> 24) == 0xff) if (value >> 24 == 0 || value >> 24 == 0xff)
{ {
// Preserve transparency of fully transparent or fully opaque pixels. // Preserve transparency of fully transparent or fully opaque pixels.
a = NearLosslessDiff((byte)((value >> 24) & 0xff), (byte)((predict >> 24) & 0xff)); a = NearLosslessDiff((byte)((value >> 24) & 0xff), (byte)((predict >> 24) & 0xff));
@ -586,7 +586,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Span<uint> tmp32 = upperRow; Span<uint> tmp32 = upperRow;
upperRow = currentRow; upperRow = currentRow;
currentRow = tmp32; currentRow = tmp32;
Span<uint> src = argb.Slice(y * width, width + ((y + 1) < height ? 1 : 0)); Span<uint> src = argb.Slice(y * width, width + (y + 1 < height ? 1 : 0));
src.CopyTo(currentRow); src.CopyTo(currentRow);
if (lowEffort) if (lowEffort)
@ -1165,7 +1165,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint MultipliersToColorCode(Vp8LMultipliers m) => 0xff000000u | ((uint)m.RedToBlue << 16) | ((uint)m.GreenToBlue << 8) | m.GreenToRed; private static uint MultipliersToColorCode(Vp8LMultipliers m) => 0xff000000u | ((uint)m.RedToBlue << 16) | ((uint)m.GreenToBlue << 8) | m.GreenToRed;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetMin(int a, int b) => (a > b) ? b : a; private static int GetMin(int a, int b) => a > b ? b : a;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetMax(int a, int b) => (a < b) ? b : a; private static int GetMax(int a, int b) => (a < b) ? b : a;

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

@ -7,10 +7,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
internal class Vp8LBackwardRefs internal class Vp8LBackwardRefs
{ {
public Vp8LBackwardRefs() public Vp8LBackwardRefs() => this.Refs = new List<PixOrCopy>();
{
this.Refs = new List<PixOrCopy>();
}
/// <summary> /// <summary>
/// Gets or sets the common block-size. /// Gets or sets the common block-size.
@ -22,9 +19,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary> /// </summary>
public List<PixOrCopy> Refs { get; } public List<PixOrCopy> Refs { get; }
public void Add(PixOrCopy pixOrCopy) public void Add(PixOrCopy pixOrCopy) => this.Refs.Add(pixOrCopy);
{
this.Refs.Add(pixOrCopy);
}
} }
} }

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

@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
double minLimit = (2 * this.Sum) - this.MaxVal; double minLimit = (2 * this.Sum) - this.MaxVal;
minLimit = (mix * minLimit) + ((1.0 - mix) * this.Entropy); minLimit = (mix * minLimit) + ((1.0 - mix) * this.Entropy);
return (this.Entropy < minLimit) ? minLimit : this.Entropy; return this.Entropy < minLimit ? minLimit : this.Entropy;
} }
public void BitsEntropyUnrefined(Span<uint> array, int n) public void BitsEntropyUnrefined(Span<uint> array, int n)

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

@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
this.Refs[i] = new Vp8LBackwardRefs this.Refs[i] = new Vp8LBackwardRefs
{ {
BlockSize = (refsBlockSize < MinBlockSize) ? MinBlockSize : refsBlockSize BlockSize = refsBlockSize < MinBlockSize ? MinBlockSize : refsBlockSize
}; };
} }
} }
@ -283,10 +283,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
bool useCache = true; bool useCache = true;
this.UsePalette = crunchConfig.EntropyIdx == EntropyIx.Palette || this.UsePalette = crunchConfig.EntropyIdx == EntropyIx.Palette ||
crunchConfig.EntropyIdx == EntropyIx.PaletteAndSpatial; crunchConfig.EntropyIdx == EntropyIx.PaletteAndSpatial;
this.UseSubtractGreenTransform = (crunchConfig.EntropyIdx == EntropyIx.SubGreen) || this.UseSubtractGreenTransform = crunchConfig.EntropyIdx == EntropyIx.SubGreen ||
(crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen;
this.UsePredictorTransform = (crunchConfig.EntropyIdx == EntropyIx.Spatial) || this.UsePredictorTransform = crunchConfig.EntropyIdx == EntropyIx.Spatial ||
(crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen;
if (lowEffort) if (lowEffort)
{ {
this.UseCrossColorTransform = false; this.UseCrossColorTransform = false;
@ -305,7 +305,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (this.nearLossless) if (this.nearLossless)
{ {
// Apply near-lossless preprocessing. // Apply near-lossless preprocessing.
bool useNearLossless = (this.nearLosslessQuality < 100) && !this.UsePalette && !this.UsePredictorTransform; bool useNearLossless = this.nearLosslessQuality < 100 && !this.UsePalette && !this.UsePredictorTransform;
if (useNearLossless) if (useNearLossless)
{ {
this.AllocateTransformBuffer(width, height); this.AllocateTransformBuffer(width, height);
@ -320,7 +320,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.MapImageFromPalette(width, height); this.MapImageFromPalette(width, height);
// If using a color cache, do not have it bigger than the number of colors. // If using a color cache, do not have it bigger than the number of colors.
if (useCache && this.PaletteSize < (1 << WebpConstants.MaxColorCacheBits)) if (useCache && this.PaletteSize < 1 << WebpConstants.MaxColorCacheBits)
{ {
this.CacheBits = WebpCommonUtils.BitsLog2Floor((uint)this.PaletteSize) + 1; this.CacheBits = WebpCommonUtils.BitsLog2Floor((uint)this.PaletteSize) + 1;
} }
@ -419,7 +419,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.TransformBits = GetTransformBits(this.method, this.HistoBits); this.TransformBits = GetTransformBits(this.method, this.HistoBits);
// Try out multiple LZ77 on images with few colors. // Try out multiple LZ77 on images with few colors.
int nlz77s = (this.PaletteSize > 0 && this.PaletteSize <= 16) ? 2 : 1; int nlz77s = this.PaletteSize > 0 && this.PaletteSize <= 16 ? 2 : 1;
EntropyIx entropyIdx = this.AnalyzeEntropy(bgra, width, height, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero); EntropyIx entropyIdx = this.AnalyzeEntropy(bgra, width, height, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero);
bool doNotCache = false; bool doNotCache = false;
@ -463,7 +463,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
crunchConfig.SubConfigs.Add(new CrunchSubConfig crunchConfig.SubConfigs.Add(new CrunchSubConfig
{ {
Lz77 = (j == 0) ? (int)Vp8LLz77Type.Lz77Standard | (int)Vp8LLz77Type.Lz77Rle : (int)Vp8LLz77Type.Lz77Box, Lz77 = j == 0 ? (int)Vp8LLz77Type.Lz77Standard | (int)Vp8LLz77Type.Lz77Rle : (int)Vp8LLz77Type.Lz77Box,
DoNotCache = doNotCache DoNotCache = doNotCache
}); });
} }
@ -944,7 +944,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, ushort[] 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);
// x and y trace the position in the image. // x and y trace the position in the image.
int x = 0; int x = 0;
@ -957,7 +957,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
while (c.MoveNext()) while (c.MoveNext())
{ {
PixOrCopy v = c.Current; PixOrCopy v = c.Current;
if ((tileX != (x & tileMask)) || (tileY != (y & tileMask))) if (tileX != (x & tileMask) || tileY != (y & tileMask))
{ {
tileX = x & tileMask; tileX = x & tileMask;
tileY = y & tileMask; tileY = y & tileMask;
@ -1038,7 +1038,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint pix = currentRow[x]; uint pix = currentRow[x];
uint pixDiff = LosslessUtils.SubPixels(pix, pixPrev); uint pixDiff = LosslessUtils.SubPixels(pix, pixPrev);
pixPrev = pix; pixPrev = pix;
if ((pixDiff == 0) || (prevRow != null && pix == prevRow[x])) if (pixDiff == 0 || (prevRow != null && pix == prevRow[x]))
{ {
continue; continue;
} }
@ -1245,11 +1245,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// This is done line by line. // This is done line by line.
if (paletteSize <= 4) if (paletteSize <= 4)
{ {
xBits = (paletteSize <= 2) ? 3 : 2; xBits = paletteSize <= 2 ? 3 : 2;
} }
else else
{ {
xBits = (paletteSize <= 16) ? 1 : 0; xBits = paletteSize <= 16 ? 1 : 0;
} }
this.CurrentWidth = LosslessUtils.SubSampleSize(width, xBits); this.CurrentWidth = LosslessUtils.SubSampleSize(width, xBits);
@ -1495,17 +1495,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
byte bd = (byte)((diff >> 0) & 0xff); byte bd = (byte)((diff >> 0) & 0xff);
if (rd != 0x00) if (rd != 0x00)
{ {
signFound |= (byte)((rd < 0x80) ? 1 : 2); signFound |= (byte)(rd < 0x80 ? 1 : 2);
} }
if (gd != 0x00) if (gd != 0x00)
{ {
signFound |= (byte)((gd < 0x80) ? 8 : 16); signFound |= (byte)(gd < 0x80 ? 8 : 16);
} }
if (bd != 0x00) if (bd != 0x00)
{ {
signFound |= (byte)((bd < 0x80) ? 64 : 128); signFound |= (byte)(bd < 0x80 ? 64 : 128);
} }
} }
@ -1555,8 +1555,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
for (int k = 0; k < 5; k++) for (int k = 0; k < 5; k++)
{ {
int numSymbols = int numSymbols =
(k == 0) ? histo.NumCodes() : k == 0 ? histo.NumCodes() :
(k == 4) ? WebpConstants.NumDistanceCodes : 256; k == 4 ? WebpConstants.NumDistanceCodes : 256;
huffmanCodes[startIdx + k].NumSymbols = numSymbols; huffmanCodes[startIdx + k].NumSymbols = numSymbols;
} }
} }
@ -1631,8 +1631,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
histoBits++; histoBits++;
} }
return (histoBits < WebpConstants.MinHuffmanBits) ? WebpConstants.MinHuffmanBits : return histoBits < WebpConstants.MinHuffmanBits ? WebpConstants.MinHuffmanBits :
(histoBits > WebpConstants.MaxHuffmanBits) ? WebpConstants.MaxHuffmanBits : histoBits; histoBits > WebpConstants.MaxHuffmanBits ? WebpConstants.MaxHuffmanBits : histoBits;
} }
/// <summary> /// <summary>
@ -1681,8 +1681,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetTransformBits(int method, int histoBits) private static int GetTransformBits(int method, int histoBits)
{ {
int maxTransformBits = (method < 4) ? 6 : (method > 4) ? 4 : 5; int maxTransformBits = method < 4 ? 6 : method > 4 ? 4 : 5;
int res = (histoBits > maxTransformBits) ? maxTransformBits : histoBits; int res = histoBits > maxTransformBits ? maxTransformBits : histoBits;
return res; return res;
} }
@ -1728,10 +1728,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint ApplyPaletteHash0(uint color) => (color >> 8) & 0xff; // Focus on the green color. private static uint ApplyPaletteHash0(uint color) => (color >> 8) & 0xff; // Focus on the green color.
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint ApplyPaletteHash1(uint color) => ((uint)((color & 0x00ffffffu) * 4222244071ul)) >> (32 - PaletteInvSizeBits); // Forget about alpha. private static uint ApplyPaletteHash1(uint color) => (uint)((color & 0x00ffffffu) * 4222244071ul) >> (32 - PaletteInvSizeBits); // Forget about alpha.
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint ApplyPaletteHash2(uint color) => ((uint)((color & 0x00ffffffu) * ((1ul << 31) - 1))) >> (32 - PaletteInvSizeBits); // Forget about alpha. private static uint ApplyPaletteHash2(uint color) => (uint)((color & 0x00ffffffu) * ((1ul << 31) - 1)) >> (32 - PaletteInvSizeBits); // Forget about alpha.
// Note that masking with 0xffffffffu is for preventing an // Note that masking with 0xffffffffu is for preventing an
// 'unsigned int overflow' warning. Doesn't impact the compiled code. // 'unsigned int overflow' warning. Doesn't impact the compiled code.
@ -1739,7 +1739,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint HashPix(uint pix) => (uint)((((long)pix + (pix >> 19)) * 0x39c5fba7L) & 0xffffffffu) >> 24; private static uint HashPix(uint pix) => (uint)((((long)pix + (pix >> 19)) * 0x39c5fba7L) & 0xffffffffu) >> 24;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int PaletteCompareColorsForSort(uint p1, uint p2) => (p1 < p2) ? -1 : 1; private static int PaletteCompareColorsForSort(uint p1, uint p2) => p1 < p2 ? -1 : 1;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint PaletteComponentDistance(uint v) => (v <= 128) ? v : (256 - v); private static uint PaletteComponentDistance(uint v) => (v <= 128) ? v : (256 - v);
@ -1749,7 +1749,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
// pixel in each, plus 2 regular scanlines of bytes. // pixel in each, plus 2 regular scanlines of bytes.
int bgraScratchSize = this.UsePredictorTransform ? ((width + 1) * 2) + (((width * 2) + 4 - 1) / 4) : 0; int bgraScratchSize = this.UsePredictorTransform ? ((width + 1) * 2) + (((width * 2) + 4 - 1) / 4) : 0;
int transformDataSize = (this.UsePredictorTransform || this.UseCrossColorTransform) ? LosslessUtils.SubSampleSize(width, this.TransformBits) * LosslessUtils.SubSampleSize(height, this.TransformBits) : 0; int transformDataSize = this.UsePredictorTransform || this.UseCrossColorTransform ? LosslessUtils.SubSampleSize(width, this.TransformBits) * LosslessUtils.SubSampleSize(height, this.TransformBits) : 0;
this.BgraScratch = this.memoryAllocator.Allocate<uint>(bgraScratchSize); this.BgraScratch = this.memoryAllocator.Allocate<uint>(bgraScratchSize);
this.TransformData = this.memoryAllocator.Allocate<uint>(transformDataSize); this.TransformData = this.memoryAllocator.Allocate<uint>(transformDataSize);

14
src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs

@ -142,8 +142,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int iter = iterMax; int iter = iterMax;
int bestLength = 0; int bestLength = 0;
uint bestDistance = 0; uint bestDistance = 0;
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;
pos = chain[basePosition]; pos = chain[basePosition];
int currLength; int currLength;
@ -273,12 +273,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetWindowSizeForHashChain(int quality, int xSize) private static int GetWindowSizeForHashChain(int quality, int xSize)
{ {
int maxWindowSize = (quality > 75) ? WindowSize int maxWindowSize = quality > 75 ? WindowSize
: (quality > 50) ? (xSize << 8) : quality > 50 ? xSize << 8
: (quality > 25) ? (xSize << 6) : quality > 25 ? xSize << 6
: (xSize << 4); : xSize << 4;
return (maxWindowSize > WindowSize) ? WindowSize : maxWindowSize; return maxWindowSize > WindowSize ? WindowSize : maxWindowSize;
} }
} }
} }

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

@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
public int NumCodes() => WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + ((this.PaletteCodeBits > 0) ? (1 << this.PaletteCodeBits) : 0); public int NumCodes() => WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (this.PaletteCodeBits > 0 ? 1 << this.PaletteCodeBits : 0);
/// <summary> /// <summary>
/// Estimate how many bits the combined entropy of literals and distance approximately maps to. /// Estimate how many bits the combined entropy of literals and distance approximately maps to.
@ -234,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
output.IsUsed[i] = this.IsUsed[i] | b.IsUsed[i]; output.IsUsed[i] = this.IsUsed[i] | b.IsUsed[i];
} }
output.TrivialSymbol = (this.TrivialSymbol == b.TrivialSymbol) output.TrivialSymbol = this.TrivialSymbol == b.TrivialSymbol
? this.TrivialSymbol ? this.TrivialSymbol
: NonTrivialSym; : NonTrivialSym;
} }

51
src/ImageSharp/Formats/WebP/Lossless/WebpLosslessDecoder.cs

@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum. // 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. // That is why 11 bits is also considered valid here.
bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= (WebpConstants.MaxColorCacheBits + 1); 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");
@ -426,7 +426,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int totalSize = 0; int totalSize = 0;
bool isTrivialLiteral = true; bool isTrivialLiteral = true;
int maxBits = 0; int maxBits = 0;
var codeLengths = new int[maxAlphabetSize]; int[] codeLengths = new int[maxAlphabetSize];
for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++) for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
{ {
int alphabetSize = WebpConstants.AlphabetSize[j]; int alphabetSize = WebpConstants.AlphabetSize[j];
@ -459,7 +459,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int k; int k;
for (k = 1; k < alphabetSize; ++k) for (k = 1; k < alphabetSize; ++k)
{ {
var codeLengthK = codeLengths[k]; int codeLengthK = codeLengths[k];
if (codeLengthK > localMaxBits) if (codeLengthK > localMaxBits)
{ {
localMaxBits = codeLengthK; localMaxBits = codeLengthK;
@ -517,7 +517,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint firstSymbolLenCode = this.bitReader.ReadValue(1); uint firstSymbolLenCode = this.bitReader.ReadValue(1);
// The first code is either 1 bit or 8 bit code. // The first code is either 1 bit or 8 bit code.
uint symbol = this.bitReader.ReadValue((firstSymbolLenCode == 0) ? 1 : 8); uint symbol = this.bitReader.ReadValue(firstSymbolLenCode == 0 ? 1 : 8);
codeLengths[symbol] = 1; codeLengths[symbol] = 1;
// The second code (if present), is always 8 bit long. // The second code (if present), is always 8 bit long.
@ -532,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// (ii) Normal Code Length Code: // (ii) Normal Code Length Code:
// The code lengths of a Huffman code are read as follows: num_code_lengths specifies the number of code lengths; // The code lengths of a Huffman code are read as follows: num_code_lengths specifies the number of code lengths;
// the rest of the code lengths (according to the order in kCodeLengthCodeOrder) are zeros. // the rest of the code lengths (according to the order in kCodeLengthCodeOrder) are zeros.
var codeLengthCodeLengths = new int[NumCodeLengthCodes]; int[] codeLengthCodeLengths = new int[NumCodeLengthCodes];
uint numCodes = this.bitReader.ReadValue(4) + 4; uint numCodes = this.bitReader.ReadValue(4) + 4;
if (numCodes > NumCodeLengthCodes) if (numCodes > NumCodeLengthCodes)
{ {
@ -644,9 +644,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// The transform data contains color table size and the entries in the color table. // The transform data contains color table size and the entries in the color table.
// 8 bit value for color table size. // 8 bit value for color table size.
uint numColors = this.bitReader.ReadValue(8) + 1; uint numColors = this.bitReader.ReadValue(8) + 1;
int bits = (numColors > 16) ? 0 int bits = numColors > 16 ? 0
: (numColors > 4) ? 1 : numColors > 4 ? 1
: (numColors > 2) ? 2 : numColors > 2 ? 2
: 3; : 3;
transform.Bits = bits; transform.Bits = bits;
using (IMemoryOwner<uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false)) using (IMemoryOwner<uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
@ -661,15 +661,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform: case Vp8LTransformType.CrossColorTransform:
{ {
// The first 3 bits of prediction data define the block width and height in number of bits. // The first 3 bits of prediction data define the block width and height in number of bits.
transform.Bits = (int)this.bitReader.ReadValue(3) + 2; transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits); int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits); int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false); IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
transform.Data = transformData; transform.Data = transformData;
break; break;
} }
} }
decoder.Transforms.Add(transform); decoder.Transforms.Add(transform);
@ -732,7 +732,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int lastRow = height; int lastRow = height;
const int lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes; const int lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes;
int mask = hdr.HuffmanMask; int mask = hdr.HuffmanMask;
HTreeGroup[] htreeGroup = (pos < last) ? GetHTreeGroupForPos(hdr, col, row) : null; HTreeGroup[] htreeGroup = pos < last ? GetHTreeGroupForPos(hdr, col, row) : null;
while (!this.bitReader.Eos && pos < last) while (!this.bitReader.Eos && pos < last)
{ {
// Only update when changing tile. // Only update when changing tile.
@ -754,7 +754,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
col = 0; col = 0;
++row; ++row;
if (row <= lastRow && (row % WebpConstants.NumArgbCacheRows == 0)) if (row <= lastRow && row % WebpConstants.NumArgbCacheRows == 0)
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -784,7 +784,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
col -= width; col -= width;
++row; ++row;
if (row <= lastRow && (row % WebpConstants.NumArgbCacheRows == 0)) if (row <= lastRow && row % WebpConstants.NumArgbCacheRows == 0)
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -813,7 +813,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
decoder.Width = width; decoder.Width = width;
decoder.Height = height; decoder.Height = height;
decoder.Metadata.HuffmanXSize = LosslessUtils.SubSampleSize(width, numBits); decoder.Metadata.HuffmanXSize = LosslessUtils.SubSampleSize(width, numBits);
decoder.Metadata.HuffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1; decoder.Metadata.HuffmanMask = numBits == 0 ? ~0 : (1 << numBits) - 1;
} }
private uint ReadPackedSymbols(HTreeGroup[] group, Span<uint> pixelData, int decodedPixels) private uint ReadPackedSymbols(HTreeGroup[] group, Span<uint> pixelData, int decodedPixels)
@ -879,11 +879,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private int GetCopyLength(int lengthSymbol) private int GetCopyLength(int lengthSymbol) =>
{ this.GetCopyDistance(lengthSymbol); // Length and distance prefixes are encoded the same way.
// Length and distance prefixes are encoded the same way.
return this.GetCopyDistance(lengthSymbol);
}
private int GetCopyDistance(int distanceSymbol) private int GetCopyDistance(int distanceSymbol)
{ {
@ -930,7 +927,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int dist = (yOffset * xSize) + xOffset; int dist = (yOffset * xSize) + xOffset;
// dist < 1 can happen if xSize is very small. // dist < 1 can happen if xSize is very small.
return (dist >= 1) ? dist : 1; return dist >= 1 ? dist : 1;
} }
/// <summary> /// <summary>

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

@ -506,7 +506,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary> /// </summary>
public static void TransformWht(Span<short> input, Span<short> output) public static void TransformWht(Span<short> input, Span<short> output)
{ {
var tmp = new int[16]; int[] tmp = new int[16];
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
int iPlus4 = 4 + i; int iPlus4 = 4 + i;
@ -732,7 +732,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int k = 3; k > 0; --k) for (int k = 3; k > 0; --k)
{ {
offset += 4 * stride; offset += 4 * stride;
SimpleVFilter16(p, offset, stride, thresh); SimpleVFilter16(p, offset, stride, thresh);
} }
} }
@ -833,7 +833,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void Dst(Span<byte> dst, int x, int y, byte v) => dst[x + (y * WebpConstants.Bps)] = v; public static void Dst(Span<byte> dst, int x, int y, byte v) => dst[x + (y * WebpConstants.Bps)] = v;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static byte Clip8B(int v) => (byte)((v & ~0xff) == 0 ? v : (v < 0) ? 0 : 255); public static byte Clip8B(int v) => (byte)((v & ~0xff) == 0 ? v : v < 0 ? 0 : 255);
// Cost of coding one event with probability 'proba'. // Cost of coding one event with probability 'proba'.
public static int Vp8BitCost(int bit, byte proba) => bit == 0 ? WebpLookupTables.Vp8EntropyCost[proba] : WebpLookupTables.Vp8EntropyCost[255 - proba]; public static int Vp8BitCost(int bit, byte proba) => bit == 0 ? WebpLookupTables.Vp8EntropyCost[proba] : WebpLookupTables.Vp8EntropyCost[255 - proba];
@ -882,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
while (size-- > 0) while (size-- > 0)
{ {
if (NeedsFilter2(p, offset, hStride, thresh2, ithresh)) if (NeedsFilter2(p, offset, hStride, thresh2, ithresh))
{ {
if (Hev(p, offset, hStride, hevThresh)) if (Hev(p, offset, hStride, hevThresh))
{ {
@ -992,7 +992,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int p0 = p[offset - step]; int p0 = p[offset - step];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
return ((4 * WebpLookupTables.Abs0[p0 - q0]) + WebpLookupTables.Abs0[p1 - q1]) <= t; return (4 * WebpLookupTables.Abs0[p0 - q0]) + WebpLookupTables.Abs0[p1 - q1] <= t;
} }
private static bool NeedsFilter2(Span<byte> p, int offset, int step, int t, int it) private static bool NeedsFilter2(Span<byte> p, int offset, int step, int t, int it)
@ -1007,7 +1007,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int q1 = p[offset + step]; int q1 = p[offset + step];
int q2 = p[offset + step2]; int q2 = p[offset + step2];
int q3 = p[offset + step3]; int q3 = p[offset + step3];
if (((4 * WebpLookupTables.Abs0[p0 - q0]) + WebpLookupTables.Abs0[p1 - q1]) > t) if ((4 * WebpLookupTables.Abs0[p0 - q0]) + WebpLookupTables.Abs0[p1 - q1] > t)
{ {
return false; return false;
} }
@ -1024,7 +1024,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int p0 = p[offset - step]; int p0 = p[offset - step];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
return (WebpLookupTables.Abs0[p1 - p0] > thresh) || (WebpLookupTables.Abs0[q1 - q0] > thresh); return WebpLookupTables.Abs0[p1 - p0] > thresh || WebpLookupTables.Abs0[q1 - q0] > thresh;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -1056,7 +1056,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static byte Clip8(int v) private static byte Clip8(int v)
{ {
int yuvMask = (256 << 6) - 1; int yuvMask = (256 << 6) - 1;
return (byte)(((v & ~yuvMask) == 0) ? (v >> 6) : (v < 0) ? 0 : 255); return (byte)((v & ~yuvMask) == 0 ? v >> 6 : v < 0 ? 0 : 255);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -1081,6 +1081,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Clamp255(int x) => x < 0 ? 0 : (x > 255 ? 255 : x); private static int Clamp255(int x) => x < 0 ? 0 : x > 255 ? 255 : x;
} }
} }

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Q = Numerics.Clamp(quality, qMin, qMax); this.Q = Numerics.Clamp(quality, qMin, qMax);
this.LastQ = this.Q; this.LastQ = this.Q;
this.Target = doSizeSearch ? targetSize this.Target = doSizeSearch ? targetSize
: (targetPsnr > 0.0f) ? targetPsnr : targetPsnr > 0.0f ? targetPsnr
: 40.0f; // default, just in case : 40.0f; // default, just in case
this.Value = 0.0f; this.Value = 0.0f;
this.LastValue = 0.0f; this.LastValue = 0.0f;
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
float dq; float dq;
if (this.IsFirst) if (this.IsFirst)
{ {
dq = (this.Value > this.Target) ? -this.Dq : this.Dq; dq = this.Value > this.Target ? -this.Dq : this.Dq;
this.IsFirst = false; this.IsFirst = false;
} }
else if (this.Value != this.LastValue) else if (this.Value != this.LastValue)

8
src/ImageSharp/Formats/WebP/Lossy/QuantEnc.cs

@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
long bestScore = Vp8ModeScore.MaxCost; long bestScore = Vp8ModeScore.MaxCost;
int nz = 0; int nz = 0;
int mode; int mode;
bool isI16 = tryBothModes || (it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16); bool isI16 = tryBothModes || it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment];
// Some empiric constants, of approximate order of magnitude. // Some empiric constants, of approximate order of magnitude.
@ -389,7 +389,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Avoid starting a checkerboard resonance from the border. See bug #432 of libwebp. // Avoid starting a checkerboard resonance from the border. See bug #432 of libwebp.
if (IsFlatSource16(src)) if (IsFlatSource16(src))
{ {
bestMode = (it.X == 0) ? 0 : 2; bestMode = it.X == 0 ? 0 : 2;
tryBothModes = false; // Stick to i16. tryBothModes = false; // Stick to i16.
} }
} }
@ -530,7 +530,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
return (last >= 0) ? 1 : 0; return last >= 0 ? 1 : 0;
} }
// Quantize as usual, but also compute and return the quantization error. // Quantize as usual, but also compute and return the quantization error.
@ -615,7 +615,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int i = 1; i < 16; ++i) for (int i = 1; i < 16; ++i)
{ {
// omit DC, we're only interested in AC // omit DC, we're only interested in AC
score += (levels[i] != 0) ? 1 : 0; score += levels[i] != 0 ? 1 : 0;
if (score > thresh) if (score > thresh)
{ {
return false; return false;

8
src/ImageSharp/Formats/WebP/Lossy/Vp8Decoder.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int extraRows = WebpConstants.FilterExtraRows[(int)LoopFilter.Complex]; // assuming worst case: complex filter int extraRows = WebpConstants.FilterExtraRows[(int)LoopFilter.Complex]; // assuming worst case: complex filter
int extraY = extraRows * this.CacheYStride; int extraY = extraRows * this.CacheYStride;
int extraUv = (extraRows / 2) * this.CacheUvStride; int extraUv = extraRows / 2 * this.CacheUvStride;
this.YuvBuffer = memoryAllocator.Allocate<byte>((WebpConstants.Bps * 17) + (WebpConstants.Bps * 9) + extraY); this.YuvBuffer = memoryAllocator.Allocate<byte>((WebpConstants.Bps * 17) + (WebpConstants.Bps * 9) + extraY);
this.CacheY = memoryAllocator.Allocate<byte>((16 * this.CacheYStride) + extraY); this.CacheY = memoryAllocator.Allocate<byte>((16 * this.CacheYStride) + extraY);
int cacheUvSize = (16 * this.CacheUvStride) + extraUv; int cacheUvSize = (16 * this.CacheUvStride) + extraUv;
@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary> /// <summary>
/// Gets the contextual macroblock info. /// Gets the contextual macroblock info.
/// </summary> /// </summary>
public Vp8MacroBlock[] MacroBlockInfo { get; } public Vp8MacroBlock[] MacroBlockInfo { get; }
/// <summary> /// <summary>
/// Gets or sets the loop filter used. The purpose of the loop filter is to eliminate (or at least reduce) /// Gets or sets the loop filter used. The purpose of the loop filter is to eliminate (or at least reduce)
@ -284,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
level = (level < 0) ? 0 : (level > 63) ? 63 : level; level = level < 0 ? 0 : level > 63 ? 63 : level;
if (level > 0) if (level > 0)
{ {
int iLevel = level; int iLevel = level;
@ -313,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
info.InnerLevel = (byte)iLevel; info.InnerLevel = (byte)iLevel;
info.Limit = (byte)((2 * level) + iLevel); info.Limit = (byte)((2 * level) + iLevel);
info.HighEdgeVarianceThreshold = (byte)((level >= 40) ? 2 : (level >= 15) ? 1 : 0); info.HighEdgeVarianceThreshold = (byte)(level >= 40 ? 2 : level >= 15 ? 1 : 0);
} }
else else
{ {

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

@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int q = quality; int q = quality;
int kThreshold = 8 + ((17 - 8) * q / 100); int kThreshold = 8 + ((17 - 8) * q / 100);
int k; int k;
var dc = new uint[16]; uint[] dc = new uint[16];
uint m; uint m;
uint m2; uint m2;
for (k = 0; k < 16; k += 4) for (k = 0; k < 16; k += 4)
@ -366,7 +366,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
else else
{ {
var modes = new byte[16]; // DC4 byte[] modes = new byte[16]; // DC4
this.SetIntra4Mode(modes); this.SetIntra4Mode(modes);
} }
@ -418,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
histos[curHisto] = new Vp8Histogram(); histos[curHisto] = new Vp8Histogram();
histos[curHisto].CollectHistogram(src, this.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]), 0, 1); histos[curHisto].CollectHistogram(src, this.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]), 0, 1);
var alpha = histos[curHisto].GetAlpha(); int alpha = histos[curHisto].GetAlpha();
if (alpha > bestModeAlpha) if (alpha > bestModeAlpha)
{ {
bestModeAlpha = alpha; bestModeAlpha = alpha;
@ -523,7 +523,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int ctx = this.TopNz[x] + this.LeftNz[y]; int ctx = this.TopNz[x] + this.LeftNz[y];
res.SetCoeffs(rd.YAcLevels.AsSpan((x + (y * 4)) * 16, 16)); res.SetCoeffs(rd.YAcLevels.AsSpan((x + (y * 4)) * 16, 16));
r += res.GetResidualCost(ctx); r += res.GetResidualCost(ctx);
this.TopNz[x] = this.LeftNz[y] = (res.Last >= 0) ? 1 : 0; this.TopNz[x] = this.LeftNz[y] = res.Last >= 0 ? 1 : 0;
} }
} }
@ -536,8 +536,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int predIdx = this.predIdx; int predIdx = this.predIdx;
int x = this.I4 & 3; int x = this.I4 & 3;
int y = this.I4 >> 2; int y = this.I4 >> 2;
int left = (x == 0) ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1]; int left = x == 0 ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1];
int top = (y == 0) ? this.Preds[predIdx - predsWidth + x] : modes[this.I4 - 4]; int top = y == 0 ? this.Preds[predIdx - predsWidth + x] : modes[this.I4 - 4];
return WebpLookupTables.Vp8FixedCostsI4[top, left]; return WebpLookupTables.Vp8FixedCostsI4[top, left];
} }
@ -573,7 +573,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int ctx = this.TopNz[4 + ch + x] + this.LeftNz[4 + ch + y]; int ctx = this.TopNz[4 + ch + x] + this.LeftNz[4 + ch + y];
res.SetCoeffs(rd.UvLevels.AsSpan(((ch * 2) + x + (y * 2)) * 16, 16)); res.SetCoeffs(rd.UvLevels.AsSpan(((ch * 2) + x + (y * 2)) * 16, 16));
r += res.GetResidualCost(ctx); r += res.GetResidualCost(ctx);
this.TopNz[4 + ch + x] = this.LeftNz[4 + ch + y] = (res.Last >= 0) ? 1 : 0; this.TopNz[4 + ch + x] = this.LeftNz[4 + ch + y] = res.Last >= 0 ? 1 : 0;
} }
} }
} }
@ -910,7 +910,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> yLeft = this.YLeft.AsSpan(); Span<byte> yLeft = this.YLeft.AsSpan();
Span<byte> uLeft = this.UvLeft.AsSpan(0, 16); Span<byte> uLeft = this.UvLeft.AsSpan(0, 16);
Span<byte> vLeft = this.UvLeft.AsSpan(16, 16); Span<byte> vLeft = this.UvLeft.AsSpan(16, 16);
byte val = (byte)((this.Y > 0) ? 129 : 127); byte val = (byte)(this.Y > 0 ? 129 : 127);
yLeft[0] = val; yLeft[0] = val;
uLeft[0] = val; uLeft[0] = val;
vLeft[0] = val; vLeft[0] = val;

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

@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx]; Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx];
Vp8CostArray table = this.LevelCost[ctype][band].Costs[ctx]; Vp8CostArray table = this.LevelCost[ctype][band].Costs[ctx];
int cost0 = (ctx > 0) ? LossyUtils.Vp8BitCost(1, p.Probabilities[0]) : 0; int cost0 = ctx > 0 ? LossyUtils.Vp8BitCost(1, p.Probabilities[0]) : 0;
int costBase = LossyUtils.Vp8BitCost(1, p.Probabilities[1]) + cost0; int costBase = LossyUtils.Vp8BitCost(1, p.Probabilities[1]) + cost0;
int v; int v;
table.Costs[0] = (ushort)(LossyUtils.Vp8BitCost(0, p.Probabilities[1]) + cost0); table.Costs[0] = (ushort)(LossyUtils.Vp8BitCost(0, p.Probabilities[1]) + cost0);

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

@ -99,6 +99,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param> /// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
/// <param name="entropyPasses">Number of entropy-analysis passes (in [1..10]).</param> /// <param name="entropyPasses">Number of entropy-analysis passes (in [1..10]).</param>
/// <param name="filterStrength">The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).</param> /// <param name="filterStrength">The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).</param>
/// <param name="spatialNoiseShaping">The spatial noise shaping. 0=off, 100=maximum.</param>
public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses, int filterStrength, int spatialNoiseShaping) public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses, int filterStrength, int spatialNoiseShaping)
{ {
this.memoryAllocator = memoryAllocator; this.memoryAllocator = memoryAllocator;
@ -110,9 +111,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10); this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10);
this.filterStrength = Numerics.Clamp(filterStrength, 0, 100); this.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100); this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
this.rdOptLevel = (method >= 6) ? Vp8RdLevel.RdOptTrellisAll this.rdOptLevel = method >= 6 ? Vp8RdLevel.RdOptTrellisAll
: (method >= 5) ? Vp8RdLevel.RdOptTrellis : method >= 5 ? Vp8RdLevel.RdOptTrellis
: (method >= 3) ? Vp8RdLevel.RdOptBasic : method >= 3 ? Vp8RdLevel.RdOptBasic
: Vp8RdLevel.RdOptNone; : Vp8RdLevel.RdOptNone;
int pixelCount = width * height; int pixelCount = width * height;
@ -362,7 +363,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
bool doSearch = targetSize > 0 || targetPsnr > 0; bool doSearch = targetSize > 0 || targetPsnr > 0;
bool fastProbe = (this.method == 0 || this.method == 3) && !doSearch; bool fastProbe = (this.method == 0 || this.method == 3) && !doSearch;
int numPassLeft = this.entropyPasses; int numPassLeft = this.entropyPasses;
Vp8RdLevel rdOpt = (this.method >= 3 || doSearch) ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone; Vp8RdLevel rdOpt = this.method >= 3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
int nbMbs = this.Mbw * this.Mbh; int nbMbs = this.Mbw * this.Mbh;
var stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality); var stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality);
@ -374,11 +375,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (this.method == 3) if (this.method == 3)
{ {
// We need more stats for method 3 to be reliable. // We need more stats for method 3 to be reliable.
nbMbs = (nbMbs > 200) ? nbMbs >> 1 : 100; nbMbs = nbMbs > 200 ? nbMbs >> 1 : 100;
} }
else else
{ {
nbMbs = (nbMbs > 200) ? nbMbs >> 2 : 50; nbMbs = nbMbs > 200 ? nbMbs >> 2 : 50;
} }
} }
@ -536,7 +537,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Simplified k-Means, to assign Nb segments based on alpha-histogram. // Simplified k-Means, to assign Nb segments based on alpha-histogram.
private void AssignSegments(int[] alphas) private void AssignSegments(int[] alphas)
{ {
int nb = (this.SegmentHeader.NumSegments < NumMbSegments) ? this.SegmentHeader.NumSegments : NumMbSegments; int nb = this.SegmentHeader.NumSegments < NumMbSegments ? this.SegmentHeader.NumSegments : NumMbSegments;
int[] centers = new int[NumMbSegments]; int[] centers = new int[NumMbSegments];
int weightedAverage = 0; int weightedAverage = 0;
int[] map = new int[WebpConstants.MaxAlpha + 1]; int[] map = new int[WebpConstants.MaxAlpha + 1];
@ -727,7 +728,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Segments with lower complexity ('beta') will be less filtered. // Segments with lower complexity ('beta') will be less filtered.
int f = baseStrength * level0 / (256 + m.Beta); int f = baseStrength * level0 / (256 + m.Beta);
m.FStrength = (f < WebpConstants.FilterStrengthCutoff) ? 0 : (f > 63) ? 63 : f; m.FStrength = f < WebpConstants.FilterStrengthCutoff ? 0 : f > 63 ? 63 : f;
} }
// We record the initial strength (mainly for the case of 1-segment only). // We record the initial strength (mainly for the case of 1-segment only).
@ -754,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
probas[1] = (byte)GetProba(p[0], p[1]); probas[1] = (byte)GetProba(p[0], p[1]);
probas[2] = (byte)GetProba(p[2], p[3]); probas[2] = (byte)GetProba(p[2], p[3]);
this.SegmentHeader.UpdateMap = (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255); this.SegmentHeader.UpdateMap = probas[0] != 255 || probas[1] != 255 || probas[2] != 255;
if (!this.SegmentHeader.UpdateMap) if (!this.SegmentHeader.UpdateMap)
{ {
this.ResetSegments(); this.ResetSegments();
@ -969,7 +970,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int ctx = it.TopNz[4 + ch + x] + it.LeftNz[4 + ch + y]; int ctx = it.TopNz[4 + ch + x] + it.LeftNz[4 + ch + y];
residual.SetCoeffs(rd.UvLevels.AsSpan(16 * ((ch * 2) + x + (y * 2)), 16)); residual.SetCoeffs(rd.UvLevels.AsSpan(16 * ((ch * 2) + x + (y * 2)), 16));
var res = this.bitWriter.PutCoeffs(ctx, residual); int res = this.bitWriter.PutCoeffs(ctx, residual);
it.TopNz[4 + ch + x] = it.LeftNz[4 + ch + y] = res; it.TopNz[4 + ch + x] = it.LeftNz[4 + ch + y] = res;
} }
} }
@ -1018,7 +1019,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int ctx = it.TopNz[x] + it.LeftNz[y]; int ctx = it.TopNz[x] + it.LeftNz[y];
Span<short> coeffs = rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16); Span<short> coeffs = rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16);
residual.SetCoeffs(coeffs); residual.SetCoeffs(coeffs);
var res = residual.RecordCoeffs(ctx); int res = residual.RecordCoeffs(ctx);
it.TopNz[x] = res; it.TopNz[x] = res;
it.LeftNz[y] = res; it.LeftNz[y] = res;
} }
@ -1059,7 +1060,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static double QualityToCompression(double c) private static double QualityToCompression(double c)
{ {
double linearC = (c < 0.75) ? c * (2.0d / 3.0d) : (2.0d * c) - 1.0d; double linearC = c < 0.75 ? c * (2.0d / 3.0d) : (2.0d * c) - 1.0d;
// The file size roughly scales as pow(quantizer, 3.). Actually, the // The file size roughly scales as pow(quantizer, 3.). Actually, the
// exponent is somewhere between 2.8 and 3.2, but we're mostly interested // exponent is somewhere between 2.8 and 3.2, but we're mostly interested
@ -1075,18 +1076,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private int FilterStrengthFromDelta(int sharpness, int delta) private int FilterStrengthFromDelta(int sharpness, int delta)
{ {
int pos = (delta < WebpConstants.MaxDelzaSize) ? delta : WebpConstants.MaxDelzaSize - 1; int pos = delta < WebpConstants.MaxDelzaSize ? delta : WebpConstants.MaxDelzaSize - 1;
return WebpLookupTables.LevelsFromDelta[sharpness, pos]; return WebpLookupTables.LevelsFromDelta[sharpness, pos];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static double GetPsnr(long mse, long size) => (mse > 0 && size > 0) ? 10.0f * Math.Log10(255.0f * 255.0f * size / mse) : 99; private static double GetPsnr(long mse, long size) => mse > 0 && size > 0 ? 10.0f * Math.Log10(255.0f * 255.0f * size / mse) : 99;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetProba(int a, int b) private static int GetProba(int a, int b)
{ {
int total = a + b; int total = a + b;
return (total == 0) ? 255 // that's the default probability. return total == 0 ? 255 // that's the default probability.
: ((255 * a) + (total / 2)) / total; // rounded proba : ((255 * a) + (total / 2)) / total; // rounded proba
} }
} }

89
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoding.cs

@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int i; int i;
#pragma warning disable SA1312 // Variable names should begin with lower-case letter #pragma warning disable SA1312 // Variable names should begin with lower-case letter
var C = new int[4 * 4]; int[] C = new int[4 * 4];
#pragma warning restore SA1312 // Variable names should begin with lower-case letter #pragma warning restore SA1312 // Variable names should begin with lower-case letter
Span<int> tmp = C.AsSpan(); Span<int> tmp = C.AsSpan();
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void FTransform(Span<byte> src, Span<byte> reference, Span<short> output) public static void FTransform(Span<byte> src, Span<byte> reference, Span<short> output)
{ {
int i; int i;
var tmp = new int[16]; int[] tmp = new int[16];
int srcIdx = 0; int srcIdx = 0;
int refIdx = 0; int refIdx = 0;
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void FTransformWht(Span<short> input, Span<short> output) public static void FTransformWht(Span<short> input, Span<short> output)
{ {
var tmp = new int[16]; int[] tmp = new int[16];
int i; int i;
int inputIdx = 0; int inputIdx = 0;
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
@ -299,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
else else
{ {
Vp8Encoding.HorizontalPred(dst, left, size); HorizontalPred(dst, left, size);
} }
} }
else else
@ -310,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// then 129, and not 127 as in the VerticalPred case. // then 129, and not 127 as in the VerticalPred case.
if (top != null) if (top != null)
{ {
Vp8Encoding.VerticalPred(dst, top, size); VerticalPred(dst, top, size);
} }
else else
{ {
@ -444,23 +444,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte d = top[topOffset + 3]; byte d = top[topOffset + 3];
LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(j, k, l)); LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(j, k, l));
var ijk = LossyUtils.Avg3(i, j, k); byte ijk = LossyUtils.Avg3(i, j, k);
LossyUtils.Dst(dst, 0, 2, ijk); LossyUtils.Dst(dst, 0, 2, ijk);
LossyUtils.Dst(dst, 1, 3, ijk); LossyUtils.Dst(dst, 1, 3, ijk);
var xij = LossyUtils.Avg3(x, i, j); byte xij = LossyUtils.Avg3(x, i, j);
LossyUtils.Dst(dst, 0, 1, xij); LossyUtils.Dst(dst, 0, 1, xij);
LossyUtils.Dst(dst, 1, 2, xij); LossyUtils.Dst(dst, 1, 2, xij);
LossyUtils.Dst(dst, 2, 3, xij); LossyUtils.Dst(dst, 2, 3, xij);
var axi = LossyUtils.Avg3(a, x, i); byte axi = LossyUtils.Avg3(a, x, i);
LossyUtils.Dst(dst, 0, 0, axi); LossyUtils.Dst(dst, 0, 0, axi);
LossyUtils.Dst(dst, 1, 1, axi); LossyUtils.Dst(dst, 1, 1, axi);
LossyUtils.Dst(dst, 2, 2, axi); LossyUtils.Dst(dst, 2, 2, axi);
LossyUtils.Dst(dst, 3, 3, axi); LossyUtils.Dst(dst, 3, 3, axi);
var bax = LossyUtils.Avg3(b, a, x); byte bax = LossyUtils.Avg3(b, a, x);
LossyUtils.Dst(dst, 1, 0, bax); LossyUtils.Dst(dst, 1, 0, bax);
LossyUtils.Dst(dst, 2, 1, bax); LossyUtils.Dst(dst, 2, 1, bax);
LossyUtils.Dst(dst, 3, 2, bax); LossyUtils.Dst(dst, 3, 2, bax);
var cba = LossyUtils.Avg3(c, b, a); byte cba = LossyUtils.Avg3(c, b, a);
LossyUtils.Dst(dst, 2, 0, cba); LossyUtils.Dst(dst, 2, 0, cba);
LossyUtils.Dst(dst, 3, 1, cba); LossyUtils.Dst(dst, 3, 1, cba);
LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(d, c, b)); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(d, c, b));
@ -477,25 +477,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte c = top[topOffset + 2]; byte c = top[topOffset + 2];
byte d = top[topOffset + 3]; byte d = top[topOffset + 3];
var xa = LossyUtils.Avg2(x, a); byte xa = LossyUtils.Avg2(x, a);
LossyUtils.Dst(dst, 0, 0, xa); LossyUtils.Dst(dst, 0, 0, xa);
LossyUtils.Dst(dst, 1, 2, xa); LossyUtils.Dst(dst, 1, 2, xa);
var ab = LossyUtils.Avg2(a, b); byte ab = LossyUtils.Avg2(a, b);
LossyUtils.Dst(dst, 1, 0, ab); LossyUtils.Dst(dst, 1, 0, ab);
LossyUtils.Dst(dst, 2, 2, ab); LossyUtils.Dst(dst, 2, 2, ab);
var bc = LossyUtils.Avg2(b, c); byte bc = LossyUtils.Avg2(b, c);
LossyUtils.Dst(dst, 2, 0, bc); LossyUtils.Dst(dst, 2, 0, bc);
LossyUtils.Dst(dst, 3, 2, bc); LossyUtils.Dst(dst, 3, 2, bc);
LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg2(c, d)); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg2(c, d));
LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(k, j, i)); LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(k, j, i));
LossyUtils.Dst(dst, 0, 2, LossyUtils.Avg3(j, i, x)); LossyUtils.Dst(dst, 0, 2, LossyUtils.Avg3(j, i, x));
var ixa = LossyUtils.Avg3(i, x, a); byte ixa = LossyUtils.Avg3(i, x, a);
LossyUtils.Dst(dst, 0, 1, ixa); LossyUtils.Dst(dst, 0, 1, ixa);
LossyUtils.Dst(dst, 1, 3, ixa); LossyUtils.Dst(dst, 1, 3, ixa);
var xab = LossyUtils.Avg3(x, a, b); byte xab = LossyUtils.Avg3(x, a, b);
LossyUtils.Dst(dst, 1, 1, xab); LossyUtils.Dst(dst, 1, 1, xab);
LossyUtils.Dst(dst, 2, 3, xab); LossyUtils.Dst(dst, 2, 3, xab);
var abc = LossyUtils.Avg3(a, b, c); byte abc = LossyUtils.Avg3(a, b, c);
LossyUtils.Dst(dst, 2, 1, abc); LossyUtils.Dst(dst, 2, 1, abc);
LossyUtils.Dst(dst, 3, 3, abc); LossyUtils.Dst(dst, 3, 3, abc);
LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b, c, d)); LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b, c, d));
@ -513,23 +513,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte h = top[topOffset + 7]; byte h = top[topOffset + 7];
LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg3(a, b, c)); LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg3(a, b, c));
var bcd = LossyUtils.Avg3(b, c, d); byte bcd = LossyUtils.Avg3(b, c, d);
LossyUtils.Dst(dst, 1, 0, bcd); LossyUtils.Dst(dst, 1, 0, bcd);
LossyUtils.Dst(dst, 0, 1, bcd); LossyUtils.Dst(dst, 0, 1, bcd);
var cde = LossyUtils.Avg3(c, d, e); byte cde = LossyUtils.Avg3(c, d, e);
LossyUtils.Dst(dst, 2, 0, cde); LossyUtils.Dst(dst, 2, 0, cde);
LossyUtils.Dst(dst, 1, 1, cde); LossyUtils.Dst(dst, 1, 1, cde);
LossyUtils.Dst(dst, 0, 2, cde); LossyUtils.Dst(dst, 0, 2, cde);
var def = LossyUtils.Avg3(d, e, f); byte def = LossyUtils.Avg3(d, e, f);
LossyUtils.Dst(dst, 3, 0, def); LossyUtils.Dst(dst, 3, 0, def);
LossyUtils.Dst(dst, 2, 1, def); LossyUtils.Dst(dst, 2, 1, def);
LossyUtils.Dst(dst, 1, 2, def); LossyUtils.Dst(dst, 1, 2, def);
LossyUtils.Dst(dst, 0, 3, def); LossyUtils.Dst(dst, 0, 3, def);
var efg = LossyUtils.Avg3(e, f, g); byte efg = LossyUtils.Avg3(e, f, g);
LossyUtils.Dst(dst, 3, 1, efg); LossyUtils.Dst(dst, 3, 1, efg);
LossyUtils.Dst(dst, 2, 2, efg); LossyUtils.Dst(dst, 2, 2, efg);
LossyUtils.Dst(dst, 1, 3, efg); LossyUtils.Dst(dst, 1, 3, efg);
var fgh = LossyUtils.Avg3(f, g, h); byte fgh = LossyUtils.Avg3(f, g, h);
LossyUtils.Dst(dst, 3, 2, fgh); LossyUtils.Dst(dst, 3, 2, fgh);
LossyUtils.Dst(dst, 2, 3, fgh); LossyUtils.Dst(dst, 2, 3, fgh);
LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(g, h, h)); LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(g, h, h));
@ -547,23 +547,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte h = top[topOffset + 7]; byte h = top[topOffset + 7];
LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(a, b)); LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(a, b));
var bc = LossyUtils.Avg2(b, c); byte bc = LossyUtils.Avg2(b, c);
LossyUtils.Dst(dst, 1, 0, bc); LossyUtils.Dst(dst, 1, 0, bc);
LossyUtils.Dst(dst, 0, 2, bc); LossyUtils.Dst(dst, 0, 2, bc);
var cd = LossyUtils.Avg2(c, d); byte cd = LossyUtils.Avg2(c, d);
LossyUtils.Dst(dst, 2, 0, cd); LossyUtils.Dst(dst, 2, 0, cd);
LossyUtils.Dst(dst, 1, 2, cd); LossyUtils.Dst(dst, 1, 2, cd);
var de = LossyUtils.Avg2(d, e); byte de = LossyUtils.Avg2(d, e);
LossyUtils.Dst(dst, 3, 0, de); LossyUtils.Dst(dst, 3, 0, de);
LossyUtils.Dst(dst, 2, 2, de); LossyUtils.Dst(dst, 2, 2, de);
LossyUtils.Dst(dst, 0, 1, LossyUtils.Avg3(a, b, c)); LossyUtils.Dst(dst, 0, 1, LossyUtils.Avg3(a, b, c));
var bcd = LossyUtils.Avg3(b, c, d); byte bcd = LossyUtils.Avg3(b, c, d);
LossyUtils.Dst(dst, 1, 1, bcd); LossyUtils.Dst(dst, 1, 1, bcd);
LossyUtils.Dst(dst, 0, 3, bcd); LossyUtils.Dst(dst, 0, 3, bcd);
var cde = LossyUtils.Avg3(c, d, e); byte cde = LossyUtils.Avg3(c, d, e);
LossyUtils.Dst(dst, 2, 1, cde); LossyUtils.Dst(dst, 2, 1, cde);
LossyUtils.Dst(dst, 1, 3, cde); LossyUtils.Dst(dst, 1, 3, cde);
var def = LossyUtils.Avg3(d, e, f); byte def = LossyUtils.Avg3(d, e, f);
LossyUtils.Dst(dst, 3, 1, def); LossyUtils.Dst(dst, 3, 1, def);
LossyUtils.Dst(dst, 2, 3, def); LossyUtils.Dst(dst, 2, 3, def);
LossyUtils.Dst(dst, 3, 2, LossyUtils.Avg3(e, f, g)); LossyUtils.Dst(dst, 3, 2, LossyUtils.Avg3(e, f, g));
@ -581,25 +581,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte b = top[topOffset + 1]; byte b = top[topOffset + 1];
byte c = top[topOffset + 2]; byte c = top[topOffset + 2];
var ix = LossyUtils.Avg2(i, x); byte ix = LossyUtils.Avg2(i, x);
LossyUtils.Dst(dst, 0, 0, ix); LossyUtils.Dst(dst, 0, 0, ix);
LossyUtils.Dst(dst, 2, 1, ix); LossyUtils.Dst(dst, 2, 1, ix);
var ji = LossyUtils.Avg2(j, i); byte ji = LossyUtils.Avg2(j, i);
LossyUtils.Dst(dst, 0, 1, ji); LossyUtils.Dst(dst, 0, 1, ji);
LossyUtils.Dst(dst, 2, 2, ji); LossyUtils.Dst(dst, 2, 2, ji);
var kj = LossyUtils.Avg2(k, j); byte kj = LossyUtils.Avg2(k, j);
LossyUtils.Dst(dst, 0, 2, kj); LossyUtils.Dst(dst, 0, 2, kj);
LossyUtils.Dst(dst, 2, 3, kj); LossyUtils.Dst(dst, 2, 3, kj);
LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg2(l, k)); LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg2(l, k));
LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(a, b, c)); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(a, b, c));
LossyUtils.Dst(dst, 2, 0, LossyUtils.Avg3(x, a, b)); LossyUtils.Dst(dst, 2, 0, LossyUtils.Avg3(x, a, b));
var ixa = LossyUtils.Avg3(i, x, a); byte ixa = LossyUtils.Avg3(i, x, a);
LossyUtils.Dst(dst, 1, 0, ixa); LossyUtils.Dst(dst, 1, 0, ixa);
LossyUtils.Dst(dst, 3, 1, ixa); LossyUtils.Dst(dst, 3, 1, ixa);
var jix = LossyUtils.Avg3(j, i, x); byte jix = LossyUtils.Avg3(j, i, x);
LossyUtils.Dst(dst, 1, 1, jix); LossyUtils.Dst(dst, 1, 1, jix);
LossyUtils.Dst(dst, 3, 2, jix); LossyUtils.Dst(dst, 3, 2, jix);
var kji = LossyUtils.Avg3(k, j, i); byte kji = LossyUtils.Avg3(k, j, i);
LossyUtils.Dst(dst, 1, 2, kji); LossyUtils.Dst(dst, 1, 2, kji);
LossyUtils.Dst(dst, 3, 3, kji); LossyUtils.Dst(dst, 3, 3, kji);
LossyUtils.Dst(dst, 1, 3, LossyUtils.Avg3(l, k, j)); LossyUtils.Dst(dst, 1, 3, LossyUtils.Avg3(l, k, j));
@ -613,17 +613,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte l = top[topOffset - 5]; byte l = top[topOffset - 5];
LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(i, j)); LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(i, j));
var jk = LossyUtils.Avg2(j, k); byte jk = LossyUtils.Avg2(j, k);
LossyUtils.Dst(dst, 2, 0, jk); LossyUtils.Dst(dst, 2, 0, jk);
LossyUtils.Dst(dst, 0, 1, jk); LossyUtils.Dst(dst, 0, 1, jk);
var kl = LossyUtils.Avg2(k, l); byte kl = LossyUtils.Avg2(k, l);
LossyUtils.Dst(dst, 2, 1, kl); LossyUtils.Dst(dst, 2, 1, kl);
LossyUtils.Dst(dst, 0, 2, kl); LossyUtils.Dst(dst, 0, 2, kl);
LossyUtils.Dst(dst, 1, 0, LossyUtils.Avg3(i, j, k)); LossyUtils.Dst(dst, 1, 0, LossyUtils.Avg3(i, j, k));
var jkl = LossyUtils.Avg3(j, k, l); byte jkl = LossyUtils.Avg3(j, k, l);
LossyUtils.Dst(dst, 3, 0, jkl); LossyUtils.Dst(dst, 3, 0, jkl);
LossyUtils.Dst(dst, 1, 1, jkl); LossyUtils.Dst(dst, 1, 1, jkl);
var kll = LossyUtils.Avg3(k, l, l); byte kll = LossyUtils.Avg3(k, l, l);
LossyUtils.Dst(dst, 3, 1, kll); LossyUtils.Dst(dst, 3, 1, kll);
LossyUtils.Dst(dst, 1, 2, kll); LossyUtils.Dst(dst, 1, 2, kll);
LossyUtils.Dst(dst, 3, 2, l); LossyUtils.Dst(dst, 3, 2, l);
@ -644,21 +644,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8b(int v) private static byte Clip8b(int v) => (v & ~0xff) == 0 ? (byte)v : v < 0 ? (byte)0 : (byte)255;
{
return ((v & ~0xff) == 0) ? (byte)v : (v < 0) ? (byte)0 : (byte)255;
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v) private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v) => dst[x + (y * WebpConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebpConstants.Bps)] + (v >> 3));
{
dst[x + (y * WebpConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebpConstants.Bps)] + (v >> 3));
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Mul(int a, int b) private static int Mul(int a, int b) => (a * b) >> 16;
{
return (a * b) >> 16;
}
} }
} }

6
src/ImageSharp/Formats/WebP/Lossy/Vp8Histogram.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// for handling the useful small values which contribute most. // for handling the useful small values which contribute most.
int maxValue = this.maxValue; int maxValue = this.maxValue;
int lastNonZero = this.lastNonZero; int lastNonZero = this.lastNonZero;
int alpha = (maxValue > 1) ? WebpConstants.AlphaScale * lastNonZero / maxValue : 0; int alpha = maxValue > 1 ? WebpConstants.AlphaScale * lastNonZero / maxValue : 0;
return alpha; return alpha;
} }
@ -129,13 +129,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int a2 = tmp[4 + i] - tmp[8 + i]; int a2 = tmp[4 + i] - tmp[8 + i];
int a3 = tmp[0 + i] - tmp[12 + i]; int a3 = tmp[0 + i] - tmp[12 + i];
output[0 + i] = (short)((a0 + a1 + 7) >> 4); // 12b output[0 + i] = (short)((a0 + a1 + 7) >> 4); // 12b
output[4 + i] = (short)((((a2 * 2217) + (a3 * 5352) + 12000) >> 16) + ((a3 != 0) ? 1 : 0)); output[4 + i] = (short)((((a2 * 2217) + (a3 * 5352) + 12000) >> 16) + (a3 != 0 ? 1 : 0));
output[8 + i] = (short)((a0 - a1 + 7) >> 4); output[8 + i] = (short)((a0 - a1 + 7) >> 4);
output[12 + i] = (short)(((a3 * 2217) - (a2 * 5352) + 51000) >> 16); output[12 + i] = (short)(((a3 * 2217) - (a2 * 5352) + 51000) >> 16);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int ClipMax(int v, int max) => (v > max) ? max : v; private static int ClipMax(int v, int max) => v > max ? max : v;
} }
} }

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

@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int i; int i;
for (i = 0; i < 2; ++i) for (i = 0; i < 2; ++i)
{ {
int isAcCoeff = (i > 0) ? 1 : 0; int isAcCoeff = i > 0 ? 1 : 0;
int bias = BiasMatrices[type][isAcCoeff]; int bias = BiasMatrices[type][isAcCoeff];
this.IQ[i] = (ushort)((1 << WebpConstants.QFix) / this.Q[i]); this.IQ[i] = (ushort)((1 << WebpConstants.QFix) / this.Q[i]);
this.Bias[i] = (uint)this.BIAS(bias); this.Bias[i] = (uint)this.BIAS(bias);

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

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vp8StatsArray s = this.Stats[n].Stats[ctx]; Vp8StatsArray s = this.Stats[n].Stats[ctx];
if (this.Last < 0) if (this.Last < 0)
{ {
this.RecordStats(0, s, 0); this.RecordStats(0, s, 0);
return 0; return 0;
} }
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
this.RecordStats(1, s, 1); this.RecordStats(1, s, 1);
var bit = (uint)(v + 1) > 2u; bool bit = (uint)(v + 1) > 2u;
if (this.RecordStats(bit ? 1 : 0, s, 2) == 0) if (this.RecordStats(bit ? 1 : 0, s, 2) == 0)
{ {
// v = -1 or 1 // v = -1 or 1
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// bitCost(1, p0) is already incorporated in t[] tables, but only if ctx != 0 // 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 // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
// be missing during the loop. // be missing during the loop.
int cost = (ctx0 == 0) ? LossyUtils.Vp8BitCost(1, (byte)p0) : 0; int cost = ctx0 == 0 ? LossyUtils.Vp8BitCost(1, (byte)p0) : 0;
if (this.Last < 0) if (this.Last < 0)
{ {
@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (; n < this.Last; ++n) for (; n < this.Last; ++n)
{ {
v = Math.Abs(this.Coeffs[n]); v = Math.Abs(this.Coeffs[n]);
int ctx = (v >= 2) ? 2 : v; int ctx = v >= 2 ? 2 : v;
cost += LevelCost(t.Costs, v); cost += LevelCost(t.Costs, v);
t = costs[n + 1].Costs[ctx]; t = costs[n + 1].Costs[ctx];
} }
@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (n < 15) if (n < 15)
{ {
int b = WebpConstants.Vp8EncBands[n + 1]; int b = WebpConstants.Vp8EncBands[n + 1];
int ctx = (v == 1) ? 1 : 2; int ctx = v == 1 ? 1 : 2;
int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0]; int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0];
cost += LossyUtils.Vp8BitCost(0, (byte)lastP0); cost += LossyUtils.Vp8BitCost(0, (byte)lastP0);
} }
@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
private static int LevelCost(Span<ushort> table, int level) private static int LevelCost(Span<ushort> table, int level)
=> WebpLookupTables.Vp8LevelFixedCosts[level] + table[(level > WebpConstants.MaxVariableLevel) ? WebpConstants.MaxVariableLevel : level]; => WebpLookupTables.Vp8LevelFixedCosts[level] + table[level > WebpConstants.MaxVariableLevel ? WebpConstants.MaxVariableLevel : level];
private int RecordStats(int bit, Vp8StatsArray statsArr, int idx) private int RecordStats(int bit, Vp8StatsArray statsArr, int idx)
{ {

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

@ -74,8 +74,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int v0 = Math.Abs(dcs[1]); int v0 = Math.Abs(dcs[1]);
int v1 = Math.Abs(dcs[2]); int v1 = Math.Abs(dcs[2]);
int v2 = Math.Abs(dcs[4]); int v2 = Math.Abs(dcs[4]);
int maxV = (v1 > v0) ? v1 : v0; int maxV = v1 > v0 ? v1 : v0;
maxV = (v2 > maxV) ? v2 : maxV; maxV = v2 > maxV ? v2 : maxV;
if (maxV > this.MaxEdge) if (maxV > this.MaxEdge)
{ {
this.MaxEdge = maxV; this.MaxEdge = maxV;

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

@ -8,10 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Vp8StatsArray"/> class. /// Initializes a new instance of the <see cref="Vp8StatsArray"/> class.
/// </summary> /// </summary>
public Vp8StatsArray() public Vp8StatsArray() => this.Stats = new uint[WebpConstants.NumProbas];
{
this.Stats = new uint[WebpConstants.NumProbas];
}
public uint[] Stats { get; } public uint[] Stats { get; }
} }

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

@ -277,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> vDst = yuv.Slice(vOff); Span<byte> vDst = yuv.Slice(vOff);
// Initialize left-most block. // Initialize left-most block.
var end = 16 * WebpConstants.Bps; int end = 16 * WebpConstants.Bps;
for (int i = 0; i < end; i += WebpConstants.Bps) for (int i = 0; i < end; i += WebpConstants.Bps)
{ {
yuv[i - 1 + yOff] = 129; yuv[i - 1 + yOff] = 129;
@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (mbx >= dec.MbWidth - 1) if (mbx >= dec.MbWidth - 1)
{ {
// On rightmost border. // On rightmost border.
var topYuv15 = topYuv.Y[15]; byte topYuv15 = topYuv.Y[15];
topRight[0] = topYuv15; topRight[0] = topYuv15;
topRight[1] = topYuv15; topRight[1] = topYuv15;
topRight[2] = topYuv15; topRight[2] = topYuv15;
@ -421,7 +421,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
break; break;
} }
this.DoTransform(bits, coeffs.AsSpan(n * 16), dst); this.DoTransform(bits, coeffs.AsSpan(n * 16), dst);
} }
} }
else else
@ -606,15 +606,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int extraYRows = WebpConstants.FilterExtraRows[(int)dec.Filter]; int extraYRows = WebpConstants.FilterExtraRows[(int)dec.Filter];
int ySize = extraYRows * dec.CacheYStride; int ySize = extraYRows * dec.CacheYStride;
int uvSize = (extraYRows / 2) * dec.CacheUvStride; int uvSize = extraYRows / 2 * dec.CacheUvStride;
Span<byte> yDst = dec.CacheY.Memory.Span; Span<byte> yDst = dec.CacheY.Memory.Span;
Span<byte> uDst = dec.CacheU.Memory.Span; Span<byte> uDst = dec.CacheU.Memory.Span;
Span<byte> vDst = dec.CacheV.Memory.Span; Span<byte> vDst = dec.CacheV.Memory.Span;
int mby = dec.MbY; int mby = dec.MbY;
bool isFirstRow = mby == 0; bool isFirstRow = mby == 0;
bool isLastRow = mby >= dec.BottomRightMbY - 1; bool isLastRow = mby >= dec.BottomRightMbY - 1;
bool filterRow = (dec.Filter != LoopFilter.None) && bool filterRow = dec.Filter != LoopFilter.None && dec.MbY >= dec.TopLeftMbY && dec.MbY <= dec.BottomRightMbY;
(dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY);
if (filterRow) if (filterRow)
{ {
@ -698,8 +697,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
// Loop over each output pairs of row. // Loop over each output pairs of row.
var bufferStride2 = 2 * bufferStride; int bufferStride2 = 2 * bufferStride;
var ioStride2 = 2 * io.YStride; int ioStride2 = 2 * io.YStride;
for (; y + 2 < yEnd; y += 2) for (; y + 2 < yEnd; y += 2)
{ {
topU = curU; topU = curU;
@ -879,7 +878,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
else else
{ {
// Parse DC // Parse DC
var dc = new short[16]; short[] dc = new short[16];
int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs); int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs);
int nz = this.GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc); int nz = this.GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc);
mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0); mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0);
@ -913,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int ctx = l + (tnz & 1); int ctx = l + (tnz & 1);
int nz = this.GetCoeffs(br, acProba, ctx, q.Y1Mat, first, dst.AsSpan(dstOffset)); int nz = this.GetCoeffs(br, acProba, ctx, q.Y1Mat, first, dst.AsSpan(dstOffset));
l = (nz > first) ? 1 : 0; l = nz > first ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 7)); tnz = (byte)((tnz >> 1) | (l << 7));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0); nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
dstOffset += 16; dstOffset += 16;
@ -930,7 +929,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int ch = 0; ch < 4; ch += 2) for (int ch = 0; ch < 4; ch += 2)
{ {
uint nzCoeffs = 0; uint nzCoeffs = 0;
var chPlus4 = 4 + ch; int chPlus4 = 4 + ch;
tnz = (byte)(mb.NoneZeroAcDcCoeffs >> chPlus4); tnz = (byte)(mb.NoneZeroAcDcCoeffs >> chPlus4);
lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> chPlus4); lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> chPlus4);
for (int y = 0; y < 2; ++y) for (int y = 0; y < 2; ++y)
@ -940,7 +939,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int ctx = l + (tnz & 1); int ctx = l + (tnz & 1);
int nz = this.GetCoeffs(br, bands[2], ctx, q.UvMat, 0, dst.AsSpan(dstOffset)); int nz = this.GetCoeffs(br, bands[2], ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
l = (nz > 0) ? 1 : 0; l = nz > 0 ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 3)); tnz = (byte)((tnz >> 1) | (l << 3));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0); nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
dstOffset += 16; dstOffset += 16;
@ -952,7 +951,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Note: we don't really need the per-4x4 details for U/V blocks. // Note: we don't really need the per-4x4 details for U/V blocks.
nonZeroUv |= nzCoeffs << (4 * ch); nonZeroUv |= nzCoeffs << (4 * ch);
outTnz |= (uint)((tnz << 4) << ch); outTnz |= (uint)(tnz << 4 << ch);
outLnz |= (uint)((lnz & 0xf0) << ch); outLnz |= (uint)((lnz & 0xf0) << ch);
} }
@ -1128,7 +1127,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3); vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3);
vp8FilterHeader.UseLfDelta = this.bitReader.ReadBool(); vp8FilterHeader.UseLfDelta = this.bitReader.ReadBool();
dec.Filter = (vp8FilterHeader.FilterLevel == 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter; dec.Filter = vp8FilterHeader.FilterLevel == 0 ? LoopFilter.None : vp8FilterHeader.LoopFilter;
if (vp8FilterHeader.UseLfDelta) if (vp8FilterHeader.UseLfDelta)
{ {
// Update lf-delta? // Update lf-delta?
@ -1157,7 +1156,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int extraRows = WebpConstants.FilterExtraRows[(int)dec.Filter]; int extraRows = WebpConstants.FilterExtraRows[(int)dec.Filter];
int extraY = extraRows * dec.CacheYStride; int extraY = extraRows * dec.CacheYStride;
int extraUv = (extraRows / 2) * dec.CacheUvStride; int extraUv = extraRows / 2 * dec.CacheUvStride;
dec.CacheYOffset = extraY; dec.CacheYOffset = extraY;
dec.CacheUvOffset = extraUv; dec.CacheUvOffset = extraUv;
} }
@ -1313,7 +1312,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
// For simple filter, we include 'extraPixels' on the other side of the boundary, // For simple filter, we include 'extraPixels' on the other side of the boundary,
// since vertical or horizontal filtering of the previous macroblock can modify some abutting pixels. // since vertical or horizontal filtering of the previous macroblock can modify some abutting pixels.
var extraShift4 = (-extraPixels) >> 4; int extraShift4 = -extraPixels >> 4;
dec.TopLeftMbX = extraShift4; dec.TopLeftMbX = extraShift4;
dec.TopLeftMbY = extraShift4; dec.TopLeftMbY = extraShift4;
if (dec.TopLeftMbX < 0) if (dec.TopLeftMbX < 0)
@ -1347,7 +1346,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz) private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz)
{ {
nzCoeffs <<= 2; nzCoeffs <<= 2;
nzCoeffs |= (uint)((nz > 3) ? 3 : (nz > 1) ? 2 : dcNz); nzCoeffs |= (uint)(nz > 3 ? 3 : nz > 1 ? 2 : dcNz);
return nzCoeffs; return nzCoeffs;
} }
@ -1359,12 +1358,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
if (mbx == 0) if (mbx == 0)
{ {
return (mby == 0) return mby == 0
? 6 // B_DC_PRED_NOTOPLEFT ? 6 // B_DC_PRED_NOTOPLEFT
: 5; // B_DC_PRED_NOLEFT : 5; // B_DC_PRED_NOLEFT
} }
return (mby == 0) return mby == 0
? 4 // B_DC_PRED_NOTOP ? 4 // B_DC_PRED_NOTOP
: 0; // B_DC_PRED : 0; // B_DC_PRED
} }
@ -1373,9 +1372,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Clip(int value, int max) private static int Clip(int value, int max) => value < 0 ? 0 : value > max ? max : value;
{
return value < 0 ? 0 : value > max ? max : value;
}
} }
} }

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

@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Bgra32 bgra1; Bgra32 bgra1;
int i, j; int i, j;
int dstIdx = 0; int dstIdx = 0;
for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2, dstIdx += 4) for (i = 0, j = 0; i < width >> 1; i += 1, j += 2, dstIdx += 4)
{ {
bgra0 = rowSpan[j]; bgra0 = rowSpan[j];
bgra1 = rowSpan[j + 1]; bgra1 = rowSpan[j + 1];
@ -291,7 +291,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static int ClipUv(int uv, int rounding) private static int ClipUv(int uv, int rounding)
{ {
uv = (uv + rounding + (128 << (YuvFix + 2))) >> (YuvFix + 2); uv = (uv + rounding + (128 << (YuvFix + 2))) >> (YuvFix + 2);
return ((uv & ~0xff) == 0) ? uv : (uv < 0) ? 0 : 255; return (uv & ~0xff) == 0 ? uv : uv < 0 ? 0 : 255;
} }
} }
} }

2
src/ImageSharp/Formats/WebP/WebpConstants.cs

@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
public static readonly byte[] Cat4 = { 176, 155, 140, 135 }; public static readonly byte[] Cat4 = { 176, 155, 140, 135 };
public static readonly byte[] Cat5 = { 180, 157, 141, 134, 130 }; public static readonly byte[] Cat5 = { 180, 157, 141, 134, 130 };
public static readonly byte[] Cat6 = { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; public static readonly byte[] Cat6 = { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
public static readonly byte[] Zigzag = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; public static readonly byte[] Zigzag = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 };
public static readonly sbyte[] YModesIntra4 = public static readonly sbyte[] YModesIntra4 =
{ {

8
src/ImageSharp/Formats/WebP/WebpDecoderCore.cs

@ -429,7 +429,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
else else
{ {
var iccpData = new byte[iccpChunkSize]; byte[] iccpData = new byte[iccpChunkSize];
this.currentStream.Read(iccpData, 0, (int)iccpChunkSize); this.currentStream.Read(iccpData, 0, (int)iccpChunkSize);
var profile = new IccProfile(iccpData); var profile = new IccProfile(iccpData);
if (profile.CheckIsValid()) if (profile.CheckIsValid())
@ -447,7 +447,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
case WebpChunkType.Alpha: case WebpChunkType.Alpha:
uint alphaChunkSize = this.ReadChunkSize(); uint alphaChunkSize = this.ReadChunkSize();
features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); features.AlphaChunkHeader = (byte)this.currentStream.ReadByte();
var alphaDataSize = (int)(alphaChunkSize - 1); int alphaDataSize = (int)(alphaChunkSize - 1);
features.AlphaData = this.memoryAllocator.Allocate<byte>(alphaDataSize); features.AlphaData = this.memoryAllocator.Allocate<byte>(alphaDataSize);
this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize); this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize);
break; break;
@ -467,7 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
return; return;
} }
var streamLength = this.currentStream.Length; long streamLength = this.currentStream.Length;
while (this.currentStream.Position < streamLength) while (this.currentStream.Position < streamLength)
{ {
// Read chunk header. // Read chunk header.
@ -476,7 +476,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
if (chunkType == WebpChunkType.Exif && this.Metadata.ExifProfile == null) if (chunkType == WebpChunkType.Exif && this.Metadata.ExifProfile == null)
{ {
var exifData = new byte[chunkLength]; byte[] exifData = new byte[chunkLength];
this.currentStream.Read(exifData, 0, (int)chunkLength); this.currentStream.Read(exifData, 0, (int)chunkLength);
this.Metadata.ExifProfile = new ExifProfile(exifData); this.Metadata.ExifProfile = new ExifProfile(exifData);
} }

5
src/ImageSharp/Formats/WebP/WebpFeatures.cs

@ -47,9 +47,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
public bool Animation { get; set; } public bool Animation { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose() => this.AlphaData?.Dispose();
{
this.AlphaData?.Dispose();
}
} }
} }

22
src/ImageSharp/Formats/WebP/WebpImageFormatDetector.cs

@ -14,36 +14,22 @@ namespace SixLabors.ImageSharp.Formats.Webp
public int HeaderSize => 12; public int HeaderSize => 12;
/// <inheritdoc /> /// <inheritdoc />
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? WebpFormat.Instance : null;
{
return this.IsSupportedFileFormat(header) ? WebpFormat.Instance : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) => header.Length >= this.HeaderSize && this.IsRiffContainer(header) && this.IsWebPFile(header);
{
return header.Length >= this.HeaderSize &&
this.IsRiffContainer(header) &&
this.IsWebPFile(header);
}
/// <summary> /// <summary>
/// Checks, if the header starts with a valid RIFF FourCC. /// Checks, if the header starts with a valid RIFF FourCC.
/// </summary> /// </summary>
/// <param name="header">The header bytes.</param> /// <param name="header">The header bytes.</param>
/// <returns>True, if its a valid RIFF FourCC.</returns> /// <returns>True, if its a valid RIFF FourCC.</returns>
private bool IsRiffContainer(ReadOnlySpan<byte> header) private bool IsRiffContainer(ReadOnlySpan<byte> header) => header.Slice(0, 4).SequenceEqual(WebpConstants.RiffFourCc);
{
return header.Slice(0, 4).SequenceEqual(WebpConstants.RiffFourCc);
}
/// <summary> /// <summary>
/// Checks if 'WEBP' is present in the header. /// Checks if 'WEBP' is present in the header.
/// </summary> /// </summary>
/// <param name="header">The header bytes.</param> /// <param name="header">The header bytes.</param>
/// <returns>True, if its a webp file.</returns> /// <returns>True, if its a webp file.</returns>
private bool IsWebPFile(ReadOnlySpan<byte> header) private bool IsWebPFile(ReadOnlySpan<byte> header) => header.Slice(8, 4).SequenceEqual(WebpConstants.WebPHeader);
{
return header.Slice(8, 4).SequenceEqual(WebpConstants.WebPHeader);
}
} }
} }

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

@ -1242,19 +1242,19 @@ namespace SixLabors.ImageSharp.Formats.Webp
Clip1 = new Dictionary<int, byte>(); Clip1 = new Dictionary<int, byte>();
for (int i = -255; i <= 255 + 255; ++i) for (int i = -255; i <= 255 + 255; ++i)
{ {
Clip1[i] = (byte)((i < 0) ? 0 : (i > 255) ? 255 : i); Clip1[i] = (byte)(i < 0 ? 0 : i > 255 ? 255 : i);
} }
Sclip1 = new Dictionary<int, sbyte>(); Sclip1 = new Dictionary<int, sbyte>();
for (int i = -1020; i <= 1020; ++i) for (int i = -1020; i <= 1020; ++i)
{ {
Sclip1[i] = (sbyte)((i < -128) ? -128 : (i > 127) ? 127 : i); Sclip1[i] = (sbyte)(i < -128 ? -128 : i > 127 ? 127 : i);
} }
Sclip2 = new Dictionary<int, sbyte>(); Sclip2 = new Dictionary<int, sbyte>();
for (int i = -112; i <= 112; ++i) for (int i = -112; i <= 112; ++i)
{ {
Sclip2[i] = (sbyte)((i < -16) ? -16 : (i > 15) ? 15 : i); Sclip2[i] = (sbyte)(i < -16 ? -16 : i > 15 ? 15 : i);
} }
InitializeModesProbabilities(); InitializeModesProbabilities();

Loading…
Cancel
Save