Browse Source

Review changes

pull/1552/head
Brian Popow 6 years ago
parent
commit
988b974453
  1. 20
      src/ImageSharp/Formats/WebP/AlphaDecoder.cs
  2. 33
      src/ImageSharp/Formats/WebP/HuffmanUtils.cs
  3. 116
      src/ImageSharp/Formats/WebP/LosslessUtils.cs
  4. 203
      src/ImageSharp/Formats/WebP/LossyUtils.cs
  5. 12
      src/ImageSharp/Formats/WebP/Vp8BitReader.cs
  6. 20
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  7. 2
      src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs
  8. 4
      src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
  9. 16
      src/ImageSharp/Formats/WebP/Vp8Profile.cs
  10. 12
      src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs
  11. 10
      src/ImageSharp/Formats/WebP/WebPChunkType.cs
  12. 6
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  13. 44
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  14. 8
      src/ImageSharp/Formats/WebP/WebPFeatures.cs
  15. 4
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs
  16. 90
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
  17. 234
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

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

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found"); WebPThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found");
} }
this.Compressed = compression is AlphaCompressionMethod.WebPLosslessCompression; this.Compressed = compression == AlphaCompressionMethod.WebPLosslessCompression;
// The filtering method used. Only values between 0 and 3 are valid. // The filtering method used. Only values between 0 and 3 are valid.
int filter = (alphaChunkHeader >> 2) & 0x03; int filter = (alphaChunkHeader >> 2) & 0x03;
@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public void Decode() public void Decode()
{ {
if (this.Compressed is 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; var pixelCount = this.Width * this.Height;
@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
Span<byte> alphaSpan = this.Alpha.Memory.Span; Span<byte> alphaSpan = this.Alpha.Memory.Span;
if (this.AlphaFilterType is WebPAlphaFilterType.None) if (this.AlphaFilterType == WebPAlphaFilterType.None)
{ {
dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan); dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan);
return; return;
@ -187,13 +187,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="stride">The stride to use.</param> /// <param name="stride">The stride to use.</param>
public void AlphaApplyFilter(int firstRow, int lastRow, Span<byte> dst, int stride) public void AlphaApplyFilter(int firstRow, int lastRow, Span<byte> dst, int stride)
{ {
if (this.AlphaFilterType is WebPAlphaFilterType.None) if (this.AlphaFilterType == WebPAlphaFilterType.None)
{ {
return; return;
} }
Span<byte> alphaSpan = this.Alpha.Memory.Span; Span<byte> alphaSpan = this.Alpha.Memory.Span;
Span<byte> prev = this.PrevRow is 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow); Span<byte> prev = this.PrevRow == 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow);
for (int y = firstRow; y < lastRow; ++y) for (int y = firstRow; y < lastRow; ++y)
{ {
switch (this.AlphaFilterType) switch (this.AlphaFilterType)
@ -220,7 +220,7 @@ 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 is WebPAlphaFilterType.None || this.AlphaFilterType is 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)
{ {
@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Span<byte> dst = output.Slice(this.Width * firstRow); Span<byte> dst = output.Slice(this.Width * firstRow);
Span<byte> input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow); Span<byte> input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow);
if (this.Vp8LDec.Transforms.Count is 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) if (this.Vp8LDec.Transforms.Count == 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform)
{ {
WebPThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing"); WebPThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing");
} }
@ -285,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int packedPixels = 0; int packedPixels = 0;
for (int x = 0; x < width; ++x) for (int x = 0; x < width; ++x)
{ {
if ((x & countMask) is 0) if ((x & countMask) == 0)
{ {
packedPixels = src[srcOffset]; packedPixels = src[srcOffset];
srcOffset++; srcOffset++;
@ -364,8 +364,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
return false; return false;
} }
// When the Huffman tree contains only one symbol, we can skip the
// call to ReadSymbol() for red/blue/alpha channels.
for (int i = 0; i < hdr.NumHTreeGroups; ++i) for (int i = 0; i < hdr.NumHTreeGroups; ++i)
{ {
List<HuffmanCode[]> htrees = hdr.HTreeGroups[i].HTrees; List<HuffmanCode[]> htrees = hdr.HTreeGroups[i].HTrees;
@ -411,7 +409,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
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) is 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)]

33
src/ImageSharp/Formats/WebP/HuffmanUtils.cs

@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
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 count = new int[WebPConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. var counts = new int[WebPConstants.MaxAllowedCodeLength + 1]; // number of codes of each length.
var offset = new int[WebPConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. var 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)
@ -41,26 +41,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
return 0; return 0;
} }
count[codeLengthOfSymbol]++; counts[codeLengthOfSymbol]++;
} }
// Error, all code lengths are zeros. // Error, all code lengths are zeros.
if (count[0] == codeLengthsSize) if (counts[0] == codeLengthsSize)
{ {
return 0; return 0;
} }
// Generate offsets into sorted symbol table by code length. // Generate offsets into sorted symbol table by code length.
offset[1] = 0; offsets[1] = 0;
for (len = 1; len < WebPConstants.MaxAllowedCodeLength; ++len) for (len = 1; len < WebPConstants.MaxAllowedCodeLength; ++len)
{ {
int codesOfLength = count[len]; int codesOfLength = counts[len];
if (codesOfLength > (1 << len)) if (codesOfLength > (1 << len))
{ {
return 0; return 0;
} }
offset[len + 1] = offset[len] + codesOfLength; offsets[len + 1] = offsets[len] + codesOfLength;
} }
// Sort symbols by length, by symbol order within each length. // Sort symbols by length, by symbol order within each length.
@ -69,12 +69,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
int symbolCodeLength = codeLengths[symbol]; int symbolCodeLength = codeLengths[symbol];
if (symbolCodeLength > 0) if (symbolCodeLength > 0)
{ {
sorted[offset[symbolCodeLength]++] = symbol; sorted[offsets[symbolCodeLength]++] = symbol;
} }
} }
// Special case code with only one value. // Special case code with only one value.
if (offset[WebPConstants.MaxAllowedCodeLength] is 1) if (offsets[WebPConstants.MaxAllowedCodeLength] == 1)
{ {
var huffmanCode = new HuffmanCode() var huffmanCode = new HuffmanCode()
{ {
@ -98,15 +98,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
// 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];
numOpen <<= 1; numOpen <<= 1;
numNodes += numOpen; numNodes += numOpen;
numOpen -= count[len]; numOpen -= counts[len];
if (numOpen < 0) if (numOpen < 0)
{ {
return 0; return 0;
} }
for (; count[len] > 0; --count[len]) for (; countsLen > 0; countsLen--)
{ {
var huffmanCode = new HuffmanCode() var huffmanCode = new HuffmanCode()
{ {
@ -116,6 +117,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
ReplicateValue(table.Slice(key), step, tableSize, huffmanCode); ReplicateValue(table.Slice(key), step, tableSize, huffmanCode);
key = GetNextKey(key, len); key = GetNextKey(key, len);
} }
counts[len] = countsLen;
} }
// Fill in 2nd level tables and add pointers to root table. // Fill in 2nd level tables and add pointers to root table.
@ -125,19 +128,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
numOpen <<= 1; numOpen <<= 1;
numNodes += numOpen; numNodes += numOpen;
numOpen -= count[len]; numOpen -= counts[len];
if (numOpen < 0) if (numOpen < 0)
{ {
return 0; return 0;
} }
for (; count[len] > 0; --count[len]) for (; counts[len] > 0; --counts[len])
{ {
if ((key & mask) != low) if ((key & mask) != low)
{ {
tableSpan = tableSpan.Slice(tableSize); tableSpan = tableSpan.Slice(tableSize);
tablePos += tableSize; tablePos += tableSize;
tableBits = NextTableBitSize(count, len, rootBits); tableBits = NextTableBitSize(counts, len, rootBits);
tableSize = 1 << tableBits; tableSize = 1 << tableBits;
totalSize += tableSize; totalSize += tableSize;
low = key & mask; low = key & mask;
@ -185,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
/// <summary> /// <summary>
/// Stores code in table[0], table[step], table[2*step], ..., table[end]. /// Stores code in table[0], table[step], table[2*step], ..., table[end-step].
/// Assumes that end is an integer multiple of step. /// Assumes that end is an integer multiple of step.
/// </summary> /// </summary>
private static void ReplicateValue(Span<HuffmanCode> table, int step, int end, HuffmanCode code) private static void ReplicateValue(Span<HuffmanCode> table, int step, int end, HuffmanCode code)

116
src/ImageSharp/Formats/WebP/LosslessUtils.cs

@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
internal static class LosslessUtils internal static class LosslessUtils
{ {
private const uint Predictor0 = WebPConstants.ArgbBlack;
/// <summary> /// <summary>
/// Add green to blue and red channels (i.e. perform the inverse transform of 'subtract green'). /// Add green to blue and red channels (i.e. perform the inverse transform of 'subtract green').
/// </summary> /// </summary>
@ -61,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// 'pixelsPerByte' increments of x. Fortunately, pixelsPerByte // 'pixelsPerByte' increments of x. Fortunately, pixelsPerByte
// is a power of 2, so we can just use a mask for that, instead of // is a power of 2, so we can just use a mask for that, instead of
// decrementing a counter. // decrementing a counter.
if ((x & countMask) is 0) if ((x & countMask) == 0)
{ {
packedPixels = GetArgbIndex(pixelData[pixelDataPos++]); packedPixels = GetArgbIndex(pixelData[pixelDataPos++]);
} }
@ -72,17 +74,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
decodedPixelData.AsSpan().CopyTo(pixelData); decodedPixelData.AsSpan().CopyTo(pixelData);
return;
} }
else
for (int y = 0; y < height; ++y)
{ {
for (int x = 0; x < width; ++x) for (int y = 0; y < height; ++y)
{ {
uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]); for (int x = 0; x < width; ++x)
pixelData[decodedPixels] = colorMap[(int)colorMapIndex]; {
decodedPixels++; uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]);
pixelData[decodedPixels] = colorMap[(int)colorMapIndex];
decodedPixels++;
}
} }
} }
} }
@ -130,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
++y; ++y;
if ((y & mask) is 0) if ((y & mask) == 0)
{ {
predRowIdxStart += tilesPerRow; predRowIdxStart += tilesPerRow;
} }
@ -154,9 +156,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint red = argb >> 16; uint red = argb >> 16;
int newRed = (int)(red & 0xff); int newRed = (int)(red & 0xff);
int newBlue = (int)argb & 0xff; int newBlue = (int)argb & 0xff;
newRed += ColorTransformDelta((sbyte)m.GreenToRed, (sbyte)green); newRed += ColorTransformDelta((sbyte)m.GreenToRed, green);
newRed &= 0xff; newRed &= 0xff;
newBlue += ColorTransformDelta((sbyte)m.GreenToBlue, (sbyte)green); newBlue += ColorTransformDelta((sbyte)m.GreenToBlue, green);
newBlue += ColorTransformDelta((sbyte)m.RedToBlue, (sbyte)newRed); newBlue += ColorTransformDelta((sbyte)m.RedToBlue, (sbyte)newRed);
newBlue &= 0xff; newBlue &= 0xff;
@ -176,7 +178,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output) public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output)
{ {
int processedPixels = 0; int processedPixels = 0;
int yStart = 0;
int width = transform.XSize; int width = transform.XSize;
Span<uint> transformData = transform.Data.GetSpan(); Span<uint> transformData = transform.Data.GetSpan();
@ -184,9 +185,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
PredictorAdd0(pixelData, processedPixels, 1, output); PredictorAdd0(pixelData, processedPixels, 1, output);
PredictorAdd1(pixelData, 1, width - 1, output); PredictorAdd1(pixelData, 1, width - 1, output);
processedPixels += width; processedPixels += width;
yStart++;
int y = yStart; int y = 1;
int yEnd = transform.YSize; int yEnd = transform.YSize;
int tileWidth = 1 << transform.Bits; int tileWidth = 1 << transform.Bits;
int mask = tileWidth - 1; int mask = tileWidth - 1;
@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
processedPixels += width; processedPixels += width;
++y; ++y;
if ((y & mask) is 0) if ((y & mask) == 0)
{ {
// Use the same mask, since tiles are squares. // Use the same mask, since tiles are squares.
predictorModeIdxBase += tilesPerRow; predictorModeIdxBase += tilesPerRow;
@ -295,9 +295,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd0(Span<uint> input, int startIdx, int numberOfPixels, Span<uint> output) private static void PredictorAdd0(Span<uint> input, int startIdx, int numberOfPixels, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
uint pred = Predictor0;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor0();
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -315,10 +315,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd2(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd2(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor2(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor2(output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -326,10 +326,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd3(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd3(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor3(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor3(output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -337,10 +337,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd4(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd4(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor4(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor4(output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -348,10 +348,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd5(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd5(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor5(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor5(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -359,10 +359,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd6(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd6(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor6(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor6(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -370,10 +370,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd7(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd7(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor7(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor7(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -381,10 +381,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd8(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd8(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor8(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor8(output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -392,10 +392,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd9(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd9(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor9(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor9(output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -403,10 +403,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd10(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd10(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor10(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor10(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -414,10 +414,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd11(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd11(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor11(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor11(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -425,10 +425,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd12(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd12(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor12(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor12(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
@ -436,40 +436,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void PredictorAdd13(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) private static void PredictorAdd13(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output)
{ {
int endIdx = startIdx + numberOfPixels; int endIdx = startIdx + numberOfPixels;
int offset = 0; int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x) for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor13(output[x - 1], output, startIdx - width + offset++); uint pred = Predictor13(output[x - 1], output, offset++);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor0() private static uint Predictor2(Span<uint> top, int idx)
{
return WebPConstants.ArgbBlack;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor1(uint left, Span<uint> top)
{
return left;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor2(uint left, Span<uint> top, int idx)
{ {
return top[idx]; return top[idx];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor3(uint left, Span<uint> top, int idx) private static uint Predictor3(Span<uint> top, int idx)
{ {
return top[idx + 1]; return top[idx + 1];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor4(uint left, Span<uint> top, int idx) private static uint Predictor4(Span<uint> top, int idx)
{ {
return top[idx - 1]; return top[idx - 1];
} }
@ -496,14 +484,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor8(uint left, Span<uint> top, int idx) private static uint Predictor8(Span<uint> top, int idx)
{ {
uint pred = Average2(top[idx - 1], top[idx]); uint pred = Average2(top[idx - 1], top[idx]);
return pred; return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Predictor9(uint left, Span<uint> top, int idx) private static uint Predictor9(Span<uint> top, int idx)
{ {
uint pred = Average2(top[idx], top[idx + 1]); uint pred = Average2(top[idx], top[idx + 1]);
return pred; return pred;
@ -539,7 +527,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2) private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2)
{ {
int a = AddSubtractComponentFull((int)(c0 >> 24), (int)(c1 >> 24), (int)(c2 >> 24)); int a = AddSubtractComponentFull(
(int)(c0 >> 24),
(int)(c1 >> 24),
(int)(c2 >> 24));
int r = AddSubtractComponentFull( int r = AddSubtractComponentFull(
(int)((c0 >> 16) & 0xff), (int)((c0 >> 16) & 0xff),
(int)((c1 >> 16) & 0xff), (int)((c1 >> 16) & 0xff),
@ -577,12 +568,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Clip255(uint a) private static uint Clip255(uint a)
{ {
if (a < 256) return a < 256 ? a : ~a >> 24;
{
return a;
}
return ~a >> 24;
} }
private static uint Select(uint a, uint b, uint c) private static uint Select(uint a, uint b, uint c)

203
src/ImageSharp/Formats/WebP/LossyUtils.cs

@ -5,6 +5,7 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal static class LossyUtils internal static class LossyUtils
@ -20,11 +21,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void DC16(Span<byte> dst, Span<byte> yuv, int offset) public static void DC16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int offsetMinus1 = offset - 1;
int offsetMinusBps = offset - WebPConstants.Bps;
int dc = 16; int dc = 16;
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
// DC += dst[-1 + j * BPS] + dst[j - BPS]; // DC += dst[-1 + j * BPS] + dst[j - BPS];
dc += yuv[offset - 1 + (j * WebPConstants.Bps)] + yuv[offset + j - WebPConstants.Bps]; dc += yuv[offsetMinus1 + (j * WebPConstants.Bps)] + yuv[offsetMinusBps + j];
} }
Put16(dc >> 5, dst); Put16(dc >> 5, dst);
@ -50,10 +53,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void HE16(Span<byte> dst, Span<byte> yuv, int offset) public static void HE16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
offset--;
for (int j = 16; j > 0; --j) for (int j = 16; j > 0; --j)
{ {
// memset(dst, dst[-1], 16); // memset(dst, dst[-1], 16);
byte v = yuv[offset - 1]; byte v = yuv[offset];
Memset(dst, v, 0, 16); Memset(dst, v, 0, 16);
offset += WebPConstants.Bps; offset += WebPConstants.Bps;
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebPConstants.Bps);
@ -96,10 +100,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void DC8uv(Span<byte> dst, Span<byte> yuv, int offset) public static void DC8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc0 = 8; int dc0 = 8;
int offsetMinus1 = offset - 1;
int offsetMinusBps = offset - WebPConstants.Bps;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
// dc0 += dst[i - BPS] + dst[-1 + i * BPS]; // dc0 += dst[i - BPS] + dst[-1 + i * BPS];
dc0 += yuv[offset + i - WebPConstants.Bps] + yuv[offset - 1 + (i * WebPConstants.Bps)]; dc0 += yuv[offsetMinusBps + i] + yuv[offsetMinus1 + (i * WebPConstants.Bps)];
} }
Put8x8uv((byte)(dc0 >> 4), dst); Put8x8uv((byte)(dc0 >> 4), dst);
@ -117,21 +123,23 @@ namespace SixLabors.ImageSharp.Formats.WebP
// vertical // vertical
Span<byte> src = yuv.Slice(offset - WebPConstants.Bps, 8); Span<byte> src = yuv.Slice(offset - WebPConstants.Bps, 8);
for (int j = 0; j < 8; ++j) int endIdx = 8 * WebPConstants.Bps;
for (int j = 0; j < endIdx; j += WebPConstants.Bps)
{ {
// memcpy(dst + j * BPS, dst - BPS, 8); // memcpy(dst + j * BPS, dst - BPS, 8);
src.CopyTo(dst.Slice(j * WebPConstants.Bps)); src.CopyTo(dst.Slice(j));
} }
} }
public static void HE8uv(Span<byte> dst, Span<byte> yuv, int offset) public static void HE8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
offset--;
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
// memset(dst, dst[-1], 8); // memset(dst, dst[-1], 8);
// dst += BPS; // dst += BPS;
byte v = yuv[offset - 1]; byte v = yuv[offset];
Memset(dst, v, 0, 8); Memset(dst, v, 0, 8);
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebPConstants.Bps);
offset += WebPConstants.Bps; offset += WebPConstants.Bps;
@ -142,10 +150,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
// DC with no top samples. // DC with no top samples.
int dc0 = 4; int dc0 = 4;
for (int i = 0; i < 8; ++i) int offsetMinusOne = offset - 1;
int endIdx = 8 * WebPConstants.Bps;
for (int i = 0; i < endIdx; i += WebPConstants.Bps)
{ {
// dc0 += dst[-1 + i * BPS]; // dc0 += dst[-1 + i * BPS];
dc0 += yuv[offset - 1 + (i * WebPConstants.Bps)]; dc0 += yuv[offsetMinusOne + i];
} }
Put8x8uv((byte)(dc0 >> 3), dst); Put8x8uv((byte)(dc0 >> 3), dst);
@ -154,11 +164,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void DC8uvNoLeft(Span<byte> dst, Span<byte> yuv, int offset) public static void DC8uvNoLeft(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// DC with no left samples. // DC with no left samples.
int offsetMinusBps = offset - WebPConstants.Bps;
int dc0 = 4; int dc0 = 4;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
// dc0 += dst[i - BPS]; // dc0 += dst[i - BPS];
dc0 += yuv[offset + i - WebPConstants.Bps]; dc0 += yuv[offsetMinusBps + i];
} }
Put8x8uv((byte)(dc0 >> 3), dst); Put8x8uv((byte)(dc0 >> 3), dst);
@ -174,15 +185,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void DC4(Span<byte> dst, Span<byte> yuv, int offset) public static void DC4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc = 4; int dc = 4;
int offsetMinusBps = offset - WebPConstants.Bps;
int offsetMinusOne = offset - 1;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
dc += yuv[offset + i - WebPConstants.Bps] + yuv[offset - 1 + (i * WebPConstants.Bps)]; dc += yuv[offsetMinusBps + i] + yuv[offsetMinusOne + (i * WebPConstants.Bps)];
} }
dc >>= 3; dc >>= 3;
for (int i = 0; i < 4; ++i) int endIndx = 4 * WebPConstants.Bps;
for (int i = 0; i < endIndx; i += WebPConstants.Bps)
{ {
Memset(dst, (byte)dc, i * WebPConstants.Bps, 4); Memset(dst, (byte)dc, i, 4);
} }
} }
@ -204,20 +218,22 @@ namespace SixLabors.ImageSharp.Formats.WebP
Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4]) Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4])
}; };
for (int i = 0; i < 4; ++i) int endIdx = 4 * WebPConstants.Bps;
for (int i = 0; i < endIdx; i += WebPConstants.Bps)
{ {
vals.CopyTo(dst.Slice(i * WebPConstants.Bps)); vals.CopyTo(dst.Slice(i));
} }
} }
public static void HE4(Span<byte> dst, Span<byte> yuv, int offset) public static void HE4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
byte a = yuv[offset - 1 - WebPConstants.Bps]; int offsetMinusOne = offset - 1;
byte b = yuv[offset - 1]; byte a = yuv[offsetMinusOne - WebPConstants.Bps];
byte c = yuv[offset - 1 + WebPConstants.Bps]; byte b = yuv[offsetMinusOne];
byte d = yuv[offset - 1 + (2 * WebPConstants.Bps)]; byte c = yuv[offsetMinusOne + WebPConstants.Bps];
byte e = yuv[offset - 1 + (3 * WebPConstants.Bps)]; byte d = yuv[offsetMinusOne + (2 * WebPConstants.Bps)];
byte e = yuv[offsetMinusOne + (3 * WebPConstants.Bps)];
uint val = 0x01010101U * Avg3(a, b, c); uint val = 0x01010101U * Avg3(a, b, c);
BinaryPrimitives.WriteUInt32BigEndian(dst, val); BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * Avg3(b, c, d); val = 0x01010101U * Avg3(b, c, d);
@ -231,11 +247,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void RD4(Span<byte> dst, Span<byte> yuv, int offset) public static void RD4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Down-right // Down-right
byte i = yuv[offset - 1]; int offsetMinusOne = offset - 1;
byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; byte i = yuv[offsetMinusOne];
byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)];
byte l = yuv[offset - 1 + (3 * WebPConstants.Bps)]; byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)];
byte x = yuv[offset - 1 - WebPConstants.Bps]; byte l = yuv[offsetMinusOne + (3 * WebPConstants.Bps)];
byte x = yuv[offsetMinusOne - WebPConstants.Bps];
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebPConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebPConstants.Bps];
@ -267,10 +284,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void VR4(Span<byte> dst, Span<byte> yuv, int offset) public static void VR4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Vertical-Right // Vertical-Right
byte i = yuv[offset - 1]; int offsetMinusOne = offset - 1;
byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; byte i = yuv[offsetMinusOne];
byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)];
byte x = yuv[offset - 1 - WebPConstants.Bps]; byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)];
byte x = yuv[offsetMinusOne - WebPConstants.Bps];
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebPConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebPConstants.Bps];
@ -437,33 +455,30 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 3, 3, l); Dst(dst, 3, 3, l);
} }
public static void Transform(Span<short> src, Span<byte> dst, bool doTwo) public static void TransformTwo(Span<short> src, Span<byte> dst)
{ {
TransformOne(src, dst); TransformOne(src, dst);
if (doTwo) TransformOne(src.Slice(16), dst.Slice(4));
{
TransformOne(src.Slice(16), dst.Slice(4));
}
} }
public static void TransformOne(Span<short> src, Span<byte> dst) public static void TransformOne(Span<short> src, Span<byte> dst)
{ {
var tmp = new int[4 * 4]; var tmp = new int[4 * 4];
int tmpOffset = 0; int tmpOffset = 0;
int srcOffset = 0; for (int srcOffset = 0; srcOffset < 4; srcOffset++)
for (int i = 0; i < 4; ++i)
{ {
// vertical pass // vertical pass
int a = src[srcOffset] + src[srcOffset + 8]; int srcOffsetPlus4 = srcOffset + 4;
int b = src[srcOffset] - src[srcOffset + 8]; int srcOffsetPlus8 = srcOffset + 8;
int c = Mul2(src[srcOffset + 4]) - Mul1(src[srcOffset + 12]); int srcOffsetPlus12 = srcOffset + 12;
int d = Mul1(src[srcOffset + 4]) + Mul2(src[srcOffset + 12]); int a = src[srcOffset] + src[srcOffsetPlus8];
tmp[tmpOffset] = a + d; int b = src[srcOffset] - src[srcOffsetPlus8];
tmp[tmpOffset + 1] = b + c; int c = Mul2(src[srcOffsetPlus4]) - Mul1(src[srcOffsetPlus12]);
tmp[tmpOffset + 2] = b - c; int d = Mul1(src[srcOffsetPlus4]) + Mul2(src[srcOffsetPlus12]);
tmp[tmpOffset + 3] = a - d; tmp[tmpOffset++] = a + d;
tmpOffset += 4; tmp[tmpOffset++] = b + c;
srcOffset++; tmp[tmpOffset++] = b - c;
tmp[tmpOffset++] = a - d;
} }
// Each pass is expanding the dynamic range by ~3.85 (upper bound). // Each pass is expanding the dynamic range by ~3.85 (upper bound).
@ -475,11 +490,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
// horizontal pass // horizontal pass
int tmpOffsetPlus4 = tmpOffset + 4;
int tmpOffsetPlus8 = tmpOffset + 8;
int tmpOffsetPlus12 = tmpOffset + 12;
int dc = tmp[tmpOffset] + 4; int dc = tmp[tmpOffset] + 4;
int a = dc + tmp[tmpOffset + 8]; int a = dc + tmp[tmpOffsetPlus8];
int b = dc - tmp[tmpOffset + 8]; int b = dc - tmp[tmpOffsetPlus8];
int c = Mul2(tmp[tmpOffset + 4]) - Mul1(tmp[tmpOffset + 12]); int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]);
int d = Mul1(tmp[tmpOffset + 4]) + Mul2(tmp[tmpOffset + 12]); int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]);
Store(dst, 0, 0, a + d); Store(dst, 0, 0, a + d);
Store(dst, 1, 0, b + c); Store(dst, 1, 0, b + c);
Store(dst, 2, 0, b - c); Store(dst, 2, 0, b - c);
@ -517,8 +535,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void TransformUv(Span<short> src, Span<byte> dst) public static void TransformUv(Span<short> src, Span<byte> dst)
{ {
Transform(src.Slice(0 * 16), dst, true); TransformTwo(src.Slice(0 * 16), dst);
Transform(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps), true); TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps));
} }
public static void TransformDcuv(Span<short> src, Span<byte> dst) public static void TransformDcuv(Span<short> src, Span<byte> dst)
@ -569,11 +587,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh) public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
for (int i = 0; i < 16; ++i) int end = 16 + offset;
for (int i = offset; i < end; ++i)
{ {
if (NeedsFilter(p, offset + i, stride, thresh2)) if (NeedsFilter(p, i, stride, thresh2))
{ {
DoFilter2(p, offset + i, stride); DoFilter2(p, i, stride);
} }
} }
} }
@ -581,11 +600,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static void SimpleHFilter16(Span<byte> p, int offset, int stride, int thresh) public static void SimpleHFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
for (int i = 0; i < 16; ++i) int end = offset + (16 * stride);
for (int i = offset; i < end; i += stride)
{ {
if (NeedsFilter(p, offset + (i * stride), 1, thresh2)) if (NeedsFilter(p, i, 1, thresh2))
{ {
DoFilter2(p, offset + (i * stride), 1); DoFilter2(p, i, 1);
} }
} }
} }
@ -656,15 +676,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop24(u, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); int offset4mulstride = offset + (4 * stride);
FilterLoop24(v, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop24(u, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh);
FilterLoop24(v, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop24(u, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); int offsetPlus4 = offset + 4;
FilterLoop24(v, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop24(u, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh);
FilterLoop24(v, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -683,9 +705,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int YuvToR(int y, int v) public static int YuvToB(int y, int u)
{ {
return Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234); return Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -695,9 +717,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int YuvToB(int y, int u) public static int YuvToR(int y, int v)
{ {
return Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); return Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234);
} }
// Complex In-loop filtering (Paragraph 15.3) // Complex In-loop filtering (Paragraph 15.3)
@ -776,7 +798,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void DoFilter4(Span<byte> p, int offset, int step) private static void DoFilter4(Span<byte> p, int offset, int step)
{ {
// 4 pixels in, 4 pixels out. // 4 pixels in, 4 pixels out.
int p1 = p[offset - (2 * step)]; int offsetMinus2Step = offset - (2 * step);
int p1 = p[offsetMinus2Step];
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];
@ -784,7 +807,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int a1 = WebPLookupTables.Sclip2[(a + 4) >> 3]; int a1 = WebPLookupTables.Sclip2[(a + 4) >> 3];
int a2 = WebPLookupTables.Sclip2[(a + 3) >> 3]; int a2 = WebPLookupTables.Sclip2[(a + 3) >> 3];
int a3 = (a1 + 1) >> 1; int a3 = (a1 + 1) >> 1;
p[offset - (2 * step)] = WebPLookupTables.Clip1[p1 + a3]; p[offsetMinus2Step] = WebPLookupTables.Clip1[p1 + a3];
p[offset - step] = WebPLookupTables.Clip1[p0 + a2]; p[offset - step] = WebPLookupTables.Clip1[p0 + a2];
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebPLookupTables.Clip1[q0 - a1];
p[offset + step] = WebPLookupTables.Clip1[q1 - a3]; p[offset + step] = WebPLookupTables.Clip1[q1 - a3];
@ -793,24 +816,27 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static void DoFilter6(Span<byte> p, int offset, int step) private static void DoFilter6(Span<byte> p, int offset, int step)
{ {
// 6 pixels in, 6 pixels out. // 6 pixels in, 6 pixels out.
int p2 = p[offset - (3 * step)]; int step2 = 2 * step;
int p1 = p[offset - (2 * step)]; int step3 = 3 * step;
int p0 = p[offset - step]; int offsetMinusStep = offset - step;
int p2 = p[offset - step3];
int p1 = p[offset - step2];
int p0 = p[offsetMinusStep];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
int q2 = p[offset + (2 * step)]; int q2 = p[offset + step2];
int a = WebPLookupTables.Sclip1[(3 * (q0 - p0)) + WebPLookupTables.Sclip1[p1 - q1]]; int a = WebPLookupTables.Sclip1[(3 * (q0 - p0)) + WebPLookupTables.Sclip1[p1 - q1]];
// a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9] // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9]
int a1 = ((27 * a) + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7 int a1 = ((27 * a) + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
int a2 = ((18 * a) + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7 int a2 = ((18 * a) + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
int a3 = ((9 * a) + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7 int a3 = ((9 * a) + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
p[offset - (3 * step)] = WebPLookupTables.Clip1[p2 + a3]; p[offset - step3] = WebPLookupTables.Clip1[p2 + a3];
p[offset - (2 * step)] = WebPLookupTables.Clip1[p1 + a2]; p[offset - step2] = WebPLookupTables.Clip1[p1 + a2];
p[offset - step] = WebPLookupTables.Clip1[p0 + a1]; p[offsetMinusStep] = WebPLookupTables.Clip1[p0 + a1];
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebPLookupTables.Clip1[q0 - a1];
p[offset + step] = WebPLookupTables.Clip1[q1 - a2]; p[offset + step] = WebPLookupTables.Clip1[q1 - a2];
p[offset + (2 * step)] = WebPLookupTables.Clip1[q2 - a3]; p[offset + step2] = WebPLookupTables.Clip1[q2 - a3];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -825,14 +851,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
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)
{ {
int step2 = 2 * step;
int step3 = 3 * step;
int p3 = p[offset - (4 * step)]; int p3 = p[offset - (4 * step)];
int p2 = p[offset - (3 * step)]; int p2 = p[offset - step3];
int p1 = p[offset - (2 * step)]; int p1 = p[offset - step2];
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];
int q2 = p[offset + (2 * step)]; int q2 = p[offset + step2];
int q3 = p[offset + (3 * step)]; 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;
@ -862,7 +890,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, int x, int y, int v) private static void Store(Span<byte> dst, int x, int y, int v)
{ {
dst[x + (y * WebPConstants.Bps)] = Clip8B(dst[x + (y * WebPConstants.Bps)] + (v >> 3)); var index = x + (y * WebPConstants.Bps);
dst[index] = Clip8B(dst[index] + (v >> 3));
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -889,32 +918,34 @@ namespace SixLabors.ImageSharp.Formats.WebP
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8B(int v) private static byte Clip8B(int v)
{ {
return (byte)((v & ~0xff) is 0 ? v : (v < 0) ? 0 : 255); return (byte)((v & ~0xff) == 0 ? v : (v < 0) ? 0 : 255);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
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) is 0) ? (v >> 6) : (v < 0) ? 0 : 255); return (byte)(((v & ~yuvMask) == 0) ? (v >> 6) : (v < 0) ? 0 : 255);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Put8x8uv(byte value, Span<byte> dst) private static void Put8x8uv(byte value, Span<byte> dst)
{ {
for (int j = 0; j < 8; ++j) int end = 8 * WebPConstants.Bps;
for (int j = 0; j < end; j += WebPConstants.Bps)
{ {
// memset(dst + j * BPS, value, 8); // memset(dst + j * BPS, value, 8);
Memset(dst, value, j * WebPConstants.Bps, 8); Memset(dst, value, j, 8);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Memset(Span<byte> dst, byte value, int startIdx, int count) private static void Memset(Span<byte> dst, byte value, int startIdx, int count)
{ {
for (int i = 0; i < count; i++) int end = startIdx + count;
for (int i = startIdx; i < end; i++)
{ {
dst[startIdx + i] = value; dst[i] = value;
} }
} }

12
src/ImageSharp/Formats/WebP/Vp8BitReader.cs

@ -58,6 +58,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="startPos">Start index in the data array. Defaults to 0.</param> /// <param name="startPos">Start index in the data array. Defaults to 0.</param>
public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0) public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0)
{ {
Guard.MustBeLessThan(imageDataSize, int.MaxValue, nameof(imageDataSize));
this.ImageDataSize = imageDataSize; this.ImageDataSize = imageDataSize;
this.PartitionLength = partitionLength; this.PartitionLength = partitionLength;
this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
@ -133,8 +135,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
ulong value = this.value >> pos; ulong value = this.value >> pos;
ulong mask = (split - value) >> 31; // -1 or 0 ulong mask = (split - value) >> 31; // -1 or 0
this.bits -= 1; this.bits -= 1;
this.range += (uint)mask; this.range = (this.range + (uint)mask) | 1;
this.range |= 1;
this.value -= ((split + 1) & mask) << pos; this.value -= ((split + 1) & mask) << pos;
return (v ^ (int)mask) - (int)mask; return (v ^ (int)mask) - (int)mask;
@ -149,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public uint ReadValue(int nBits) public uint ReadValue(int nBits)
{ {
Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); Guard.MustBeGreaterThan(nBits, 0, nameof(nBits));
Guard.MustBeLessThanOrEqualTo(nBits, 32, nameof(nBits));
uint v = 0; uint v = 0;
while (nBits-- > 0) while (nBits-- > 0)
@ -162,6 +164,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int ReadSignedValue(int nBits) public int ReadSignedValue(int nBits)
{ {
Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); Guard.MustBeGreaterThan(nBits, 0, nameof(nBits));
Guard.MustBeLessThanOrEqualTo(nBits, 32, nameof(nBits));
int value = (int)this.ReadValue(nBits); int value = (int)this.ReadValue(nBits);
return this.ReadValue(1) != 0 ? -value : value; return this.ReadValue(1) != 0 ? -value : value;
@ -169,13 +172,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void InitBitreader(uint size, int pos = 0) private void InitBitreader(uint size, int pos = 0)
{ {
var 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.
this.eof = false; this.eof = false;
this.pos = pos; this.pos = pos;
this.bufferEnd = (uint)(pos + size); this.bufferEnd = (uint)posPlusSize;
this.bufferMax = (uint)(size > 8 ? pos + size - 8 + 1 : pos); this.bufferMax = (uint)(size > 8 ? posPlusSize - 8 + 1 : pos);
this.LoadNewBytes(); this.LoadNewBytes();
} }

20
src/ImageSharp/Formats/WebP/Vp8Decoder.cs

@ -67,8 +67,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
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);
this.CacheU = memoryAllocator.Allocate<byte>((16 * this.CacheUvStride) + extraUv); int cacheUvSize = (16 * this.CacheUvStride) + extraUv;
this.CacheV = memoryAllocator.Allocate<byte>((16 * this.CacheUvStride) + extraUv); this.CacheU = memoryAllocator.Allocate<byte>(cacheUvSize);
this.CacheV = memoryAllocator.Allocate<byte>(cacheUvSize);
this.TmpYBuffer = memoryAllocator.Allocate<byte>((int)width); this.TmpYBuffer = memoryAllocator.Allocate<byte>((int)width);
this.TmpUBuffer = memoryAllocator.Allocate<byte>((int)width); this.TmpUBuffer = memoryAllocator.Allocate<byte>((int)width);
this.TmpVBuffer = memoryAllocator.Allocate<byte>((int)width); this.TmpVBuffer = memoryAllocator.Allocate<byte>((int)width);
@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public LoopFilter Filter { get; set; } public LoopFilter Filter { get; set; }
/// <summary> /// <summary>
/// Gets the filter strengths. /// Gets the pre-calculated per-segment filter strengths.
/// </summary> /// </summary>
public Vp8FilterInfo[,] FilterStrength { get; } public Vp8FilterInfo[,] FilterStrength { get; }
@ -233,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public IMemoryOwner<byte> Pixels { get; } public IMemoryOwner<byte> Pixels { get; }
/// <summary> /// <summary>
/// Gets or sets filter strength info. /// Gets or sets filter info.
/// </summary> /// </summary>
public Vp8FilterInfo[] FilterInfo { get; set; } public Vp8FilterInfo[] FilterInfo { get; set; }
@ -249,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
get get
{ {
if (this.leftMacroBlock is null) if (this.leftMacroBlock == null)
{ {
this.leftMacroBlock = new Vp8MacroBlock(); this.leftMacroBlock = new Vp8MacroBlock();
} }
@ -268,7 +269,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public void PrecomputeFilterStrengths() public void PrecomputeFilterStrengths()
{ {
if (this.Filter is LoopFilter.None) if (this.Filter == LoopFilter.None)
{ {
return; return;
} }
@ -320,9 +321,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
iLevel >>= 1; iLevel >>= 1;
} }
if (iLevel > 9 - hdr.Sharpness) int iLevelCap = 9 - hdr.Sharpness;
if (iLevel > iLevelCap)
{ {
iLevel = 9 - hdr.Sharpness; iLevel = iLevelCap;
} }
} }
@ -340,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
info.Limit = 0; // no filtering. info.Limit = 0; // no filtering.
} }
info.UseInnerFiltering = i4x4 is 1; info.UseInnerFiltering = i4x4 == 1;
} }
} }
} }

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

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Vp8FilterInfo"/> class. /// Initializes a new instance of the <see cref="Vp8FilterInfo"/> class.
/// </summary> /// </summary>
/// <param name="other">The filter info to create an instance from.</param> /// <param name="other">The filter info to create a copy from.</param>
public Vp8FilterInfo(Vp8FilterInfo other) public Vp8FilterInfo(Vp8FilterInfo other)
{ {
this.Limit = other.Limit; this.Limit = other.Limit;

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

@ -132,8 +132,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (!this.Eos && nBits <= Vp8LMaxNumBitRead) if (!this.Eos && nBits <= Vp8LMaxNumBitRead)
{ {
ulong val = this.PrefetchBits() & this.bitMask[nBits]; ulong val = this.PrefetchBits() & this.bitMask[nBits];
int newBits = this.bitPos + nBits; this.bitPos += nBits;
this.bitPos = newBits;
this.ShiftBytes(); this.ShiftBytes();
return (uint)val; return (uint)val;
} }
@ -193,6 +192,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return this.Eos || ((this.pos == this.len) && (this.bitPos > Lbits)); return this.Eos || ((this.pos == this.len) && (this.bitPos > Lbits));
} }
[MethodImpl(InliningOptions.ShortMethod)]
private void DoFillBitWindow() private void DoFillBitWindow()
{ {
this.ShiftBytes(); this.ShiftBytes();

16
src/ImageSharp/Formats/WebP/Vp8Profile.cs

@ -4,7 +4,21 @@
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary> /// <summary>
/// The version number setting enables or disables certain features in the bitstream. /// The version number of the frame header enables or disables certain features in the bitstream.
/// +---------+-------------------------+-------------+
/// | Version | Reconstruction Filter | Loop Filter |
/// +---------+-------------------------+-------------+
/// | 0 | Bicubic | Normal |
/// | | | |
/// | 1 | Bilinear | Simple |
/// | | | |
/// | 2 | Bilinear | None |
/// | | | |
/// | 3 | None | None |
/// | | | |
/// | Other | Reserved for future use | |
/// +---------+-------------------------+-------------+
/// See paragraph 9, https://tools.ietf.org/html/rfc6386.
/// </summary> /// </summary>
internal class Vp8Profile internal class Vp8Profile
{ {

12
src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs

@ -5,6 +5,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal class Vp8QuantMatrix internal class Vp8QuantMatrix
{ {
private int dither;
public int[] Y1Mat { get; } = new int[2]; public int[] Y1Mat { get; } = new int[2];
public int[] Y2Mat { get; } = new int[2]; public int[] Y2Mat { get; } = new int[2];
@ -19,6 +21,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets the dithering amplitude (0 = off, max=255). /// Gets or sets the dithering amplitude (0 = off, max=255).
/// </summary> /// </summary>
public int Dither { get; set; } public int Dither
{
get => this.dither;
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 255, nameof(this.dither));
this.dither = value;
}
}
} }
} }

10
src/ImageSharp/Formats/WebP/WebPChunkType.cs

@ -6,15 +6,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Contains a list of different webp chunk types. /// Contains a list of different webp chunk types.
/// </summary> /// </summary>
/// <remarks>See WebP Container Specification for more details: https://developers.google.com/speed/webp/docs/riff_container </remarks>
public enum WebPChunkType : uint public enum WebPChunkType : uint
{ {
/// <summary> /// <summary>
/// Header signaling the use of VP8 format. /// Header signaling the use of the VP8 format.
/// </summary> /// </summary>
Vp8 = 0x56503820U, Vp8 = 0x56503820U,
/// <summary> /// <summary>
/// Header for a extended-VP8 chunk. /// Header signaling the image uses lossless encoding.
/// </summary> /// </summary>
Vp8L = 0x5650384CU, Vp8L = 0x5650384CU,
@ -52,10 +53,5 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// For animated images, this chunk contains information about a single frame. If the Animation flag is not set, then this chunk SHOULD NOT be present. /// For animated images, this chunk contains information about a single frame. If the Animation flag is not set, then this chunk SHOULD NOT be present.
/// </summary> /// </summary>
Animation = 0x414E4D46, Animation = 0x414E4D46,
/// <summary>
/// TODO: not sure what this is for yet.
/// </summary>
FRGM = 0x4652474D,
} }
} }

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

@ -105,9 +105,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static readonly int[] AlphabetSize = public static readonly int[] AlphabetSize =
{ {
NumLiteralCodes + NumLengthCodes, NumLiteralCodes + NumLengthCodes,
NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, NumLiteralCodes,
NumDistanceCodes NumDistanceCodes
}; };
// VP8 constants from here on: // VP8 constants from here on:

44
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.currentStream = stream; this.currentStream = stream;
uint fileSize = this.ReadImageHeader(); uint fileSize = this.ReadImageHeader();
using var imageInfo = this.ReadVp8Info(); using WebPImageInfo imageInfo = this.ReadVp8Info();
if (imageInfo.Features != null && imageInfo.Features.Animation) if (imageInfo.Features != null && imageInfo.Features.Animation)
{ {
WebPThrowHelper.ThrowNotSupportedException("Animations are not supported"); WebPThrowHelper.ThrowNotSupportedException("Animations are not supported");
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
var image = new Image<TPixel>(this.configuration, (int)imageInfo.Width, (int)imageInfo.Height, this.Metadata); var image = new Image<TPixel>(this.configuration, (int)imageInfo.Width, (int)imageInfo.Height, this.Metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (imageInfo.IsLossLess) if (imageInfo.IsLossless)
{ {
var losslessDecoder = new WebPLosslessDecoder(imageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration); var losslessDecoder = new WebPLosslessDecoder(imageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
losslessDecoder.Decode(pixels, image.Width, image.Height); losslessDecoder.Decode(pixels, image.Width, image.Height);
@ -159,11 +159,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
return this.ReadVp8LHeader(); return this.ReadVp8LHeader();
case WebPChunkType.Vp8X: case WebPChunkType.Vp8X:
return this.ReadVp8XHeader(); return this.ReadVp8XHeader();
default:
WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header");
return new WebPImageInfo(); // this return will never be reached, because throw helper will throw an exception.
} }
WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header");
return new WebPImageInfo();
} }
/// <summary> /// <summary>
@ -275,8 +274,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.currentStream.Read(this.buffer, 0, 3); this.currentStream.Read(this.buffer, 0, 3);
uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16)); uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16));
remaining -= 3; remaining -= 3;
bool isKeyFrame = (frameTag & 0x1) is 0; bool isNoKeyFrame = (frameTag & 0x1) == 1;
if (!isKeyFrame) if (isNoKeyFrame)
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 header indicates the image is not a key frame"); WebPThrowHelper.ThrowImageFormatException("VP8 header indicates the image is not a key frame");
} }
@ -287,8 +286,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException($"VP8 header indicates unknown profile {version}"); WebPThrowHelper.ThrowImageFormatException($"VP8 header indicates unknown profile {version}");
} }
bool showFrame = ((frameTag >> 4) & 0x1) is 1; bool invisibleFrame = ((frameTag >> 4) & 0x1) == 0;
if (!showFrame) if (invisibleFrame)
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible"); WebPThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible");
} }
@ -314,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint height = tmp & 0x3fff; uint height = tmp & 0x3fff;
sbyte yScale = (sbyte)(tmp >> 6); sbyte yScale = (sbyte)(tmp >> 6);
remaining -= 7; remaining -= 7;
if (width is 0 || height is 0) if (width == 0 || height == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); WebPThrowHelper.ThrowImageFormatException("width or height can not be zero");
} }
@ -344,8 +343,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
Height = height, Height = height,
XScale = xScale, XScale = xScale,
YScale = yScale, YScale = yScale,
BitsPerPixel = features?.Alpha is true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24, BitsPerPixel = features?.Alpha == true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24,
IsLossLess = false, IsLossless = false,
Features = features, Features = features,
Vp8Profile = (sbyte)version, Vp8Profile = (sbyte)version,
Vp8FrameHeader = vp8FrameHeader, Vp8FrameHeader = vp8FrameHeader,
@ -377,9 +376,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
// The first 28 bits of the bitstream specify the width and height of the image. // The first 28 bits of the bitstream specify the width and height of the image.
uint width = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1; uint width = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1;
uint height = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1; uint height = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1;
if (width is 0 || height is 0) if (width == 1 || height == 1)
{ {
WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); WebPThrowHelper.ThrowImageFormatException("invalid width or height read");
} }
// The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise. // The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise.
@ -399,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Width = width, Width = width,
Height = height, Height = height,
BitsPerPixel = WebPBitsPerPixel.Pixel32, BitsPerPixel = WebPBitsPerPixel.Pixel32,
IsLossLess = true, IsLossless = true,
Features = features, Features = features,
Vp8LBitReader = bitReader Vp8LBitReader = bitReader
}; };
@ -455,18 +454,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="features">The webp features.</param> /// <param name="features">The webp features.</param>
private void ParseOptionalChunks(WebPFeatures features) private void ParseOptionalChunks(WebPFeatures features)
{ {
if (this.IgnoreMetadata || (features.ExifProfile is false && features.XmpMetaData is false)) if (this.IgnoreMetadata || (features.ExifProfile == false && features.XmpMetaData == false))
{ {
return; return;
} }
while (this.currentStream.Position < this.currentStream.Length) var streamLength = this.currentStream.Length;
while (this.currentStream.Position < streamLength)
{ {
// Read chunk header. // Read chunk header.
WebPChunkType chunkType = this.ReadChunkType(); WebPChunkType chunkType = this.ReadChunkType();
uint chunkLength = this.ReadChunkSize(); uint chunkLength = this.ReadChunkSize();
if (chunkType is WebPChunkType.Exif) if (chunkType == WebPChunkType.Exif)
{ {
var exifData = new byte[chunkLength]; var exifData = new byte[chunkLength];
this.currentStream.Read(exifData, 0, (int)chunkLength); this.currentStream.Read(exifData, 0, (int)chunkLength);
@ -488,7 +488,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </exception> /// </exception>
private WebPChunkType ReadChunkType() private WebPChunkType ReadChunkType()
{ {
if (this.currentStream.Read(this.buffer, 0, 4) is 4) if (this.currentStream.Read(this.buffer, 0, 4) == 4)
{ {
var chunkType = (WebPChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); var chunkType = (WebPChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
this.webpMetadata.ChunkTypes.Enqueue(chunkType); this.webpMetadata.ChunkTypes.Enqueue(chunkType);
@ -505,10 +505,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <returns>The chunk size in bytes.</returns> /// <returns>The chunk size in bytes.</returns>
private uint ReadChunkSize() private uint ReadChunkSize()
{ {
if (this.currentStream.Read(this.buffer, 0, 4) is 4) if (this.currentStream.Read(this.buffer, 0, 4) == 4)
{ {
uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
return (chunkSize % 2 is 0) ? chunkSize : chunkSize + 1; return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
} }
throw new ImageFormatException("Invalid WebP data."); throw new ImageFormatException("Invalid WebP data.");

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

@ -12,12 +12,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
internal class WebPFeatures : IDisposable internal class WebPFeatures : IDisposable
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image has a ICC Profile. /// Gets or sets a value indicating whether this image has an ICC Profile.
/// </summary> /// </summary>
public bool IccProfile { get; set; } public bool IccProfile { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image has a alpha channel. /// Gets or sets a value indicating whether this image has an alpha channel.
/// </summary> /// </summary>
public bool Alpha { get; set; } public bool Alpha { get; set; }
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public byte AlphaChunkHeader { get; set; } public byte AlphaChunkHeader { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image has a EXIF Profile. /// Gets or sets a value indicating whether this image has an EXIF Profile.
/// </summary> /// </summary>
public bool ExifProfile { get; set; } public bool ExifProfile { get; set; }
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public bool XmpMetaData { get; set; } public bool XmpMetaData { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image is a animation. /// Gets or sets a value indicating whether this image is an animation.
/// </summary> /// </summary>
public bool Animation { get; set; } public bool Animation { get; set; }

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

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image uses lossless compression. /// Gets or sets a value indicating whether this image uses lossless compression.
/// </summary> /// </summary>
public bool IsLossLess { get; set; } public bool IsLossless { get; set; }
/// <summary> /// <summary>
/// Gets or sets additional features present in a VP8X image. /// Gets or sets additional features present in a VP8X image.
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8FrameHeader Vp8FrameHeader { get; set; } public Vp8FrameHeader Vp8FrameHeader { get; set; }
/// <summary> /// <summary>
/// Gets or sets the VP8L bitreader. Will be null, if its not lossless image. /// Gets or sets the VP8L bitreader. Will be null, if its not a lossless image.
/// </summary> /// </summary>
public Vp8LBitReader Vp8LBitReader { get; set; } = null; public Vp8LBitReader Vp8LBitReader { get; set; } = null;

90
src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

@ -37,13 +37,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
private static readonly int BitsSpecialMarker = 0x100; private const int BitsSpecialMarker = 0x100;
private static readonly uint PackedNonLiteralCode = 0; private const uint PackedNonLiteralCode = 0;
private static readonly int CodeToPlaneCodes = WebPLookupTables.CodeToPlane.Length; private static readonly int CodeToPlaneCodes = WebPLookupTables.CodeToPlane.Length;
private static readonly int FixedTableSize = (630 * 3) + 410; // Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha and distance alphabets are constant (256 for red, blue and alpha, 40 for
// distance) and lookup table sizes for them in worst case are 630 and 410 respectively. Size of green alphabet depends on color cache size and is equal
// to 256 (green component values) + 24 (length prefix values) + color_cache_size (between 0 and 2048).
// All values computed for 8-bit first level lookup with Mark Adler's tool:
// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c
private const int FixedTableSize = (630 * 3) + 410;
private static readonly int[] TableSize = private static readonly int[] TableSize =
{ {
@ -133,14 +138,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// Color cache. // Color cache.
bool colorCachePresent = this.bitReader.ReadBit(); bool isColorCachePresent = this.bitReader.ReadBit();
int colorCacheBits = 0; int colorCacheBits = 0;
int colorCacheSize = 0; int colorCacheSize = 0;
if (colorCachePresent) if (isColorCachePresent)
{ {
colorCacheBits = (int)this.bitReader.ReadValue(4); colorCacheBits = (int)this.bitReader.ReadValue(4);
bool coloCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits; bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits;
if (!coloCacheBitsIsValid) if (!colorCacheBitsIsValid)
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
} }
@ -151,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
decoder.Metadata.ColorCacheSize = colorCacheSize; decoder.Metadata.ColorCacheSize = colorCacheSize;
// Finish setting up the color-cache. // Finish setting up the color-cache.
if (colorCachePresent) if (isColorCachePresent)
{ {
decoder.Metadata.ColorCache = new ColorCache(); decoder.Metadata.ColorCache = new ColorCache();
colorCacheSize = 1 << colorCacheBits; colorCacheSize = 1 << colorCacheBits;
@ -192,9 +197,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
ApplyInverseTransforms(decoder, pixelData, this.memoryAllocator); ApplyInverseTransforms(decoder, pixelData, this.memoryAllocator);
Span<byte> pixelDataAsBytes = MemoryMarshal.Cast<uint, byte>(pixelData); Span<byte> pixelDataAsBytes = MemoryMarshal.Cast<uint, byte>(pixelData);
int widthMul4 = width * 4;
for (int y = 0; y < decoder.Height; y++) for (int y = 0; y < decoder.Height; y++)
{ {
Span<byte> row = pixelDataAsBytes.Slice(y * width * 4, width * 4); Span<byte> row = pixelDataAsBytes.Slice(y * widthMul4, widthMul4);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgra32Bytes( PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration, this.configuration,
@ -211,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int height = decoder.Height; int height = decoder.Height;
int row = lastPixel / width; int row = lastPixel / width;
int col = lastPixel % width; int col = lastPixel % width;
int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; const int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes;
int colorCacheSize = decoder.Metadata.ColorCacheSize; int colorCacheSize = decoder.Metadata.ColorCacheSize;
ColorCache colorCache = decoder.Metadata.ColorCache; ColorCache colorCache = decoder.Metadata.ColorCache;
int colorCacheLimit = lenCodeLimit + colorCacheSize; int colorCacheLimit = lenCodeLimit + colorCacheSize;
@ -224,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (decodedPixels < totalPixels) while (decodedPixels < totalPixels)
{ {
int code; int code;
if ((col & mask) is 0) if ((col & mask) == 0)
{ {
hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row); hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row);
} }
@ -279,8 +285,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
break; break;
} }
int pixelIdx = decodedPixels; pixelData[decodedPixels] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
pixelData[pixelIdx] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
} }
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached); this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
@ -305,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (col >= width) while (col >= width)
{ {
col -= width; col -= width;
++row; row++;
} }
if ((col & mask) != 0) if ((col & mask) != 0)
@ -344,12 +349,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached) private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached)
{ {
++col; col++;
decodedPixels++; decodedPixels++;
if (col >= width) if (col >= width)
{ {
col = 0; col = 0;
++row; row++;
if (colorCache != null) if (colorCache != null)
{ {
@ -373,13 +378,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (allowRecursion && this.bitReader.ReadBit()) if (allowRecursion && this.bitReader.ReadBit())
{ {
// Use meta Huffman codes. // Use meta Huffman codes.
uint huffmanPrecision = this.bitReader.ReadValue(3) + 2; int huffmanPrecision = (int)(this.bitReader.ReadValue(3) + 2);
int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision); int huffmanXSize = LosslessUtils.SubSampleSize(xSize, huffmanPrecision);
int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision); int huffmanYSize = LosslessUtils.SubSampleSize(ySize, huffmanPrecision);
int huffmanPixels = huffmanXSize * huffmanYSize; int huffmanPixels = huffmanXSize * huffmanYSize;
IMemoryOwner<uint> huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false); IMemoryOwner<uint> huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
Span<uint> huffmanImageSpan = huffmanImage.GetSpan(); Span<uint> huffmanImageSpan = huffmanImage.GetSpan();
decoder.Metadata.HuffmanSubSampleBits = (int)huffmanPrecision; decoder.Metadata.HuffmanSubSampleBits = huffmanPrecision;
for (int i = 0; i < huffmanPixels; ++i) for (int i = 0; i < huffmanPixels; ++i)
{ {
// The huffman data is stored in red and green bytes. // The huffman data is stored in red and green bytes.
@ -431,19 +436,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable); int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable);
if (size is 0) if (size == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero");
} }
hTreeGroup.HTrees.Add(huffmanTable.ToArray()); hTreeGroup.HTrees.Add(huffmanTable.ToArray());
HuffmanCode huffTableZero = huffmanTable[0];
if (isTrivialLiteral && LiteralMap[j] == 1) if (isTrivialLiteral && LiteralMap[j] == 1)
{ {
isTrivialLiteral = huffmanTable[0].BitsUsed == 0; isTrivialLiteral = huffTableZero.BitsUsed == 0;
} }
totalSize += huffmanTable[0].BitsUsed; totalSize += huffTableZero.BitsUsed;
huffmanTable = huffmanTable.Slice(size); huffmanTable = huffmanTable.Slice(size);
if (j <= HuffIndex.Alpha) if (j <= HuffIndex.Alpha)
@ -452,9 +458,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
int k; int k;
for (k = 1; k < alphabetSize; ++k) for (k = 1; k < alphabetSize; ++k)
{ {
if (codeLengths[k] > localMaxBits) var codeLengthK = codeLengths[k];
if (codeLengthK > localMaxBits)
{ {
localMaxBits = codeLengths[k]; localMaxBits = codeLengthK;
} }
} }
@ -501,19 +508,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (simpleCode) if (simpleCode)
{ {
// (i) Simple Code Length Code. // (i) Simple Code Length Code.
// This variant is used in the special case when only 1 or 2 Huffman code lengths are non - zero, // This variant is used in the special case when only 1 or 2 Huffman code lengths are non-zero,
// and are in the range of[0, 255].All other Huffman code lengths are implicitly zeros. // and are in the range of[0, 255]. All other Huffman code lengths are implicitly zeros.
// Read symbols, codes & code lengths directly. // Read symbols, codes & code lengths directly.
uint numSymbols = this.bitReader.ReadValue(1) + 1; uint numSymbols = this.bitReader.ReadValue(1) + 1;
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 is 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.
if (numSymbols is 2) if (numSymbols == 2)
{ {
symbol = this.bitReader.ReadValue(8); symbol = this.bitReader.ReadValue(8);
codeLengths[symbol] = 1; codeLengths[symbol] = 1;
@ -550,7 +557,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int symbol = 0; int symbol = 0;
int prevCodeLen = WebPConstants.DefaultCodeLength; int prevCodeLen = WebPConstants.DefaultCodeLength;
int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes); int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes);
if (size is 0) if (size == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("Error building huffman table"); WebPThrowHelper.ThrowImageFormatException("Error building huffman table");
} }
@ -567,7 +574,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (symbol < numSymbols) while (symbol < numSymbols)
{ {
if (maxSymbol-- is 0) if (maxSymbol-- == 0)
{ {
break; break;
} }
@ -667,7 +674,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// A WebP lossless image can go through four different types of transformation before being entropy encoded. /// A WebP lossless image can go through four different types of transformation before being entropy encoded.
/// This will reverses the transformations, if any are present. /// This will reverse the transformations, if any are present.
/// </summary> /// </summary>
/// <param name="decoder">The decoder holding the transformation infos.</param> /// <param name="decoder">The decoder holding the transformation infos.</param>
/// <param name="pixelData">The pixel data to apply the transformation.</param> /// <param name="pixelData">The pixel data to apply the transformation.</param>
@ -677,13 +684,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
List<Vp8LTransform> transforms = decoder.Transforms; List<Vp8LTransform> transforms = decoder.Transforms;
for (int i = transforms.Count - 1; i >= 0; i--) for (int i = transforms.Count - 1; i >= 0; i--)
{ {
Vp8LTransformType transformType = transforms[i].TransformType; Vp8LTransform transform = transforms[i];
Vp8LTransformType transformType = transform.TransformType;
switch (transformType) switch (transformType)
{ {
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
using (IMemoryOwner<uint> output = memoryAllocator.Allocate<uint>(pixelData.Length, AllocationOptions.Clean)) using (IMemoryOwner<uint> output = memoryAllocator.Allocate<uint>(pixelData.Length, AllocationOptions.Clean))
{ {
LosslessUtils.PredictorInverseTransform(transforms[i], pixelData, output.GetSpan()); LosslessUtils.PredictorInverseTransform(transform, pixelData, output.GetSpan());
} }
break; break;
@ -691,10 +699,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
LosslessUtils.AddGreenToBlueAndRed(pixelData); LosslessUtils.AddGreenToBlueAndRed(pixelData);
break; break;
case Vp8LTransformType.CrossColorTransform: case Vp8LTransformType.CrossColorTransform:
LosslessUtils.ColorSpaceInverseTransform(transforms[i], pixelData); LosslessUtils.ColorSpaceInverseTransform(transform, pixelData);
break; break;
case Vp8LTransformType.ColorIndexingTransform: case Vp8LTransformType.ColorIndexingTransform:
LosslessUtils.ColorIndexInverseTransform(transforms[i], pixelData); LosslessUtils.ColorIndexInverseTransform(transform, pixelData);
break; break;
} }
} }
@ -719,13 +727,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
int end = width * height; // End of data. int end = width * height; // End of data.
int last = end; // Last pixel to decode. int last = end; // Last pixel to decode.
int lastRow = height; int lastRow = height;
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.
if ((col & mask) is 0) if ((col & mask) == 0)
{ {
htreeGroup = GetHTreeGroupForPos(hdr, col, row); htreeGroup = GetHTreeGroupForPos(hdr, col, row);
} }
@ -743,7 +751,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
col = 0; col = 0;
++row; ++row;
if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0))
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -773,7 +781,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
col -= width; col -= width;
++row; ++row;
if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0))
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -802,7 +810,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
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 is 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)

234
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Decode image data. // Decode image data.
this.ParseFrame(decoder, io); this.ParseFrame(decoder, io);
if (info.Features?.Alpha is true) if (info.Features?.Alpha == true)
{ {
using (var alphaDecoder = new AlphaDecoder( using (var alphaDecoder = new AlphaDecoder(
width, width,
@ -112,35 +112,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> pixels, IMemoryOwner<byte> alpha = null) private void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
if (alpha != null) int widthMul3 = width * 3;
{
TPixel color = default;
Span<byte> alphaSpan = alpha.Memory.Span;
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
int offset = (y * width) + x;
int idxBgr = offset * 3;
byte b = pixelData[idxBgr];
byte g = pixelData[idxBgr + 1];
byte r = pixelData[idxBgr + 2];
byte a = alphaSpan[offset];
color.FromBgra32(new Bgra32(r, g, b, a));
pixelRow[x] = color;
}
}
return;
}
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
Span<byte> row = pixelData.Slice(y * width * 3, width * 3); Span<byte> row = pixelData.Slice(y * widthMul3, widthMul3);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y); Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgr24Bytes( PixelOperations<TPixel>.Instance.FromBgr24Bytes(
this.configuration, this.configuration,
@ -150,6 +128,29 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> pixels, IMemoryOwner<byte> alpha)
where TPixel : unmanaged, IPixel<TPixel>
{
TPixel color = default;
Span<byte> alphaSpan = alpha.Memory.Span;
for (int y = 0; y < height; y++)
{
int yMulWidth = y * width;
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
int offset = yMulWidth + x;
int idxBgr = offset * 3;
byte b = pixelData[idxBgr];
byte g = pixelData[idxBgr + 1];
byte r = pixelData[idxBgr + 2];
byte a = alphaSpan[offset];
color.FromBgra32(new Bgra32(r, g, b, a));
pixelRow[x] = color;
}
}
}
private void ParseFrame(Vp8Decoder dec, Vp8Io io) private void ParseFrame(Vp8Decoder dec, Vp8Io io)
{ {
for (dec.MbY = 0; dec.MbY < dec.BottomRightMbY; ++dec.MbY) for (dec.MbY = 0; dec.MbY < dec.BottomRightMbY; ++dec.MbY)
@ -186,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (dec.SegmentHeader.UpdateMap) if (dec.SegmentHeader.UpdateMap)
{ {
// Hardcoded tree parsing. // Hardcoded tree parsing.
block.Segment = this.bitReader.GetBit((int)dec.Probabilities.Segments[0]) is 0 block.Segment = this.bitReader.GetBit((int)dec.Probabilities.Segments[0]) == 0
? (byte)this.bitReader.GetBit((int)dec.Probabilities.Segments[1]) ? (byte)this.bitReader.GetBit((int)dec.Probabilities.Segments[1])
: (byte)(this.bitReader.GetBit((int)dec.Probabilities.Segments[2]) + 2); : (byte)(this.bitReader.GetBit((int)dec.Probabilities.Segments[2]) + 2);
} }
@ -198,10 +199,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (dec.UseSkipProbability) if (dec.UseSkipProbability)
{ {
block.Skip = this.bitReader.GetBit(dec.SkipProbability) is 1; block.Skip = this.bitReader.GetBit(dec.SkipProbability) == 1;
} }
block.IsI4x4 = this.bitReader.GetBit(145) is 0; block.IsI4x4 = this.bitReader.GetBit(145) == 0;
if (!block.IsI4x4) if (!block.IsI4x4)
{ {
// Hardcoded 16x16 intra-mode decision tree. // Hardcoded 16x16 intra-mode decision tree.
@ -241,8 +242,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// Hardcoded UVMode decision tree. // Hardcoded UVMode decision tree.
block.UvMode = (byte)(this.bitReader.GetBit(142) is 0 ? 0 : block.UvMode = (byte)(this.bitReader.GetBit(142) == 0 ? 0 :
this.bitReader.GetBit(114) is 0 ? 2 : this.bitReader.GetBit(114) == 0 ? 2 :
this.bitReader.GetBit(183) != 0 ? 1 : 3); this.bitReader.GetBit(183) != 0 ? 1 : 3);
} }
@ -268,9 +269,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void ReconstructRow(Vp8Decoder dec) private void ReconstructRow(Vp8Decoder dec)
{ {
int mby = dec.MbY; int mby = dec.MbY;
int yOff = (WebPConstants.Bps * 1) + 8; const int yOff = (WebPConstants.Bps * 1) + 8;
int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps; const int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps;
int vOff = uOff + 16; const int vOff = uOff + 16;
Span<byte> yuv = dec.YuvBuffer.Memory.Span; Span<byte> yuv = dec.YuvBuffer.Memory.Span;
Span<byte> yDst = yuv.Slice(yOff); Span<byte> yDst = yuv.Slice(yOff);
@ -278,15 +279,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
Span<byte> vDst = yuv.Slice(vOff); Span<byte> vDst = yuv.Slice(vOff);
// Initialize left-most block. // Initialize left-most block.
for (int i = 0; i < 16; ++i) var end = 16 * WebPConstants.Bps;
for (int i = 0; i < end; i += WebPConstants.Bps)
{ {
yuv[(i * WebPConstants.Bps) - 1 + yOff] = 129; yuv[i - 1 + yOff] = 129;
} }
for (int i = 0; i < 8; ++i) end = 8 * WebPConstants.Bps;
for (int i = 0; i < end; i += WebPConstants.Bps)
{ {
yuv[(i * WebPConstants.Bps) - 1 + uOff] = 129; yuv[i - 1 + uOff] = 129;
yuv[(i * WebPConstants.Bps) - 1 + vOff] = 129; yuv[i - 1 + vOff] = 129;
} }
// Init top-left sample on left column too. // Init top-left sample on left column too.
@ -364,10 +367,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (mbx >= dec.MbWidth - 1) if (mbx >= dec.MbWidth - 1)
{ {
// On rightmost border. // On rightmost border.
topRight[0] = topYuv.Y[15]; var topYuv15 = topYuv.Y[15];
topRight[1] = topYuv.Y[15]; topRight[0] = topYuv15;
topRight[2] = topYuv.Y[15]; topRight[1] = topYuv15;
topRight[3] = topYuv.Y[15]; topRight[2] = topYuv15;
topRight[3] = topYuv15;
} }
else else
{ {
@ -517,8 +521,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
uDst.Slice(j * WebPConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(j * dec.CacheUvStride)); int jUvStride = j * dec.CacheUvStride;
vDst.Slice(j * WebPConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(j * dec.CacheUvStride)); uDst.Slice(j * WebPConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(jUvStride));
vDst.Slice(j * WebPConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(jUvStride));
} }
} }
} }
@ -539,12 +544,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
int iLevel = filterInfo.InnerLevel; int iLevel = filterInfo.InnerLevel;
int limit = filterInfo.Limit; int limit = filterInfo.Limit;
if (limit is 0) if (limit == 0)
{ {
return; return;
} }
if (dec.Filter is LoopFilter.Simple) if (dec.Filter == LoopFilter.Simple)
{ {
int offset = dec.CacheYOffset + (mbx * 16); int offset = dec.CacheYOffset + (mbx * 16);
if (mbx > 0) if (mbx > 0)
@ -567,7 +572,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
LossyUtils.SimpleVFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit); LossyUtils.SimpleVFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit);
} }
} }
else if (dec.Filter is LoopFilter.Complex) else if (dec.Filter == LoopFilter.Complex)
{ {
int uvBps = dec.CacheUvStride; int uvBps = dec.CacheUvStride;
int yOffset = dec.CacheYOffset + (mbx * 16); int yOffset = dec.CacheYOffset + (mbx * 16);
@ -608,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
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 is 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);
@ -682,7 +687,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int uvw = (mbw + 1) / 2; int uvw = (mbw + 1) / 2;
int y = io.MbY; int y = io.MbY;
if (y is 0) if (y == 0)
{ {
// First line is special cased. We mirror the u/v samples at boundary. // First line is special cased. We mirror the u/v samples at boundary.
this.UpSample(curY, null, curU, curV, curU, curV, dst, null, mbw); this.UpSample(curY, null, curU, curV, curU, curV, dst, null, mbw);
@ -721,7 +726,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
else else
{ {
// Process the very last row of even-sized picture. // Process the very last row of even-sized picture.
if ((yEnd & 1) is 0) if ((yEnd & 1) == 0)
{ {
this.UpSample(curY, null, curU, curV, curU, curV, dst.Slice(bufferStride), null, mbw); this.UpSample(curY, null, curU, curV, curU, curV, dst.Slice(bufferStride), null, mbw);
} }
@ -756,22 +761,23 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint diag03 = (avg + (2 * (tluv + uv))) >> 3; uint diag03 = (avg + (2 * (tluv + uv))) >> 3;
uv0 = (diag12 + tluv) >> 1; uv0 = (diag12 + tluv) >> 1;
uint uv1 = (diag03 + tuv) >> 1; uint uv1 = (diag03 + tuv) >> 1;
LossyUtils.YuvToBgr(topY[(2 * x) - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice(((2 * x) - 1) * xStep)); int xMul2 = x * 2;
LossyUtils.YuvToBgr(topY[(2 * x) - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst.Slice(((2 * x) - 0) * xStep)); LossyUtils.YuvToBgr(topY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((xMul2 - 1) * xStep));
LossyUtils.YuvToBgr(topY[xMul2 - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst.Slice((xMul2 - 0) * xStep));
if (bottomY != null) if (bottomY != null)
{ {
uv0 = (diag03 + luv) >> 1; uv0 = (diag03 + luv) >> 1;
uv1 = (diag12 + uv) >> 1; uv1 = (diag12 + uv) >> 1;
LossyUtils.YuvToBgr(bottomY[(2 * x) - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice(((2 * x) - 1) * xStep)); LossyUtils.YuvToBgr(bottomY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((xMul2 - 1) * xStep));
LossyUtils.YuvToBgr(bottomY[(2 * x) + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice(((2 * x) + 0) * xStep)); LossyUtils.YuvToBgr(bottomY[xMul2 + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice((xMul2 + 0) * xStep));
} }
tluv = tuv; tluv = tuv;
luv = uv; luv = uv;
} }
if ((len & 1) is 0) if ((len & 1) == 0)
{ {
uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2; uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
LossyUtils.YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((len - 1) * xStep)); LossyUtils.YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((len - 1) * xStep));
@ -788,7 +794,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
switch (bits >> 30) switch (bits >> 30)
{ {
case 3: case 3:
LossyUtils.Transform(src, dst, false); LossyUtils.TransformOne(src, dst);
break; break;
case 2: case 2:
LossyUtils.TransformAc3(src, dst); LossyUtils.TransformAc3(src, dst);
@ -823,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Vp8MacroBlock left = dec.LeftMacroBlock; Vp8MacroBlock left = dec.LeftMacroBlock;
Vp8MacroBlock macroBlock = dec.CurrentMacroBlock; Vp8MacroBlock macroBlock = dec.CurrentMacroBlock;
Vp8MacroBlockData blockData = dec.CurrentBlockData; Vp8MacroBlockData blockData = dec.CurrentBlockData;
bool skip = dec.UseSkipProbability ? blockData.Skip : false; bool skip = dec.UseSkipProbability && blockData.Skip;
if (!skip) if (!skip)
{ {
@ -867,7 +873,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
dst[i] = 0; dst[i] = 0;
} }
if (!block.IsI4x4) if (block.IsI4x4)
{
first = 0;
acProba = GetBandsRow(bands, 3);
}
else
{ {
// Parse DC // Parse DC
var dc = new short[16]; var dc = new short[16];
@ -892,11 +903,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
first = 1; first = 1;
acProba = GetBandsRow(bands, 0); acProba = GetBandsRow(bands, 0);
} }
else
{
first = 0;
acProba = GetBandsRow(bands, 3);
}
byte tnz = (byte)(mb.NoneZeroAcDcCoeffs & 0x0f); byte tnz = (byte)(mb.NoneZeroAcDcCoeffs & 0x0f);
byte lnz = (byte)(leftMb.NoneZeroAcDcCoeffs & 0x0f); byte lnz = (byte)(leftMb.NoneZeroAcDcCoeffs & 0x0f);
@ -926,8 +932,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int ch = 0; ch < 4; ch += 2) for (int ch = 0; ch < 4; ch += 2)
{ {
uint nzCoeffs = 0; uint nzCoeffs = 0;
tnz = (byte)(mb.NoneZeroAcDcCoeffs >> (4 + ch)); var chPlus4 = 4 + ch;
lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> (4 + ch)); tnz = (byte)(mb.NoneZeroAcDcCoeffs >> chPlus4);
lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> chPlus4);
for (int y = 0; y < 2; ++y) for (int y = 0; y < 2; ++y)
{ {
int l = lnz & 1; int l = lnz & 1;
@ -957,26 +964,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
block.NonZeroY = nonZeroY; block.NonZeroY = nonZeroY;
block.NonZeroUv = nonZeroUv; block.NonZeroUv = nonZeroUv;
return (nonZeroY | nonZeroUv) is 0; return (nonZeroY | nonZeroUv) == 0;
} }
private int GetCoeffs(Vp8BitReader br, Vp8BandProbas[] prob, int ctx, int[] dq, int n, Span<short> coeffs) private int GetCoeffs(Vp8BitReader br, Vp8BandProbas[] prob, int ctx, int[] dq, int n, Span<short> coeffs)
{ {
// Returns the position of the last non - zero coeff plus one. // Returns the position of the last non-zero coeff plus one.
Vp8ProbaArray p = prob[n].Probabilities[ctx]; Vp8ProbaArray p = prob[n].Probabilities[ctx];
for (; n < 16; ++n) for (; n < 16; ++n)
{ {
if (br.GetBit(p.Probabilities[0]) is 0) if (br.GetBit(p.Probabilities[0]) == 0)
{ {
// Previous coeff was last non - zero coeff. // Previous coeff was last non-zero coeff.
return n; return n;
} }
// Sequence of zero coeffs. // Sequence of zero coeffs.
while (br.GetBit(p.Probabilities[1]) is 0) while (br.GetBit(p.Probabilities[1]) == 0)
{ {
p = prob[++n].Probabilities[0]; p = prob[++n].Probabilities[0];
if (n is 16) if (n == 16)
{ {
return 16; return 16;
} }
@ -984,7 +991,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Non zero coeffs. // Non zero coeffs.
int v; int v;
if (br.GetBit(p.Probabilities[2]) is 0) if (br.GetBit(p.Probabilities[2]) == 0)
{ {
v = 1; v = 1;
p = prob[n + 1].Probabilities[1]; p = prob[n + 1].Probabilities[1];
@ -1006,9 +1013,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
// See section 13 - 2: http://tools.ietf.org/html/rfc6386#section-13.2 // See section 13 - 2: http://tools.ietf.org/html/rfc6386#section-13.2
int v; int v;
if (br.GetBit(p[3]) is 0) if (br.GetBit(p[3]) == 0)
{ {
if (br.GetBit(p[4]) is 0) if (br.GetBit(p[4]) == 0)
{ {
v = 2; v = 2;
} }
@ -1019,9 +1026,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
else else
{ {
if (br.GetBit(p[6]) is 0) if (br.GetBit(p[6]) == 0)
{ {
if (br.GetBit(p[7]) is 0) if (br.GetBit(p[7]) == 0)
{ {
v = 5 + br.GetBit(159); v = 5 + br.GetBit(159);
} }
@ -1077,24 +1084,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
var tmp = new int[16]; var tmp = new int[16];
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
int a0 = input[0 + i] + input[12 + i]; int iPlus4 = 4 + i;
int a1 = input[4 + i] + input[8 + i]; int iPlus8 = 8 + i;
int a2 = input[4 + i] - input[8 + i]; int iPlus12 = 12 + i;
int a3 = input[0 + i] - input[12 + i]; int a0 = input[i] + input[iPlus12];
tmp[0 + i] = a0 + a1; int a1 = input[iPlus4] + input[iPlus8];
tmp[8 + i] = a0 - a1; int a2 = input[iPlus4] - input[iPlus8];
tmp[4 + i] = a3 + a2; int a3 = input[i] - input[iPlus12];
tmp[12 + i] = a3 - a2; tmp[i] = a0 + a1;
tmp[iPlus8] = a0 - a1;
tmp[iPlus4] = a3 + a2;
tmp[iPlus12] = a3 - a2;
} }
int outputOffset = 0; int outputOffset = 0;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
int dc = tmp[0 + (i * 4)] + 3; int imul4 = i * 4;
int a0 = dc + tmp[3 + (i * 4)]; int dc = tmp[0 + imul4] + 3;
int a1 = tmp[1 + (i * 4)] + tmp[2 + (i * 4)]; int a0 = dc + tmp[3 + imul4];
int a2 = tmp[1 + (i * 4)] - tmp[2 + (i * 4)]; int a1 = tmp[1 + imul4] + tmp[2 + imul4];
int a3 = dc - tmp[3 + (i * 4)]; int a2 = tmp[1 + imul4] - tmp[2 + imul4];
int a3 = dc - tmp[3 + imul4];
output[outputOffset + 0] = (short)((a0 + a1) >> 3); output[outputOffset + 0] = (short)((a0 + a1) >> 3);
output[outputOffset + 16] = (short)((a3 + a2) >> 3); output[outputOffset + 16] = (short)((a3 + a2) >> 3);
output[outputOffset + 32] = (short)((a0 - a1) >> 3); output[outputOffset + 32] = (short)((a0 - a1) >> 3);
@ -1120,15 +1131,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
{ {
hasValue = this.bitReader.ReadBool(); hasValue = this.bitReader.ReadBool();
int quantizeValue = hasValue ? this.bitReader.ReadSignedValue(7) : 0; byte quantizeValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0);
vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue; vp8SegmentHeader.Quantizer[i] = quantizeValue;
} }
for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
{ {
hasValue = this.bitReader.ReadBool(); hasValue = this.bitReader.ReadBool();
int filterStrengthValue = hasValue ? this.bitReader.ReadSignedValue(6) : 0; byte filterStrengthValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0);
vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue; vp8SegmentHeader.FilterStrength[i] = filterStrengthValue;
} }
if (vp8SegmentHeader.UpdateMap) if (vp8SegmentHeader.UpdateMap)
@ -1157,7 +1168,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
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.Level is 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter; dec.Filter = (vp8FilterHeader.Level == 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter;
if (vp8FilterHeader.UseLfDelta) if (vp8FilterHeader.UseLfDelta)
{ {
// Update lf-delta? // Update lf-delta?
@ -1200,8 +1211,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1;
int lastPart = dec.NumPartsMinusOne; int lastPart = dec.NumPartsMinusOne;
int partStart = startIdx + (lastPart * 3); int lastPartMul3 = lastPart * 3;
sizeLeft -= lastPart * 3; int partStart = startIdx + lastPartMul3;
sizeLeft -= lastPartMul3;
for (int p = 0; p < lastPart; ++p) for (int p = 0; p < lastPart; ++p)
{ {
int pSize = sz[0] | (sz[1] << 8) | (sz[2] << 16); int pSize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
@ -1292,10 +1304,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebPConstants.NumProbas; ++p)
{ {
byte prob = WebPLookupTables.CoeffsUpdateProba[t, b, c, p]; byte prob = WebPLookupTables.CoeffsUpdateProba[t, b, c, p];
int v = this.bitReader.GetBit(prob) != 0 byte v = (byte)(this.bitReader.GetBit(prob) != 0
? (int)this.bitReader.ReadValue(8) ? this.bitReader.ReadValue(8)
: WebPLookupTables.DefaultCoeffsProba[t, b, c, p]; : WebPLookupTables.DefaultCoeffsProba[t, b, c, p]);
proba.Bands[t, b].Probabilities[c].Probabilities[p] = (byte)v; proba.Bands[t, b].Probabilities[c].Probabilities[p] = v;
} }
} }
} }
@ -1323,14 +1335,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
io.ScaledHeight = io.ScaledHeight; io.ScaledHeight = io.ScaledHeight;
io.MbW = io.Width; io.MbW = io.Width;
io.MbH = io.Height; io.MbH = io.Height;
io.YStride = (int)(16 * ((pictureHeader.Width + 15) >> 4)); uint strideLength = (pictureHeader.Width + 15) >> 4;
io.UvStride = (int)(8 * ((pictureHeader.Width + 15) >> 4)); io.YStride = (int)(16 * strideLength);
io.UvStride = (int)(8 * strideLength);
int intraPredModeSize = 4 * dec.MbWidth; int intraPredModeSize = 4 * dec.MbWidth;
dec.IntraT = new byte[intraPredModeSize]; dec.IntraT = new byte[intraPredModeSize];
int extraPixels = WebPConstants.FilterExtraRows[(int)dec.Filter]; int extraPixels = WebPConstants.FilterExtraRows[(int)dec.Filter];
if (dec.Filter is LoopFilter.Complex) if (dec.Filter == LoopFilter.Complex)
{ {
// For complex filter, we need to preserve the dependency chain. // For complex filter, we need to preserve the dependency chain.
dec.TopLeftMbX = 0; dec.TopLeftMbX = 0;
@ -1340,8 +1353,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
// 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.
dec.TopLeftMbX = (-extraPixels) >> 4; var extraShift4 = (-extraPixels) >> 4;
dec.TopLeftMbY = (-extraPixels) >> 4; dec.TopLeftMbX = extraShift4;
dec.TopLeftMbY = extraShift4;
if (dec.TopLeftMbX < 0) if (dec.TopLeftMbX < 0)
{ {
dec.TopLeftMbX = 0; dec.TopLeftMbX = 0;
@ -1388,16 +1402,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
private static int CheckMode(int mbx, int mby, int mode) private static int CheckMode(int mbx, int mby, int mode)
{ {
// B_DC_PRED // B_DC_PRED
if (mode is 0) if (mode == 0)
{ {
if (mbx is 0) if (mbx == 0)
{ {
return (mby is 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 is 0) return (mby == 0)
? 4 // B_DC_PRED_NOTOP ? 4 // B_DC_PRED_NOTOP
: 0; // B_DC_PRED : 0; // B_DC_PRED
} }

Loading…
Cancel
Save