diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs index fc0bb92af..756c1d645 100644 --- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.WebP 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. int filter = (alphaChunkHeader >> 2) & 0x03; @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// public void Decode() { - if (this.Compressed is false) + if (this.Compressed == false) { Span dataSpan = this.Data.Memory.Span; var pixelCount = this.Width * this.Height; @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } Span alphaSpan = this.Alpha.Memory.Span; - if (this.AlphaFilterType is WebPAlphaFilterType.None) + if (this.AlphaFilterType == WebPAlphaFilterType.None) { dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan); return; @@ -187,13 +187,13 @@ namespace SixLabors.ImageSharp.Formats.WebP /// The stride to use. public void AlphaApplyFilter(int firstRow, int lastRow, Span dst, int stride) { - if (this.AlphaFilterType is WebPAlphaFilterType.None) + if (this.AlphaFilterType == WebPAlphaFilterType.None) { return; } Span alphaSpan = this.Alpha.Memory.Span; - Span prev = this.PrevRow is 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow); + Span prev = this.PrevRow == 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow); for (int y = firstRow; y < lastRow; ++y) { 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 // 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; if (lastRow > firstRow) { @@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.WebP Span dst = output.Slice(this.Width * firstRow); Span 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"); } @@ -285,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int packedPixels = 0; for (int x = 0; x < width; ++x) { - if ((x & countMask) is 0) + if ((x & countMask) == 0) { packedPixels = src[srcOffset]; srcOffset++; @@ -364,8 +364,6 @@ namespace SixLabors.ImageSharp.Formats.WebP 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) { List htrees = hdr.HTreeGroups[i].HTrees; @@ -411,7 +409,7 @@ namespace SixLabors.ImageSharp.Formats.WebP private static int GradientPredictor(byte a, byte b, byte 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)] diff --git a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs b/src/ImageSharp/Formats/WebP/HuffmanUtils.cs index e9ac2e9fb..ee2e5ea6d 100644 --- a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs +++ b/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 len; // current code length. int symbol; // symbol index in original or sorted table. - var count = 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 counts = new int[WebPConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. + var offsets = new int[WebPConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. // Build histogram of code lengths. for (symbol = 0; symbol < codeLengthsSize; ++symbol) @@ -41,26 +41,26 @@ namespace SixLabors.ImageSharp.Formats.WebP return 0; } - count[codeLengthOfSymbol]++; + counts[codeLengthOfSymbol]++; } // Error, all code lengths are zeros. - if (count[0] == codeLengthsSize) + if (counts[0] == codeLengthsSize) { return 0; } // Generate offsets into sorted symbol table by code length. - offset[1] = 0; + offsets[1] = 0; for (len = 1; len < WebPConstants.MaxAllowedCodeLength; ++len) { - int codesOfLength = count[len]; + int codesOfLength = counts[len]; if (codesOfLength > (1 << len)) { return 0; } - offset[len + 1] = offset[len] + codesOfLength; + offsets[len + 1] = offsets[len] + codesOfLength; } // Sort symbols by length, by symbol order within each length. @@ -69,12 +69,12 @@ namespace SixLabors.ImageSharp.Formats.WebP int symbolCodeLength = codeLengths[symbol]; if (symbolCodeLength > 0) { - sorted[offset[symbolCodeLength]++] = symbol; + sorted[offsets[symbolCodeLength]++] = symbol; } } // Special case code with only one value. - if (offset[WebPConstants.MaxAllowedCodeLength] is 1) + if (offsets[WebPConstants.MaxAllowedCodeLength] == 1) { var huffmanCode = new HuffmanCode() { @@ -98,15 +98,16 @@ namespace SixLabors.ImageSharp.Formats.WebP // Fill in root table. for (len = 1, step = 2; len <= rootBits; ++len, step <<= 1) { + var countsLen = counts[len]; numOpen <<= 1; numNodes += numOpen; - numOpen -= count[len]; + numOpen -= counts[len]; if (numOpen < 0) { return 0; } - for (; count[len] > 0; --count[len]) + for (; countsLen > 0; countsLen--) { var huffmanCode = new HuffmanCode() { @@ -116,6 +117,8 @@ namespace SixLabors.ImageSharp.Formats.WebP ReplicateValue(table.Slice(key), step, tableSize, huffmanCode); key = GetNextKey(key, len); } + + counts[len] = countsLen; } // Fill in 2nd level tables and add pointers to root table. @@ -125,19 +128,19 @@ namespace SixLabors.ImageSharp.Formats.WebP { numOpen <<= 1; numNodes += numOpen; - numOpen -= count[len]; + numOpen -= counts[len]; if (numOpen < 0) { return 0; } - for (; count[len] > 0; --count[len]) + for (; counts[len] > 0; --counts[len]) { if ((key & mask) != low) { tableSpan = tableSpan.Slice(tableSize); tablePos += tableSize; - tableBits = NextTableBitSize(count, len, rootBits); + tableBits = NextTableBitSize(counts, len, rootBits); tableSize = 1 << tableBits; totalSize += tableSize; low = key & mask; @@ -185,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } /// - /// 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. /// private static void ReplicateValue(Span table, int step, int end, HuffmanCode code) diff --git a/src/ImageSharp/Formats/WebP/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/LosslessUtils.cs index c8b4fcfed..f49000c6e 100644 --- a/src/ImageSharp/Formats/WebP/LosslessUtils.cs +++ b/src/ImageSharp/Formats/WebP/LosslessUtils.cs @@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.Formats.WebP /// internal static class LosslessUtils { + private const uint Predictor0 = WebPConstants.ArgbBlack; + /// /// Add green to blue and red channels (i.e. perform the inverse transform of 'subtract green'). /// @@ -61,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // 'pixelsPerByte' increments of x. Fortunately, pixelsPerByte // is a power of 2, so we can just use a mask for that, instead of // decrementing a counter. - if ((x & countMask) is 0) + if ((x & countMask) == 0) { packedPixels = GetArgbIndex(pixelData[pixelDataPos++]); } @@ -72,17 +74,17 @@ namespace SixLabors.ImageSharp.Formats.WebP } decodedPixelData.AsSpan().CopyTo(pixelData); - - return; } - - for (int y = 0; y < height; ++y) + else { - for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) { - uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]); - pixelData[decodedPixels] = colorMap[(int)colorMapIndex]; - decodedPixels++; + for (int x = 0; x < width; ++x) + { + uint colorMapIndex = GetArgbIndex(pixelData[decodedPixels]); + pixelData[decodedPixels] = colorMap[(int)colorMapIndex]; + decodedPixels++; + } } } } @@ -130,7 +132,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } ++y; - if ((y & mask) is 0) + if ((y & mask) == 0) { predRowIdxStart += tilesPerRow; } @@ -154,9 +156,9 @@ namespace SixLabors.ImageSharp.Formats.WebP uint red = argb >> 16; int newRed = (int)(red & 0xff); int newBlue = (int)argb & 0xff; - newRed += ColorTransformDelta((sbyte)m.GreenToRed, (sbyte)green); + newRed += ColorTransformDelta((sbyte)m.GreenToRed, green); newRed &= 0xff; - newBlue += ColorTransformDelta((sbyte)m.GreenToBlue, (sbyte)green); + newBlue += ColorTransformDelta((sbyte)m.GreenToBlue, green); newBlue += ColorTransformDelta((sbyte)m.RedToBlue, (sbyte)newRed); newBlue &= 0xff; @@ -176,7 +178,6 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span output) { int processedPixels = 0; - int yStart = 0; int width = transform.XSize; Span transformData = transform.Data.GetSpan(); @@ -184,9 +185,8 @@ namespace SixLabors.ImageSharp.Formats.WebP PredictorAdd0(pixelData, processedPixels, 1, output); PredictorAdd1(pixelData, 1, width - 1, output); processedPixels += width; - yStart++; - int y = yStart; + int y = 1; int yEnd = transform.YSize; int tileWidth = 1 << transform.Bits; int mask = tileWidth - 1; @@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Formats.WebP processedPixels += width; ++y; - if ((y & mask) is 0) + if ((y & mask) == 0) { // Use the same mask, since tiles are squares. predictorModeIdxBase += tilesPerRow; @@ -295,9 +295,9 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd0(Span input, int startIdx, int numberOfPixels, Span output) { int endIdx = startIdx + numberOfPixels; + uint pred = Predictor0; for (int x = startIdx; x < endIdx; ++x) { - uint pred = Predictor0(); output[x] = AddPixels(input[x], pred); } } @@ -315,10 +315,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd2(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -326,10 +326,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd3(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -337,10 +337,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd4(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -348,10 +348,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd5(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -359,10 +359,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd6(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -370,10 +370,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd7(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -381,10 +381,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd8(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -392,10 +392,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd9(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -403,10 +403,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd10(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -414,10 +414,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd11(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -425,10 +425,10 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd12(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } @@ -436,40 +436,28 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void PredictorAdd13(Span input, int startIdx, int numberOfPixels, int width, Span output) { int endIdx = startIdx + numberOfPixels; - int offset = 0; + int offset = startIdx - width; 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); } } [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor0() - { - return WebPConstants.ArgbBlack; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor1(uint left, Span top) - { - return left; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor2(uint left, Span top, int idx) + private static uint Predictor2(Span top, int idx) { return top[idx]; } [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor3(uint left, Span top, int idx) + private static uint Predictor3(Span top, int idx) { return top[idx + 1]; } [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor4(uint left, Span top, int idx) + private static uint Predictor4(Span top, int idx) { return top[idx - 1]; } @@ -496,14 +484,14 @@ namespace SixLabors.ImageSharp.Formats.WebP } [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor8(uint left, Span top, int idx) + private static uint Predictor8(Span top, int idx) { uint pred = Average2(top[idx - 1], top[idx]); return pred; } [MethodImpl(InliningOptions.ShortMethod)] - private static uint Predictor9(uint left, Span top, int idx) + private static uint Predictor9(Span top, int idx) { uint pred = Average2(top[idx], top[idx + 1]); return pred; @@ -539,7 +527,10 @@ namespace SixLabors.ImageSharp.Formats.WebP 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)((c0 >> 16) & 0xff), (int)((c1 >> 16) & 0xff), @@ -577,12 +568,7 @@ namespace SixLabors.ImageSharp.Formats.WebP [MethodImpl(InliningOptions.ShortMethod)] private static uint Clip255(uint a) { - if (a < 256) - { - return a; - } - - return ~a >> 24; + return a < 256 ? a : ~a >> 24; } private static uint Select(uint a, uint b, uint c) diff --git a/src/ImageSharp/Formats/WebP/LossyUtils.cs b/src/ImageSharp/Formats/WebP/LossyUtils.cs index 837897e78..2d71938a1 100644 --- a/src/ImageSharp/Formats/WebP/LossyUtils.cs +++ b/src/ImageSharp/Formats/WebP/LossyUtils.cs @@ -5,6 +5,7 @@ using System; using System.Buffers.Binary; using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.WebP { internal static class LossyUtils @@ -20,11 +21,13 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void DC16(Span dst, Span yuv, int offset) { + int offsetMinus1 = offset - 1; + int offsetMinusBps = offset - WebPConstants.Bps; int dc = 16; for (int j = 0; j < 16; ++j) { // 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); @@ -50,10 +53,11 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void HE16(Span dst, Span yuv, int offset) { // horizontal + offset--; for (int j = 16; j > 0; --j) { // memset(dst, dst[-1], 16); - byte v = yuv[offset - 1]; + byte v = yuv[offset]; Memset(dst, v, 0, 16); offset += WebPConstants.Bps; dst = dst.Slice(WebPConstants.Bps); @@ -96,10 +100,12 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void DC8uv(Span dst, Span yuv, int offset) { int dc0 = 8; + int offsetMinus1 = offset - 1; + int offsetMinusBps = offset - WebPConstants.Bps; for (int i = 0; i < 8; ++i) { // 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); @@ -117,21 +123,23 @@ namespace SixLabors.ImageSharp.Formats.WebP // vertical Span 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); - src.CopyTo(dst.Slice(j * WebPConstants.Bps)); + src.CopyTo(dst.Slice(j)); } } public static void HE8uv(Span dst, Span yuv, int offset) { // horizontal + offset--; for (int j = 0; j < 8; ++j) { // memset(dst, dst[-1], 8); // dst += BPS; - byte v = yuv[offset - 1]; + byte v = yuv[offset]; Memset(dst, v, 0, 8); dst = dst.Slice(WebPConstants.Bps); offset += WebPConstants.Bps; @@ -142,10 +150,12 @@ namespace SixLabors.ImageSharp.Formats.WebP { // DC with no top samples. 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 += yuv[offset - 1 + (i * WebPConstants.Bps)]; + dc0 += yuv[offsetMinusOne + i]; } Put8x8uv((byte)(dc0 >> 3), dst); @@ -154,11 +164,12 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void DC8uvNoLeft(Span dst, Span yuv, int offset) { // DC with no left samples. + int offsetMinusBps = offset - WebPConstants.Bps; int dc0 = 4; for (int i = 0; i < 8; ++i) { // dc0 += dst[i - BPS]; - dc0 += yuv[offset + i - WebPConstants.Bps]; + dc0 += yuv[offsetMinusBps + i]; } Put8x8uv((byte)(dc0 >> 3), dst); @@ -174,15 +185,18 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void DC4(Span dst, Span yuv, int offset) { int dc = 4; + int offsetMinusBps = offset - WebPConstants.Bps; + int offsetMinusOne = offset - 1; 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; - 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]) }; - 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 dst, Span yuv, int offset) { // horizontal - byte a = yuv[offset - 1 - WebPConstants.Bps]; - byte b = yuv[offset - 1]; - byte c = yuv[offset - 1 + WebPConstants.Bps]; - byte d = yuv[offset - 1 + (2 * WebPConstants.Bps)]; - byte e = yuv[offset - 1 + (3 * WebPConstants.Bps)]; + int offsetMinusOne = offset - 1; + byte a = yuv[offsetMinusOne - WebPConstants.Bps]; + byte b = yuv[offsetMinusOne]; + byte c = yuv[offsetMinusOne + WebPConstants.Bps]; + byte d = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; + byte e = yuv[offsetMinusOne + (3 * WebPConstants.Bps)]; uint val = 0x01010101U * Avg3(a, b, c); BinaryPrimitives.WriteUInt32BigEndian(dst, val); val = 0x01010101U * Avg3(b, c, d); @@ -231,11 +247,12 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void RD4(Span dst, Span yuv, int offset) { // Down-right - byte i = yuv[offset - 1]; - byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; - byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; - byte l = yuv[offset - 1 + (3 * WebPConstants.Bps)]; - byte x = yuv[offset - 1 - WebPConstants.Bps]; + int offsetMinusOne = offset - 1; + byte i = yuv[offsetMinusOne]; + byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)]; + byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; + byte l = yuv[offsetMinusOne + (3 * WebPConstants.Bps)]; + byte x = yuv[offsetMinusOne - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebPConstants.Bps]; @@ -267,10 +284,11 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void VR4(Span dst, Span yuv, int offset) { // Vertical-Right - byte i = yuv[offset - 1]; - byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; - byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; - byte x = yuv[offset - 1 - WebPConstants.Bps]; + int offsetMinusOne = offset - 1; + byte i = yuv[offsetMinusOne]; + byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)]; + byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; + byte x = yuv[offsetMinusOne - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebPConstants.Bps]; @@ -437,33 +455,30 @@ namespace SixLabors.ImageSharp.Formats.WebP Dst(dst, 3, 3, l); } - public static void Transform(Span src, Span dst, bool doTwo) + public static void TransformTwo(Span src, Span 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 src, Span dst) { var tmp = new int[4 * 4]; int tmpOffset = 0; - int srcOffset = 0; - for (int i = 0; i < 4; ++i) + for (int srcOffset = 0; srcOffset < 4; srcOffset++) { // vertical pass - int a = src[srcOffset] + src[srcOffset + 8]; - int b = src[srcOffset] - src[srcOffset + 8]; - int c = Mul2(src[srcOffset + 4]) - Mul1(src[srcOffset + 12]); - int d = Mul1(src[srcOffset + 4]) + Mul2(src[srcOffset + 12]); - tmp[tmpOffset] = a + d; - tmp[tmpOffset + 1] = b + c; - tmp[tmpOffset + 2] = b - c; - tmp[tmpOffset + 3] = a - d; - tmpOffset += 4; - srcOffset++; + int srcOffsetPlus4 = srcOffset + 4; + int srcOffsetPlus8 = srcOffset + 8; + int srcOffsetPlus12 = srcOffset + 12; + int a = src[srcOffset] + src[srcOffsetPlus8]; + int b = src[srcOffset] - src[srcOffsetPlus8]; + int c = Mul2(src[srcOffsetPlus4]) - Mul1(src[srcOffsetPlus12]); + int d = Mul1(src[srcOffsetPlus4]) + Mul2(src[srcOffsetPlus12]); + tmp[tmpOffset++] = a + d; + tmp[tmpOffset++] = b + c; + tmp[tmpOffset++] = b - c; + tmp[tmpOffset++] = a - d; } // 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) { // horizontal pass + int tmpOffsetPlus4 = tmpOffset + 4; + int tmpOffsetPlus8 = tmpOffset + 8; + int tmpOffsetPlus12 = tmpOffset + 12; int dc = tmp[tmpOffset] + 4; - int a = dc + tmp[tmpOffset + 8]; - int b = dc - tmp[tmpOffset + 8]; - int c = Mul2(tmp[tmpOffset + 4]) - Mul1(tmp[tmpOffset + 12]); - int d = Mul1(tmp[tmpOffset + 4]) + Mul2(tmp[tmpOffset + 12]); + int a = dc + tmp[tmpOffsetPlus8]; + int b = dc - tmp[tmpOffsetPlus8]; + int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]); + int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]); Store(dst, 0, 0, a + d); Store(dst, 1, 0, b + c); Store(dst, 2, 0, b - c); @@ -517,8 +535,8 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void TransformUv(Span src, Span dst) { - Transform(src.Slice(0 * 16), dst, true); - Transform(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps), true); + TransformTwo(src.Slice(0 * 16), dst); + TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps)); } public static void TransformDcuv(Span src, Span dst) @@ -569,11 +587,12 @@ namespace SixLabors.ImageSharp.Formats.WebP public static void SimpleVFilter16(Span p, int offset, int stride, int thresh) { 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 p, int offset, int stride, int thresh) { 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)] public static void VFilter8i(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { - FilterLoop24(u, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); - FilterLoop24(v, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); + int offset4mulstride = offset + (4 * stride); + FilterLoop24(u, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh); + FilterLoop24(v, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh); } [MethodImpl(InliningOptions.ShortMethod)] public static void HFilter8i(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { - FilterLoop24(u, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); - FilterLoop24(v, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); + int offsetPlus4 = offset + 4; + FilterLoop24(u, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh); + FilterLoop24(v, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh); } [MethodImpl(InliningOptions.ShortMethod)] @@ -683,9 +705,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } [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)] @@ -695,9 +717,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } [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) @@ -776,7 +798,8 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void DoFilter4(Span p, int offset, int step) { // 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 q0 = p[offset]; int q1 = p[offset + step]; @@ -784,7 +807,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int a1 = WebPLookupTables.Sclip2[(a + 4) >> 3]; int a2 = WebPLookupTables.Sclip2[(a + 3) >> 3]; 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] = WebPLookupTables.Clip1[q0 - a1]; p[offset + step] = WebPLookupTables.Clip1[q1 - a3]; @@ -793,24 +816,27 @@ namespace SixLabors.ImageSharp.Formats.WebP private static void DoFilter6(Span p, int offset, int step) { // 6 pixels in, 6 pixels out. - int p2 = p[offset - (3 * step)]; - int p1 = p[offset - (2 * step)]; - int p0 = p[offset - step]; + int step2 = 2 * step; + int step3 = 3 * step; + int offsetMinusStep = offset - step; + int p2 = p[offset - step3]; + int p1 = p[offset - step2]; + int p0 = p[offsetMinusStep]; int q0 = p[offset]; 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]]; // 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 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 - p[offset - (3 * step)] = WebPLookupTables.Clip1[p2 + a3]; - p[offset - (2 * step)] = WebPLookupTables.Clip1[p1 + a2]; - p[offset - step] = WebPLookupTables.Clip1[p0 + a1]; + p[offset - step3] = WebPLookupTables.Clip1[p2 + a3]; + p[offset - step2] = WebPLookupTables.Clip1[p1 + a2]; + p[offsetMinusStep] = WebPLookupTables.Clip1[p0 + a1]; p[offset] = WebPLookupTables.Clip1[q0 - a1]; 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)] @@ -825,14 +851,16 @@ namespace SixLabors.ImageSharp.Formats.WebP private static bool NeedsFilter2(Span p, int offset, int step, int t, int it) { + int step2 = 2 * step; + int step3 = 3 * step; int p3 = p[offset - (4 * step)]; - int p2 = p[offset - (3 * step)]; - int p1 = p[offset - (2 * step)]; + int p2 = p[offset - step3]; + int p1 = p[offset - step2]; int p0 = p[offset - step]; int q0 = p[offset]; int q1 = p[offset + step]; - int q2 = p[offset + (2 * step)]; - int q3 = p[offset + (3 * step)]; + int q2 = p[offset + step2]; + int q3 = p[offset + step3]; if (((4 * WebPLookupTables.Abs0[p0 - q0]) + WebPLookupTables.Abs0[p1 - q1]) > t) { return false; @@ -862,7 +890,8 @@ namespace SixLabors.ImageSharp.Formats.WebP [MethodImpl(InliningOptions.ShortMethod)] private static void Store(Span 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)] @@ -889,32 +918,34 @@ namespace SixLabors.ImageSharp.Formats.WebP [MethodImpl(InliningOptions.ShortMethod)] 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)] private static byte Clip8(int v) { 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)] private static void Put8x8uv(byte value, Span 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, value, j * WebPConstants.Bps, 8); + Memset(dst, value, j, 8); } } [MethodImpl(InliningOptions.ShortMethod)] private static void Memset(Span 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; } } diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs index 66681b89d..1601c7339 100644 --- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs @@ -58,6 +58,8 @@ namespace SixLabors.ImageSharp.Formats.WebP /// Start index in the data array. Defaults to 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.PartitionLength = partitionLength; this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); @@ -133,8 +135,7 @@ namespace SixLabors.ImageSharp.Formats.WebP ulong value = this.value >> pos; ulong mask = (split - value) >> 31; // -1 or 0 this.bits -= 1; - this.range += (uint)mask; - this.range |= 1; + this.range = (this.range + (uint)mask) | 1; this.value -= ((split + 1) & mask) << pos; return (v ^ (int)mask) - (int)mask; @@ -149,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public uint ReadValue(int nBits) { Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); + Guard.MustBeLessThanOrEqualTo(nBits, 32, nameof(nBits)); uint v = 0; while (nBits-- > 0) @@ -162,6 +164,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public int ReadSignedValue(int nBits) { Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); + Guard.MustBeLessThanOrEqualTo(nBits, 32, nameof(nBits)); int value = (int)this.ReadValue(nBits); return this.ReadValue(1) != 0 ? -value : value; @@ -169,13 +172,14 @@ namespace SixLabors.ImageSharp.Formats.WebP private void InitBitreader(uint size, int pos = 0) { + var posPlusSize = pos + size; this.range = 255 - 1; this.value = 0; this.bits = -8; // to load the very first 8 bits. this.eof = false; this.pos = pos; - this.bufferEnd = (uint)(pos + size); - this.bufferMax = (uint)(size > 8 ? pos + size - 8 + 1 : pos); + this.bufferEnd = (uint)posPlusSize; + this.bufferMax = (uint)(size > 8 ? posPlusSize - 8 + 1 : pos); this.LoadNewBytes(); } diff --git a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs index 49d642d48..aa69bb17e 100644 --- a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs +++ b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs @@ -67,8 +67,9 @@ namespace SixLabors.ImageSharp.Formats.WebP int extraUv = (extraRows / 2) * this.CacheUvStride; this.YuvBuffer = memoryAllocator.Allocate((WebPConstants.Bps * 17) + (WebPConstants.Bps * 9) + extraY); this.CacheY = memoryAllocator.Allocate((16 * this.CacheYStride) + extraY); - this.CacheU = memoryAllocator.Allocate((16 * this.CacheUvStride) + extraUv); - this.CacheV = memoryAllocator.Allocate((16 * this.CacheUvStride) + extraUv); + int cacheUvSize = (16 * this.CacheUvStride) + extraUv; + this.CacheU = memoryAllocator.Allocate(cacheUvSize); + this.CacheV = memoryAllocator.Allocate(cacheUvSize); this.TmpYBuffer = memoryAllocator.Allocate((int)width); this.TmpUBuffer = memoryAllocator.Allocate((int)width); this.TmpVBuffer = memoryAllocator.Allocate((int)width); @@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public LoopFilter Filter { get; set; } /// - /// Gets the filter strengths. + /// Gets the pre-calculated per-segment filter strengths. /// public Vp8FilterInfo[,] FilterStrength { get; } @@ -233,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public IMemoryOwner Pixels { get; } /// - /// Gets or sets filter strength info. + /// Gets or sets filter info. /// public Vp8FilterInfo[] FilterInfo { get; set; } @@ -249,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { get { - if (this.leftMacroBlock is null) + if (this.leftMacroBlock == null) { this.leftMacroBlock = new Vp8MacroBlock(); } @@ -268,7 +269,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public void PrecomputeFilterStrengths() { - if (this.Filter is LoopFilter.None) + if (this.Filter == LoopFilter.None) { return; } @@ -320,9 +321,10 @@ namespace SixLabors.ImageSharp.Formats.WebP 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.UseInnerFiltering = i4x4 is 1; + info.UseInnerFiltering = i4x4 == 1; } } } diff --git a/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs b/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs index 16c9d9c0e..6b53532ba 100644 --- a/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs +++ b/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Initializes a new instance of the class. /// - /// The filter info to create an instance from. + /// The filter info to create a copy from. public Vp8FilterInfo(Vp8FilterInfo other) { this.Limit = other.Limit; diff --git a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs index f90023b89..10b7307b3 100644 --- a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs @@ -132,8 +132,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (!this.Eos && nBits <= Vp8LMaxNumBitRead) { ulong val = this.PrefetchBits() & this.bitMask[nBits]; - int newBits = this.bitPos + nBits; - this.bitPos = newBits; + this.bitPos += nBits; this.ShiftBytes(); return (uint)val; } @@ -193,6 +192,7 @@ namespace SixLabors.ImageSharp.Formats.WebP return this.Eos || ((this.pos == this.len) && (this.bitPos > Lbits)); } + [MethodImpl(InliningOptions.ShortMethod)] private void DoFillBitWindow() { this.ShiftBytes(); diff --git a/src/ImageSharp/Formats/WebP/Vp8Profile.cs b/src/ImageSharp/Formats/WebP/Vp8Profile.cs index b1d757cb5..bc3a3ac06 100644 --- a/src/ImageSharp/Formats/WebP/Vp8Profile.cs +++ b/src/ImageSharp/Formats/WebP/Vp8Profile.cs @@ -4,7 +4,21 @@ namespace SixLabors.ImageSharp.Formats.WebP { /// - /// 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. /// internal class Vp8Profile { diff --git a/src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs b/src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs index ca9f4b69e..5fd288abe 100644 --- a/src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs +++ b/src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs @@ -5,6 +5,8 @@ namespace SixLabors.ImageSharp.Formats.WebP { internal class Vp8QuantMatrix { + private int dither; + public int[] Y1Mat { get; } = new int[2]; public int[] Y2Mat { get; } = new int[2]; @@ -19,6 +21,14 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Gets or sets the dithering amplitude (0 = off, max=255). /// - public int Dither { get; set; } + public int Dither + { + get => this.dither; + set + { + Guard.MustBeBetweenOrEqualTo(value, 0, 255, nameof(this.dither)); + this.dither = value; + } + } } } diff --git a/src/ImageSharp/Formats/WebP/WebPChunkType.cs b/src/ImageSharp/Formats/WebP/WebPChunkType.cs index a03f6bfb1..4743935e1 100644 --- a/src/ImageSharp/Formats/WebP/WebPChunkType.cs +++ b/src/ImageSharp/Formats/WebP/WebPChunkType.cs @@ -6,15 +6,16 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Contains a list of different webp chunk types. /// + /// See WebP Container Specification for more details: https://developers.google.com/speed/webp/docs/riff_container public enum WebPChunkType : uint { /// - /// Header signaling the use of VP8 format. + /// Header signaling the use of the VP8 format. /// Vp8 = 0x56503820U, /// - /// Header for a extended-VP8 chunk. + /// Header signaling the image uses lossless encoding. /// 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. /// Animation = 0x414E4D46, - - /// - /// TODO: not sure what this is for yet. - /// - FRGM = 0x4652474D, } } diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index 78519da82..29cec2b47 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -105,9 +105,9 @@ namespace SixLabors.ImageSharp.Formats.WebP public static readonly int[] AlphabetSize = { - NumLiteralCodes + NumLengthCodes, - NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, - NumDistanceCodes + NumLiteralCodes + NumLengthCodes, + NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, + NumDistanceCodes }; // VP8 constants from here on: diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index f0060edaf..180c7c960 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.WebP this.currentStream = stream; uint fileSize = this.ReadImageHeader(); - using var imageInfo = this.ReadVp8Info(); + using WebPImageInfo imageInfo = this.ReadVp8Info(); if (imageInfo.Features != null && imageInfo.Features.Animation) { WebPThrowHelper.ThrowNotSupportedException("Animations are not supported"); @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.WebP var image = new Image(this.configuration, (int)imageInfo.Width, (int)imageInfo.Height, this.Metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); - if (imageInfo.IsLossLess) + if (imageInfo.IsLossless) { var losslessDecoder = new WebPLosslessDecoder(imageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration); losslessDecoder.Decode(pixels, image.Width, image.Height); @@ -159,11 +159,10 @@ namespace SixLabors.ImageSharp.Formats.WebP return this.ReadVp8LHeader(); case WebPChunkType.Vp8X: 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(); } /// @@ -275,8 +274,8 @@ namespace SixLabors.ImageSharp.Formats.WebP this.currentStream.Read(this.buffer, 0, 3); uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16)); remaining -= 3; - bool isKeyFrame = (frameTag & 0x1) is 0; - if (!isKeyFrame) + bool isNoKeyFrame = (frameTag & 0x1) == 1; + if (isNoKeyFrame) { 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}"); } - bool showFrame = ((frameTag >> 4) & 0x1) is 1; - if (!showFrame) + bool invisibleFrame = ((frameTag >> 4) & 0x1) == 0; + if (invisibleFrame) { WebPThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible"); } @@ -314,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.WebP uint height = tmp & 0x3fff; sbyte yScale = (sbyte)(tmp >> 6); remaining -= 7; - if (width is 0 || height is 0) + if (width == 0 || height == 0) { WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); } @@ -344,8 +343,8 @@ namespace SixLabors.ImageSharp.Formats.WebP Height = height, XScale = xScale, YScale = yScale, - BitsPerPixel = features?.Alpha is true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24, - IsLossLess = false, + BitsPerPixel = features?.Alpha == true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24, + IsLossless = false, Features = features, Vp8Profile = (sbyte)version, 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. uint width = 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. @@ -399,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.WebP Width = width, Height = height, BitsPerPixel = WebPBitsPerPixel.Pixel32, - IsLossLess = true, + IsLossless = true, Features = features, Vp8LBitReader = bitReader }; @@ -455,18 +454,19 @@ namespace SixLabors.ImageSharp.Formats.WebP /// The webp 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; } - while (this.currentStream.Position < this.currentStream.Length) + var streamLength = this.currentStream.Length; + while (this.currentStream.Position < streamLength) { // Read chunk header. WebPChunkType chunkType = this.ReadChunkType(); uint chunkLength = this.ReadChunkSize(); - if (chunkType is WebPChunkType.Exif) + if (chunkType == WebPChunkType.Exif) { var exifData = new byte[chunkLength]; this.currentStream.Read(exifData, 0, (int)chunkLength); @@ -488,7 +488,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// 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); this.webpMetadata.ChunkTypes.Enqueue(chunkType); @@ -505,10 +505,10 @@ namespace SixLabors.ImageSharp.Formats.WebP /// The chunk size in bytes. 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); - return (chunkSize % 2 is 0) ? chunkSize : chunkSize + 1; + return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1; } throw new ImageFormatException("Invalid WebP data."); diff --git a/src/ImageSharp/Formats/WebP/WebPFeatures.cs b/src/ImageSharp/Formats/WebP/WebPFeatures.cs index cf1053057..c4fc0ccba 100644 --- a/src/ImageSharp/Formats/WebP/WebPFeatures.cs +++ b/src/ImageSharp/Formats/WebP/WebPFeatures.cs @@ -12,12 +12,12 @@ namespace SixLabors.ImageSharp.Formats.WebP internal class WebPFeatures : IDisposable { /// - /// 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. /// public bool IccProfile { get; set; } /// - /// 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. /// public bool Alpha { get; set; } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public byte AlphaChunkHeader { get; set; } /// - /// 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. /// public bool ExifProfile { get; set; } @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public bool XmpMetaData { get; set; } /// - /// Gets or sets a value indicating whether this image is a animation. + /// Gets or sets a value indicating whether this image is an animation. /// public bool Animation { get; set; } diff --git a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs index b7aa4b36a..cf4a6fef7 100644 --- a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs +++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Gets or sets a value indicating whether this image uses lossless compression. /// - public bool IsLossLess { get; set; } + public bool IsLossless { get; set; } /// /// Gets or sets additional features present in a VP8X image. @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public Vp8FrameHeader Vp8FrameHeader { get; set; } /// - /// 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. /// public Vp8LBitReader Vp8LBitReader { get; set; } = null; diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 88c1cd0d1..6811c9dd9 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -37,13 +37,18 @@ namespace SixLabors.ImageSharp.Formats.WebP /// 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 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 = { @@ -133,14 +138,14 @@ namespace SixLabors.ImageSharp.Formats.WebP } // Color cache. - bool colorCachePresent = this.bitReader.ReadBit(); + bool isColorCachePresent = this.bitReader.ReadBit(); int colorCacheBits = 0; int colorCacheSize = 0; - if (colorCachePresent) + if (isColorCachePresent) { colorCacheBits = (int)this.bitReader.ReadValue(4); - bool coloCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits; - if (!coloCacheBitsIsValid) + bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits; + if (!colorCacheBitsIsValid) { WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); } @@ -151,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.WebP decoder.Metadata.ColorCacheSize = colorCacheSize; // Finish setting up the color-cache. - if (colorCachePresent) + if (isColorCachePresent) { decoder.Metadata.ColorCache = new ColorCache(); colorCacheSize = 1 << colorCacheBits; @@ -192,9 +197,10 @@ namespace SixLabors.ImageSharp.Formats.WebP ApplyInverseTransforms(decoder, pixelData, this.memoryAllocator); Span pixelDataAsBytes = MemoryMarshal.Cast(pixelData); + int widthMul4 = width * 4; for (int y = 0; y < decoder.Height; y++) { - Span row = pixelDataAsBytes.Slice(y * width * 4, width * 4); + Span row = pixelDataAsBytes.Slice(y * widthMul4, widthMul4); Span pixelSpan = pixels.GetRowSpan(y); PixelOperations.Instance.FromBgra32Bytes( this.configuration, @@ -211,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int height = decoder.Height; int row = lastPixel / width; int col = lastPixel % width; - int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; + const int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; int colorCacheSize = decoder.Metadata.ColorCacheSize; ColorCache colorCache = decoder.Metadata.ColorCache; int colorCacheLimit = lenCodeLimit + colorCacheSize; @@ -224,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.WebP while (decodedPixels < totalPixels) { int code; - if ((col & mask) is 0) + if ((col & mask) == 0) { hTreeGroup = GetHTreeGroupForPos(decoder.Metadata, col, row); } @@ -279,8 +285,7 @@ namespace SixLabors.ImageSharp.Formats.WebP break; } - int pixelIdx = decodedPixels; - pixelData[pixelIdx] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue); + pixelData[decodedPixels] = (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); @@ -305,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.WebP while (col >= width) { col -= width; - ++row; + row++; } 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 pixelData, ref int lastCached) { - ++col; + col++; decodedPixels++; if (col >= width) { col = 0; - ++row; + row++; if (colorCache != null) { @@ -373,13 +378,13 @@ namespace SixLabors.ImageSharp.Formats.WebP if (allowRecursion && this.bitReader.ReadBit()) { // Use meta Huffman codes. - uint huffmanPrecision = this.bitReader.ReadValue(3) + 2; - int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision); - int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision); + int huffmanPrecision = (int)(this.bitReader.ReadValue(3) + 2); + int huffmanXSize = LosslessUtils.SubSampleSize(xSize, huffmanPrecision); + int huffmanYSize = LosslessUtils.SubSampleSize(ySize, huffmanPrecision); int huffmanPixels = huffmanXSize * huffmanYSize; IMemoryOwner huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false); Span huffmanImageSpan = huffmanImage.GetSpan(); - decoder.Metadata.HuffmanSubSampleBits = (int)huffmanPrecision; + decoder.Metadata.HuffmanSubSampleBits = huffmanPrecision; for (int i = 0; i < huffmanPixels; ++i) { // 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); - if (size is 0) + if (size == 0) { WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); } hTreeGroup.HTrees.Add(huffmanTable.ToArray()); + HuffmanCode huffTableZero = huffmanTable[0]; 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); if (j <= HuffIndex.Alpha) @@ -452,9 +458,10 @@ namespace SixLabors.ImageSharp.Formats.WebP int 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) { // (i) Simple Code Length Code. - // 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. + // 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. // Read symbols, codes & code lengths directly. uint numSymbols = this.bitReader.ReadValue(1) + 1; uint firstSymbolLenCode = this.bitReader.ReadValue(1); // 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; // The second code (if present), is always 8 bit long. - if (numSymbols is 2) + if (numSymbols == 2) { symbol = this.bitReader.ReadValue(8); codeLengths[symbol] = 1; @@ -550,7 +557,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int symbol = 0; int prevCodeLen = WebPConstants.DefaultCodeLength; int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes); - if (size is 0) + if (size == 0) { WebPThrowHelper.ThrowImageFormatException("Error building huffman table"); } @@ -567,7 +574,7 @@ namespace SixLabors.ImageSharp.Formats.WebP while (symbol < numSymbols) { - if (maxSymbol-- is 0) + if (maxSymbol-- == 0) { break; } @@ -667,7 +674,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// 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. /// /// The decoder holding the transformation infos. /// The pixel data to apply the transformation. @@ -677,13 +684,14 @@ namespace SixLabors.ImageSharp.Formats.WebP List transforms = decoder.Transforms; for (int i = transforms.Count - 1; i >= 0; i--) { - Vp8LTransformType transformType = transforms[i].TransformType; + Vp8LTransform transform = transforms[i]; + Vp8LTransformType transformType = transform.TransformType; switch (transformType) { case Vp8LTransformType.PredictorTransform: using (IMemoryOwner output = memoryAllocator.Allocate(pixelData.Length, AllocationOptions.Clean)) { - LosslessUtils.PredictorInverseTransform(transforms[i], pixelData, output.GetSpan()); + LosslessUtils.PredictorInverseTransform(transform, pixelData, output.GetSpan()); } break; @@ -691,10 +699,10 @@ namespace SixLabors.ImageSharp.Formats.WebP LosslessUtils.AddGreenToBlueAndRed(pixelData); break; case Vp8LTransformType.CrossColorTransform: - LosslessUtils.ColorSpaceInverseTransform(transforms[i], pixelData); + LosslessUtils.ColorSpaceInverseTransform(transform, pixelData); break; case Vp8LTransformType.ColorIndexingTransform: - LosslessUtils.ColorIndexInverseTransform(transforms[i], pixelData); + LosslessUtils.ColorIndexInverseTransform(transform, pixelData); break; } } @@ -719,13 +727,13 @@ namespace SixLabors.ImageSharp.Formats.WebP int end = width * height; // End of data. int last = end; // Last pixel to decode. int lastRow = height; - int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; + const int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; int mask = hdr.HuffmanMask; HTreeGroup[] htreeGroup = (pos < last) ? GetHTreeGroupForPos(hdr, col, row) : null; while (!this.bitReader.Eos && pos < last) { // Only update when changing tile. - if ((col & mask) is 0) + if ((col & mask) == 0) { htreeGroup = GetHTreeGroupForPos(hdr, col, row); } @@ -743,7 +751,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { col = 0; ++row; - if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) + if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0)) { dec.ExtractPalettedAlphaRows(row); } @@ -773,7 +781,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { col -= width; ++row; - if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) + if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0)) { dec.ExtractPalettedAlphaRows(row); } @@ -802,7 +810,7 @@ namespace SixLabors.ImageSharp.Formats.WebP decoder.Width = width; decoder.Height = height; 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 pixelData, int decodedPixels) diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index a741ad42d..b82a79dae 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Decode image data. this.ParseFrame(decoder, io); - if (info.Features?.Alpha is true) + if (info.Features?.Alpha == true) { using (var alphaDecoder = new AlphaDecoder( width, @@ -112,35 +112,13 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels, IMemoryOwner alpha = null) + private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels) where TPixel : unmanaged, IPixel { - if (alpha != null) - { - TPixel color = default; - Span alphaSpan = alpha.Memory.Span; - for (int y = 0; y < height; y++) - { - Span 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; - } - + int widthMul3 = width * 3; for (int y = 0; y < height; y++) { - Span row = pixelData.Slice(y * width * 3, width * 3); + Span row = pixelData.Slice(y * widthMul3, widthMul3); Span pixelSpan = pixels.GetRowSpan(y); PixelOperations.Instance.FromBgr24Bytes( this.configuration, @@ -150,6 +128,29 @@ namespace SixLabors.ImageSharp.Formats.WebP } } + private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels, IMemoryOwner alpha) + where TPixel : unmanaged, IPixel + { + TPixel color = default; + Span alphaSpan = alpha.Memory.Span; + for (int y = 0; y < height; y++) + { + int yMulWidth = y * width; + Span 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) { for (dec.MbY = 0; dec.MbY < dec.BottomRightMbY; ++dec.MbY) @@ -186,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (dec.SegmentHeader.UpdateMap) { // 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[2]) + 2); } @@ -198,10 +199,10 @@ namespace SixLabors.ImageSharp.Formats.WebP 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) { // Hardcoded 16x16 intra-mode decision tree. @@ -241,8 +242,8 @@ namespace SixLabors.ImageSharp.Formats.WebP } // Hardcoded UVMode decision tree. - block.UvMode = (byte)(this.bitReader.GetBit(142) is 0 ? 0 : - this.bitReader.GetBit(114) is 0 ? 2 : + block.UvMode = (byte)(this.bitReader.GetBit(142) == 0 ? 0 : + this.bitReader.GetBit(114) == 0 ? 2 : this.bitReader.GetBit(183) != 0 ? 1 : 3); } @@ -268,9 +269,9 @@ namespace SixLabors.ImageSharp.Formats.WebP private void ReconstructRow(Vp8Decoder dec) { int mby = dec.MbY; - int yOff = (WebPConstants.Bps * 1) + 8; - int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps; - int vOff = uOff + 16; + const int yOff = (WebPConstants.Bps * 1) + 8; + const int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps; + const int vOff = uOff + 16; Span yuv = dec.YuvBuffer.Memory.Span; Span yDst = yuv.Slice(yOff); @@ -278,15 +279,17 @@ namespace SixLabors.ImageSharp.Formats.WebP Span vDst = yuv.Slice(vOff); // 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 * WebPConstants.Bps) - 1 + vOff] = 129; + yuv[i - 1 + uOff] = 129; + yuv[i - 1 + vOff] = 129; } // Init top-left sample on left column too. @@ -364,10 +367,11 @@ namespace SixLabors.ImageSharp.Formats.WebP if (mbx >= dec.MbWidth - 1) { // On rightmost border. - topRight[0] = topYuv.Y[15]; - topRight[1] = topYuv.Y[15]; - topRight[2] = topYuv.Y[15]; - topRight[3] = topYuv.Y[15]; + var topYuv15 = topYuv.Y[15]; + topRight[0] = topYuv15; + topRight[1] = topYuv15; + topRight[2] = topYuv15; + topRight[3] = topYuv15; } else { @@ -517,8 +521,9 @@ namespace SixLabors.ImageSharp.Formats.WebP for (int j = 0; j < 8; ++j) { - uDst.Slice(j * WebPConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(j * dec.CacheUvStride)); - vDst.Slice(j * WebPConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(j * dec.CacheUvStride)); + int jUvStride = 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 limit = filterInfo.Limit; - if (limit is 0) + if (limit == 0) { return; } - if (dec.Filter is LoopFilter.Simple) + if (dec.Filter == LoopFilter.Simple) { int offset = dec.CacheYOffset + (mbx * 16); if (mbx > 0) @@ -567,7 +572,7 @@ namespace SixLabors.ImageSharp.Formats.WebP 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 yOffset = dec.CacheYOffset + (mbx * 16); @@ -608,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.WebP Span uDst = dec.CacheU.Memory.Span; Span vDst = dec.CacheV.Memory.Span; int mby = dec.MbY; - bool isFirstRow = mby is 0; + bool isFirstRow = mby == 0; bool isLastRow = mby >= dec.BottomRightMbY - 1; bool filterRow = (dec.Filter != LoopFilter.None) && (dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY); @@ -682,7 +687,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int uvw = (mbw + 1) / 2; int y = io.MbY; - if (y is 0) + if (y == 0) { // First line is special cased. We mirror the u/v samples at boundary. this.UpSample(curY, null, curU, curV, curU, curV, dst, null, mbw); @@ -721,7 +726,7 @@ namespace SixLabors.ImageSharp.Formats.WebP else { // 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); } @@ -756,22 +761,23 @@ namespace SixLabors.ImageSharp.Formats.WebP uint diag03 = (avg + (2 * (tluv + uv))) >> 3; uv0 = (diag12 + tluv) >> 1; uint uv1 = (diag03 + tuv) >> 1; - LossyUtils.YuvToBgr(topY[(2 * x) - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice(((2 * x) - 1) * xStep)); - LossyUtils.YuvToBgr(topY[(2 * x) - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst.Slice(((2 * x) - 0) * xStep)); + int xMul2 = x * 2; + 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) { uv0 = (diag03 + luv) >> 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[(2 * x) + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice(((2 * x) + 0) * xStep)); + LossyUtils.YuvToBgr(bottomY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((xMul2 - 1) * xStep)); + LossyUtils.YuvToBgr(bottomY[xMul2 + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice((xMul2 + 0) * xStep)); } tluv = tuv; luv = uv; } - if ((len & 1) is 0) + if ((len & 1) == 0) { uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2; 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) { case 3: - LossyUtils.Transform(src, dst, false); + LossyUtils.TransformOne(src, dst); break; case 2: LossyUtils.TransformAc3(src, dst); @@ -823,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.WebP Vp8MacroBlock left = dec.LeftMacroBlock; Vp8MacroBlock macroBlock = dec.CurrentMacroBlock; Vp8MacroBlockData blockData = dec.CurrentBlockData; - bool skip = dec.UseSkipProbability ? blockData.Skip : false; + bool skip = dec.UseSkipProbability && blockData.Skip; if (!skip) { @@ -867,7 +873,12 @@ namespace SixLabors.ImageSharp.Formats.WebP dst[i] = 0; } - if (!block.IsI4x4) + if (block.IsI4x4) + { + first = 0; + acProba = GetBandsRow(bands, 3); + } + else { // Parse DC var dc = new short[16]; @@ -892,11 +903,6 @@ namespace SixLabors.ImageSharp.Formats.WebP first = 1; acProba = GetBandsRow(bands, 0); } - else - { - first = 0; - acProba = GetBandsRow(bands, 3); - } byte tnz = (byte)(mb.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) { uint nzCoeffs = 0; - tnz = (byte)(mb.NoneZeroAcDcCoeffs >> (4 + ch)); - lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> (4 + ch)); + var chPlus4 = 4 + ch; + tnz = (byte)(mb.NoneZeroAcDcCoeffs >> chPlus4); + lnz = (byte)(leftMb.NoneZeroAcDcCoeffs >> chPlus4); for (int y = 0; y < 2; ++y) { int l = lnz & 1; @@ -957,26 +964,26 @@ namespace SixLabors.ImageSharp.Formats.WebP block.NonZeroY = nonZeroY; 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 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]; 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; } // Sequence of zero coeffs. - while (br.GetBit(p.Probabilities[1]) is 0) + while (br.GetBit(p.Probabilities[1]) == 0) { p = prob[++n].Probabilities[0]; - if (n is 16) + if (n == 16) { return 16; } @@ -984,7 +991,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Non zero coeffs. int v; - if (br.GetBit(p.Probabilities[2]) is 0) + if (br.GetBit(p.Probabilities[2]) == 0) { v = 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 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; } @@ -1019,9 +1026,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } 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); } @@ -1077,24 +1084,28 @@ namespace SixLabors.ImageSharp.Formats.WebP var tmp = new int[16]; for (int i = 0; i < 4; ++i) { - int a0 = input[0 + i] + input[12 + i]; - int a1 = input[4 + i] + input[8 + i]; - int a2 = input[4 + i] - input[8 + i]; - int a3 = input[0 + i] - input[12 + i]; - tmp[0 + i] = a0 + a1; - tmp[8 + i] = a0 - a1; - tmp[4 + i] = a3 + a2; - tmp[12 + i] = a3 - a2; + int iPlus4 = 4 + i; + int iPlus8 = 8 + i; + int iPlus12 = 12 + i; + int a0 = input[i] + input[iPlus12]; + int a1 = input[iPlus4] + input[iPlus8]; + int a2 = input[iPlus4] - input[iPlus8]; + int a3 = input[i] - input[iPlus12]; + tmp[i] = a0 + a1; + tmp[iPlus8] = a0 - a1; + tmp[iPlus4] = a3 + a2; + tmp[iPlus12] = a3 - a2; } int outputOffset = 0; for (int i = 0; i < 4; ++i) { - int dc = tmp[0 + (i * 4)] + 3; - int a0 = dc + tmp[3 + (i * 4)]; - int a1 = tmp[1 + (i * 4)] + tmp[2 + (i * 4)]; - int a2 = tmp[1 + (i * 4)] - tmp[2 + (i * 4)]; - int a3 = dc - tmp[3 + (i * 4)]; + int imul4 = i * 4; + int dc = tmp[0 + imul4] + 3; + int a0 = dc + tmp[3 + imul4]; + int a1 = tmp[1 + imul4] + tmp[2 + imul4]; + int a2 = tmp[1 + imul4] - tmp[2 + imul4]; + int a3 = dc - tmp[3 + imul4]; output[outputOffset + 0] = (short)((a0 + a1) >> 3); output[outputOffset + 16] = (short)((a3 + a2) >> 3); output[outputOffset + 32] = (short)((a0 - a1) >> 3); @@ -1120,15 +1131,15 @@ namespace SixLabors.ImageSharp.Formats.WebP for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) { hasValue = this.bitReader.ReadBool(); - int quantizeValue = hasValue ? this.bitReader.ReadSignedValue(7) : 0; - vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue; + byte quantizeValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0); + vp8SegmentHeader.Quantizer[i] = quantizeValue; } for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) { hasValue = this.bitReader.ReadBool(); - int filterStrengthValue = hasValue ? this.bitReader.ReadSignedValue(6) : 0; - vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue; + byte filterStrengthValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0); + vp8SegmentHeader.FilterStrength[i] = filterStrengthValue; } if (vp8SegmentHeader.UpdateMap) @@ -1157,7 +1168,7 @@ namespace SixLabors.ImageSharp.Formats.WebP vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3); 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) { // Update lf-delta? @@ -1200,8 +1211,9 @@ namespace SixLabors.ImageSharp.Formats.WebP dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; int lastPart = dec.NumPartsMinusOne; - int partStart = startIdx + (lastPart * 3); - sizeLeft -= lastPart * 3; + int lastPartMul3 = lastPart * 3; + int partStart = startIdx + lastPartMul3; + sizeLeft -= lastPartMul3; for (int p = 0; p < lastPart; ++p) { 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) { byte prob = WebPLookupTables.CoeffsUpdateProba[t, b, c, p]; - int v = this.bitReader.GetBit(prob) != 0 - ? (int)this.bitReader.ReadValue(8) - : WebPLookupTables.DefaultCoeffsProba[t, b, c, p]; - proba.Bands[t, b].Probabilities[c].Probabilities[p] = (byte)v; + byte v = (byte)(this.bitReader.GetBit(prob) != 0 + ? this.bitReader.ReadValue(8) + : WebPLookupTables.DefaultCoeffsProba[t, b, c, p]); + proba.Bands[t, b].Probabilities[c].Probabilities[p] = v; } } } @@ -1323,14 +1335,15 @@ namespace SixLabors.ImageSharp.Formats.WebP io.ScaledHeight = io.ScaledHeight; io.MbW = io.Width; io.MbH = io.Height; - io.YStride = (int)(16 * ((pictureHeader.Width + 15) >> 4)); - io.UvStride = (int)(8 * ((pictureHeader.Width + 15) >> 4)); + uint strideLength = (pictureHeader.Width + 15) >> 4; + io.YStride = (int)(16 * strideLength); + io.UvStride = (int)(8 * strideLength); int intraPredModeSize = 4 * dec.MbWidth; dec.IntraT = new byte[intraPredModeSize]; 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. 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, // since vertical or horizontal filtering of the previous macroblock can modify some abutting pixels. - dec.TopLeftMbX = (-extraPixels) >> 4; - dec.TopLeftMbY = (-extraPixels) >> 4; + var extraShift4 = (-extraPixels) >> 4; + dec.TopLeftMbX = extraShift4; + dec.TopLeftMbY = extraShift4; if (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) { // 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 : 5; // B_DC_PRED_NOLEFT } - return (mby is 0) + return (mby == 0) ? 4 // B_DC_PRED_NOTOP : 0; // B_DC_PRED }