diff --git a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs index a0ade54fa6..942579487a 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs @@ -340,6 +340,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless colorCache[i].Init(i); } + // TODO: Don't use the enumerator here. // Find the cache_bits giving the lowest entropy. using List.Enumerator c = refs.Refs.GetEnumerator(); while (c.MoveNext()) @@ -363,7 +364,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless ++histos[0].Alpha[a]; // Deal with cacheBits > 0. - for (int i = cacheBitsMax; i >= 1; i--, key >>= 1) + for (int i = cacheBitsMax; i >= 1; --i, key >>= 1) { if (colorCache[i].Lookup(key) == pix) { @@ -388,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless int len = v.Len; uint bgraPrev = bgra[pos] ^ 0xffffffffu; + // TODO: Original has this loop? + // VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value); + // for (i = 0; i <= cache_bits_max; ++i) + // { + // ++histos[i]->literal_[NUM_LITERAL_CODES + code]; + // } // Update the color caches. do { @@ -409,7 +416,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - for (int i = 0; i <= cacheBitsMax; i++) + for (int i = 0; i <= cacheBitsMax; ++i) { double entropy = histos[i].EstimateBits(); if (i == 0 || entropy < entropyMin) @@ -461,7 +468,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Add first pixel as literal. AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManager.Costs, distArray); - for (int i = 1; i < pixCount; i++) + for (int i = 1; i < pixCount; ++i) { float prevCost = costManager.Costs[i - 1]; int offset = hashChain.FindOffset(i); @@ -660,7 +667,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // [i,i+len) + [i+len, length of best match at i+len) // while we check if we can use: // [i,j) (where j<=i+len) + [j, length of best match at j) - for (j = iLastCheck + 1; j <= jMax; j++) + for (j = iLastCheck + 1; j <= jMax; ++j) { int lenJ = hashChain.FindLength(j); int reach = j + (lenJ >= MinLength ? lenJ : 1); // 1 for single literal. @@ -720,7 +727,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless int i = pixelCount - 2; int countsPos = i; counts[countsPos + 1] = 1; - for (; i >= 0; i--, countsPos--) + for (; i >= 0; --i, --countsPos) { if (bgra[i] == bgra[i + 1]) { @@ -739,9 +746,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Figure out the window offsets around a pixel. They are stored in a // spiraling order around the pixel as defined by DistanceToPlaneCode. - for (int y = 0; y <= 6; y++) + for (int y = 0; y <= 6; ++y) { - for (int x = -6; x <= 6; x++) + for (int x = -6; x <= 6; ++x) { int offset = (y * xSize) + x; @@ -762,7 +769,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } // For narrow images, not all plane codes are reached, so remove those. - for (i = 0; i < WindowOffsetsSizeMax; i++) + for (i = 0; i < WindowOffsetsSizeMax; ++i) { if (windowOffsets[i] == 0) { @@ -774,7 +781,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless // Given a pixel P, find the offsets that reach pixels unreachable from P-1 // with any of the offsets in windowOffsets[]. - for (i = 0; i < windowOffsetsSize; i++) + for (i = 0; i < windowOffsetsSize; ++i) { bool isReachable = false; for (int j = 0; j < windowOffsetsSize && !isReachable; ++j) @@ -785,7 +792,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (!isReachable) { windowOffsetsNew[windowOffsetsNewSize] = windowOffsets[i]; - windowOffsetsNewSize++; + ++windowOffsetsNewSize; } } @@ -917,7 +924,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)prevRowLen)); if (useColorCache) { - for (int k = 0; k < prevRowLen; k++) + for (int k = 0; k < prevRowLen; ++k) { colorCache.Insert(bgra[i + k]); } @@ -943,6 +950,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// private static void BackwardRefsWithLocalCache(Span bgra, int cacheBits, Vp8LBackwardRefs refs) { + // TODO: Don't use enumerator. int pixelIndex = 0; using List.Enumerator c = refs.Refs.GetEnumerator(); var colorCache = new ColorCache(); @@ -969,7 +977,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless else { // refs was created without local cache, so it can not have cache indexes. - for (int k = 0; k < v.Len; k++) + for (int k = 0; k < v.Len; ++k) { colorCache.Insert(bgra[pixelIndex++]); } diff --git a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs index eac88609c3..16b5bce069 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// /// Utility functions for the lossless decoder. /// - internal static class LosslessUtils + internal static unsafe class LosslessUtils { private const uint Predictor0 = WebPConstants.ArgbBlack; @@ -244,104 +244,116 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// /// The transform data. /// The pixel data to apply the inverse transform. - /// The resulting pixel data with the reversed transformation data. - public static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span output) - { - int processedPixels = 0; - int width = transform.XSize; - Span transformData = transform.Data.GetSpan(); - - // First Row follows the L (mode=1) mode. - PredictorAdd0(pixelData, processedPixels, 1, output); - PredictorAdd1(pixelData, 1, width - 1, output); - processedPixels += width; - - int y = 1; - int yEnd = transform.YSize; - int tileWidth = 1 << transform.Bits; - int mask = tileWidth - 1; - int tilesPerRow = SubSampleSize(width, transform.Bits); - int predictorModeIdxBase = (y >> transform.Bits) * tilesPerRow; - while (y < yEnd) + /// The resulting pixel data with the reversed transformation data. + public static void PredictorInverseTransform( + Vp8LTransform transform, + Span pixelData, + Span outputSpan) + { + fixed (uint* inputFixed = pixelData) + fixed (uint* outputFixed = outputSpan) { - int predictorModeIdx = predictorModeIdxBase; - int x = 1; + uint* input = inputFixed; + uint* output = outputFixed; + + int width = transform.XSize; + Span transformData = transform.Data.GetSpan(); + + // First Row follows the L (mode=1) mode. + PredictorAdd0(input, null, 1, output); + PredictorAdd1(input + 1, null, width - 1, output + 1); + input += width; + output += width; + + int y = 1; + int yEnd = transform.YSize; + int tileWidth = 1 << transform.Bits; + int mask = tileWidth - 1; + int tilesPerRow = SubSampleSize(width, transform.Bits); + int predictorModeIdxBase = (y >> transform.Bits) * tilesPerRow; + while (y < yEnd) + { + int predictorModeIdx = predictorModeIdxBase; + int x = 1; - // First pixel follows the T (mode=2) mode. - PredictorAdd2(pixelData, processedPixels, 1, width, output); + // First pixel follows the T (mode=2) mode. + PredictorAdd2(input, output - width, 1, output); - while (x < width) - { - uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf; - int xEnd = (x & ~mask) + tileWidth; - if (xEnd > width) + // .. the rest: + while (x < width) { - xEnd = width; - } + uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf; + int xEnd = (x & ~mask) + tileWidth; + if (xEnd > width) + { + xEnd = width; + } - // There are 14 different prediction modes. - // In each prediction mode, the current pixel value is predicted from one or more neighboring pixels whose values are already known. - int startIdx = processedPixels + x; - int numberOfPixels = xEnd - x; - switch (predictorMode) - { - case 0: - PredictorAdd0(pixelData, startIdx, numberOfPixels, output); - break; - case 1: - PredictorAdd1(pixelData, startIdx, numberOfPixels, output); - break; - case 2: - PredictorAdd2(pixelData, startIdx, numberOfPixels, width, output); - break; - case 3: - PredictorAdd3(pixelData, startIdx, numberOfPixels, width, output); - break; - case 4: - PredictorAdd4(pixelData, startIdx, numberOfPixels, width, output); - break; - case 5: - PredictorAdd5(pixelData, startIdx, numberOfPixels, width, output); - break; - case 6: - PredictorAdd6(pixelData, startIdx, numberOfPixels, width, output); - break; - case 7: - PredictorAdd7(pixelData, startIdx, numberOfPixels, width, output); - break; - case 8: - PredictorAdd8(pixelData, startIdx, numberOfPixels, width, output); - break; - case 9: - PredictorAdd9(pixelData, startIdx, numberOfPixels, width, output); - break; - case 10: - PredictorAdd10(pixelData, startIdx, numberOfPixels, width, output); - break; - case 11: - PredictorAdd11(pixelData, startIdx, numberOfPixels, width, output); - break; - case 12: - PredictorAdd12(pixelData, startIdx, numberOfPixels, width, output); - break; - case 13: - PredictorAdd13(pixelData, startIdx, numberOfPixels, width, output); - break; + // There are 14 different prediction modes. + // In each prediction mode, the current pixel value is predicted from one + // or more neighboring pixels whose values are already known. + switch (predictorMode) + { + case 0: + PredictorAdd0(input + x, output + x - width, xEnd - x, output + x); + break; + case 1: + PredictorAdd1(input + x, output + x - width, xEnd - x, output + x); + break; + case 2: + PredictorAdd2(input + x, output + x - width, xEnd - x, output + x); + break; + case 3: + PredictorAdd3(input + x, output + x - width, xEnd - x, output + x); + break; + case 4: + PredictorAdd4(input + x, output + x - width, xEnd - x, output + x); + break; + case 5: + PredictorAdd5(input + x, output + x - width, xEnd - x, output + x); + break; + case 6: + PredictorAdd6(input + x, output + x - width, xEnd - x, output + x); + break; + case 7: + PredictorAdd7(input + x, output + x - width, xEnd - x, output + x); + break; + case 8: + PredictorAdd8(input + x, output + x - width, xEnd - x, output + x); + break; + case 9: + PredictorAdd9(input + x, output + x - width, xEnd - x, output + x); + break; + case 10: + PredictorAdd10(input + x, output + x - width, xEnd - x, output + x); + break; + case 11: + PredictorAdd11(input + x, output + x - width, xEnd - x, output + x); + break; + case 12: + PredictorAdd12(input + x, output + x - width, xEnd - x, output + x); + break; + case 13: + PredictorAdd13(input + x, output + x - width, xEnd - x, output + x); + break; + } + + x = xEnd; } - x = xEnd; - } + input += width; + output += width; + ++y; - processedPixels += width; - ++y; - if ((y & mask) == 0) - { - // Use the same mask, since tiles are squares. - predictorModeIdxBase += tilesPerRow; + if ((y & mask) == 0) + { + // Use the same mask, since tiles are squares. + predictorModeIdxBase += tilesPerRow; + } } } - output.CopyTo(pixelData); + outputSpan.CopyTo(pixelData); } public static void ExpandColorMap(int numColors, Span transformData, Span newColorMap) @@ -582,374 +594,352 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless return code; } - private static void PredictorAdd0(Span input, int startIdx, int numberOfPixels, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd0(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - uint pred = Predictor0; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - output[x] = AddPixels(input[x], pred); + output[x] = AddPixels(input[x], WebPConstants.ArgbBlack); } } - private static void PredictorAdd1(Span input, int startIdx, int numberOfPixels, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd1(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - uint left = output[startIdx - 1]; - for (int x = startIdx; x < endIdx; ++x) + uint left = output[-1]; + for (int x = 0; x < numberOfPixels; ++x) { output[x] = left = AddPixels(input[x], left); } } - private static void PredictorAdd2(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd2(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor2(output, offset++); + uint pred = Predictor2(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd3(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd3(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor3(output, offset++); + uint pred = Predictor3(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd4(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd4(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor4(output, offset++); + uint pred = Predictor4(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd5(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd5(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor5(output[x - 1], output, offset++); + uint pred = Predictor5(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd6(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd6(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor6(output[x - 1], output, offset++); + uint pred = Predictor6(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd7(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd7(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor7(output[x - 1], output, offset++); + uint pred = Predictor7(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd8(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd8(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor8(output, offset++); + uint pred = Predictor8(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd9(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd9(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor9(output, offset++); + uint pred = Predictor9(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd10(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd10(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor10(output[x - 1], output, offset++); + uint pred = Predictor10(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd11(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd11(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor11(output[x - 1], output, offset++); + uint pred = Predictor11(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd12(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd12(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor12(output[x - 1], output, offset++); + uint pred = Predictor12(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } - private static void PredictorAdd13(Span input, int startIdx, int numberOfPixels, int width, Span output) + [MethodImpl(InliningOptions.ShortMethod)] + private static void PredictorAdd13(uint* input, uint* upper, int numberOfPixels, uint* output) { - int endIdx = startIdx + numberOfPixels; - int offset = startIdx - width; - for (int x = startIdx; x < endIdx; ++x) + for (int x = 0; x < numberOfPixels; ++x) { - uint pred = Predictor13(output[x - 1], output, offset++); + uint pred = Predictor13(output[x - 1], upper + x); output[x] = AddPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor2(Span top, int idx) + public static uint Predictor2(uint left, uint* top) { - return top[idx]; + return top[0]; } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor3(Span top, int idx) + public static uint Predictor3(uint left, uint* top) { - return top[idx + 1]; + return top[1]; } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor4(Span top, int idx) + public static uint Predictor4(uint left, uint* top) { - return top[idx - 1]; + return top[-1]; } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor5(uint left, Span top, int idx) + public static uint Predictor5(uint left, uint* top) { - uint pred = Average3(left, top[idx], top[idx + 1]); - return pred; + return Average3(left, top[0], top[1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor6(uint left, Span top, int idx) + public static uint Predictor6(uint left, uint* top) { - uint pred = Average2(left, top[idx - 1]); - return pred; + return Average2(left, top[-1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor7(uint left, Span top, int idx) + public static uint Predictor7(uint left, uint* top) { - uint pred = Average2(left, top[idx]); - return pred; + return Average2(left, top[0]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor8(Span top, int idx) + public static uint Predictor8(uint left, uint* top) { - uint pred = Average2(top[idx - 1], top[idx]); - return pred; + return Average2(top[-1], top[0]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor9(Span top, int idx) + public static uint Predictor9(uint left, uint* top) { - uint pred = Average2(top[idx], top[idx + 1]); - return pred; + return Average2(top[0], top[1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor10(uint left, Span top, int idx) + public static uint Predictor10(uint left, uint* top) { - uint pred = Average4(left, top[idx - 1], top[idx], top[idx + 1]); - return pred; + return Average4(left, top[-1], top[0], top[1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor11(uint left, Span top, int idx) + public static uint Predictor11(uint left, uint* top) { - uint pred = Select(top[idx], left, top[idx - 1]); - return pred; + return Select(top[0], left, top[-1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor12(uint left, Span top, int idx) + public static uint Predictor12(uint left, uint* top) { - uint pred = ClampedAddSubtractFull(left, top[idx], top[idx - 1]); - return pred; + return ClampedAddSubtractFull(left, top[0], top[-1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static uint Predictor13(uint left, Span top, int idx) + public static uint Predictor13(uint left, uint* top) { - uint pred = ClampedAddSubtractHalf(left, top[idx], top[idx - 1]); - return pred; + return ClampedAddSubtractHalf(left, top[0], top[-1]); } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub0(Span input, int numPixels, Span output) + public static void PredictorSub0(uint* input, int numPixels, uint* output) { - for (int i = 0; i < numPixels; i++) + for (int i = 0; i < numPixels; ++i) { output[i] = SubPixels(input[i], WebPConstants.ArgbBlack); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub1(Span input, int idx, int numPixels, Span output) + public static void PredictorSub1(uint* input, int numPixels, uint* output) { - for (int i = 0; i < numPixels; i++) + for (int i = 0; i < numPixels; ++i) { - output[i] = SubPixels(input[idx + i], input[idx + i - 1]); + output[i] = SubPixels(input[i], input[i - 1]); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub2(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub2(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor2(upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor2(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub3(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub3(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor3(upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor3(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub4(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub4(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor4(upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor4(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub5(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub5(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor5(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor5(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub6(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub6(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor6(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor6(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub7(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub7(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor7(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor7(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub8(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub8(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor8(upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor8(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub9(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub9(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor9(upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor9(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub10(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub10(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor10(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor10(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub11(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub11(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor11(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor11(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub12(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub12(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor12(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor12(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } [MethodImpl(InliningOptions.ShortMethod)] - public static void PredictorSub13(Span input, int idx, Span upper, int numPixels, Span output) + public static void PredictorSub13(uint* input, uint* upper, int numPixels, uint* output) { - for (int x = 0; x < numPixels; x++) + for (int x = 0; x < numPixels; ++x) { - uint pred = Predictor13(input[idx - 1], upper, x); - output[x] = SubPixels(input[idx + x], pred); + uint pred = Predictor13(input[x - 1], upper + x); + output[x] = SubPixels(input[x], pred); } } diff --git a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs index bc2c7b5946..156f82032b 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// /// Image transform methods for the lossless webp encoder. /// - internal static class PredictorEncoder + internal static unsafe class PredictorEncoder { private const int GreenRedToBlueNumAxis = 8; @@ -27,27 +27,61 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// with respect to predictions. If nearLosslessQuality < 100, applies /// near lossless processing, shaving off more bits of residuals for lower qualities. /// - public static void ResidualImage(int width, int height, int bits, Span argb, Span argbScratch, Span image, int nearLosslessQuality, bool exact, bool usedSubtractGreen) + public static void ResidualImage( + int width, + int height, + int bits, + Span argb, + Span argbScratch, + Span image, + int nearLosslessQuality, + bool exact, + bool usedSubtractGreen) { int tilesPerRow = LosslessUtils.SubSampleSize(width, bits); int tilesPerCol = LosslessUtils.SubSampleSize(height, bits); int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality); + + // TODO: Can we optimize this? var histo = new int[4][]; for (int i = 0; i < 4; i++) { histo[i] = new int[256]; } - for (int tileY = 0; tileY < tilesPerCol; tileY++) + // TODO: Low Effort + for (int tileY = 0; tileY < tilesPerCol; ++tileY) { - for (int tileX = 0; tileX < tilesPerRow; tileX++) + for (int tileX = 0; tileX < tilesPerRow; ++tileX) { - int pred = GetBestPredictorForTile(width, height, tileX, tileY, bits, histo, argbScratch, argb, maxQuantization, exact, usedSubtractGreen, image); + int pred = GetBestPredictorForTile( + width, + height, + tileX, + tileY, + bits, + histo, + argbScratch, + argb, + maxQuantization, + exact, + usedSubtractGreen, + image); + image[(tileY * tilesPerRow) + tileX] = (uint)(WebPConstants.ArgbBlack | (pred << 8)); } } - CopyImageWithPrediction(width, height, bits, image, argbScratch, argb, maxQuantization, exact, usedSubtractGreen); + CopyImageWithPrediction( + width, + height, + bits, + image, + argbScratch, + argb, + maxQuantization, + exact, + usedSubtractGreen); } public static void ColorSpaceTransform(int width, int height, int bits, int quality, Span argb, Span image) @@ -55,8 +89,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless int maxTileSize = 1 << bits; int tileXSize = LosslessUtils.SubSampleSize(width, bits); int tileYSize = LosslessUtils.SubSampleSize(height, bits); - int[] accumulatedRedHisto = new int[256]; - int[] accumulatedBlueHisto = new int[256]; + var accumulatedRedHisto = new int[256]; + var accumulatedBlueHisto = new int[256]; var prevX = default(Vp8LMultipliers); var prevY = default(Vp8LMultipliers); for (int tileY = 0; tileY < tileYSize; tileY++) @@ -165,9 +199,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless Span maxDiffs = MemoryMarshal.Cast(currentRow.Slice(width + 1)); float bestDiff = MaxDiffCost; int bestMode = 0; - uint[] residuals = new uint[1 << WebPConstants.MaxTransformBits]; - int[][] histoArgb = new int[4][]; - int[][] bestHisto = new int[4][]; + var residuals = new uint[1 << WebPConstants.MaxTransformBits]; + var histoArgb = new int[4][]; + var bestHisto = new int[4][]; for (int i = 0; i < 4; i++) { histoArgb[i] = new int[256]; @@ -207,13 +241,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless Span src = argb.Slice((y * width) + contextStartX, maxX + haveLeft + ((y + 1) < height ? 1 : 0)); Span dst = currentRow.Slice(contextStartX); src.CopyTo(dst); + + // TODO: Source wraps this in conditional + // WEBP_NEAR_LOSSLESS == 1 if (maxQuantization > 1 && y >= 1 && y + 1 < height) { MaxDiffsForRow(contextWidth, width, argb.Slice((y * width) + contextStartX), maxDiffs.Slice(contextStartX), usedSubtractGreen); } GetResidual(width, height, upperRow, currentRow, maxDiffs, mode, startX, startX + maxX, y, maxQuantization, exact, usedSubtractGreen, residuals); - for (int relativeX = 0; relativeX < maxX; relativeX++) + for (int relativeX = 0; relativeX < maxX; ++relativeX) { UpdateHisto(histoArgb, residuals[relativeX]); } @@ -234,6 +271,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless if (curDiff < bestDiff) { + // TODO: Consider swapping references for (int i = 0; i < 4; i++) { histoArgb[i].AsSpan().CopyTo(bestHisto[i]); @@ -261,113 +299,130 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless /// the deviation further to pixels which depend on the current pixel for their /// predictions. /// - private static void GetResidual(int width, int height, Span upperRow, Span currentRow, Span maxDiffs, int mode, int xStart, int xEnd, int y, int maxQuantization, bool exact, bool usedSubtractGreen, Span output) + private static void GetResidual( + int width, + int height, + Span upperRowSpan, + Span currentRowSpan, + Span maxDiffs, + int mode, + int xStart, + int xEnd, + int y, + int maxQuantization, + bool exact, + bool usedSubtractGreen, + Span output) { if (exact) { - PredictBatch(mode, xStart, y, xEnd - xStart, currentRow, upperRow, output); + PredictBatch(mode, xStart, y, xEnd - xStart, currentRowSpan, upperRowSpan, output); } else { - for (int x = xStart; x < xEnd; x++) + fixed (uint* currentRow = currentRowSpan) + fixed (uint* upperRow = upperRowSpan) { - uint predict = 0; - uint residual; - if (y == 0) - { - predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left. - } - else if (x == 0) - { - predict = upperRow[x]; // Top. - } - else + for (int x = xStart; x < xEnd; ++x) { - switch (mode) + uint predict = 0; + uint residual; + if (y == 0) { - case 0: - predict = WebPConstants.ArgbBlack; - break; - case 1: - predict = currentRow[x - 1]; - break; - case 2: - predict = LosslessUtils.Predictor2(upperRow, x); - break; - case 3: - predict = LosslessUtils.Predictor3(upperRow, x); - break; - case 4: - predict = LosslessUtils.Predictor4(upperRow, x); - break; - case 5: - predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow, x); - break; - case 6: - predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow, x); - break; - case 7: - predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow, x); - break; - case 8: - predict = LosslessUtils.Predictor8(upperRow, x); - break; - case 9: - predict = LosslessUtils.Predictor9(upperRow, x); - break; - case 10: - predict = LosslessUtils.Predictor10(currentRow[x - 1], upperRow, x); - break; - case 11: - predict = LosslessUtils.Predictor11(currentRow[x - 1], upperRow, x); - break; - case 12: - predict = LosslessUtils.Predictor12(currentRow[x - 1], upperRow, x); - break; - case 13: - predict = LosslessUtils.Predictor13(currentRow[x - 1], upperRow, x); - break; + predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left. + } + else if (x == 0) + { + predict = upperRow[x]; // Top. + } + else + { + switch (mode) + { + case 0: + predict = WebPConstants.ArgbBlack; + break; + case 1: + predict = currentRow[x - 1]; + break; + case 2: + predict = LosslessUtils.Predictor2(currentRow[x - 1], upperRow + x); + break; + case 3: + predict = LosslessUtils.Predictor3(currentRow[x - 1], upperRow + x); + break; + case 4: + predict = LosslessUtils.Predictor4(currentRow[x - 1], upperRow + x); + break; + case 5: + predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow + x); + break; + case 6: + predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow + x); + break; + case 7: + predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow + x); + break; + case 8: + predict = LosslessUtils.Predictor8(currentRow[x - 1], upperRow + x); + break; + case 9: + predict = LosslessUtils.Predictor9(currentRow[x - 1], upperRow + x); + break; + case 10: + predict = LosslessUtils.Predictor10(currentRow[x - 1], upperRow + x); + break; + case 11: + predict = LosslessUtils.Predictor11(currentRow[x - 1], upperRow + x); + break; + case 12: + predict = LosslessUtils.Predictor12(currentRow[x - 1], upperRow + x); + break; + case 13: + predict = LosslessUtils.Predictor13(currentRow[x - 1], upperRow + x); + break; + } } - } - if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1) - { - residual = LosslessUtils.SubPixels(currentRow[x], predict); - } - else - { - residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen); + if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1) + { + residual = LosslessUtils.SubPixels(currentRow[x], predict); + } + else + { + residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen); - // Update the source image. - currentRow[x] = LosslessUtils.AddPixels(predict, residual); + // Update the source image. + currentRow[x] = LosslessUtils.AddPixels(predict, residual); - // x is never 0 here so we do not need to update upperRow like below. - } + // x is never 0 here so we do not need to update upperRow like below. + } - if ((currentRow[x] & MaskAlpha) == 0) - { - // If alpha is 0, cleanup RGB. We can choose the RGB values of the - // residual for best compression. The prediction of alpha itself can be - // non-zero and must be kept though. We choose RGB of the residual to be - // 0. - residual &= MaskAlpha; - - // Update the source image. - currentRow[x] = predict & ~MaskAlpha; - - // The prediction for the rightmost pixel in a row uses the leftmost - // pixel - // in that row as its top-right context pixel. Hence if we change the - // leftmost pixel of current_row, the corresponding change must be - // applied - // to upperRow as well where top-right context is being read from. - if (x == 0 && y != 0) + if ((currentRow[x] & MaskAlpha) == 0) { - upperRow[width] = currentRow[0]; + // If alpha is 0, cleanup RGB. We can choose the RGB values of the + // residual for best compression. The prediction of alpha itself can be + // non-zero and must be kept though. We choose RGB of the residual to be + // 0. + residual &= MaskAlpha; + + // Update the source image. + currentRow[x] = predict & ~MaskAlpha; + + // The prediction for the rightmost pixel in a row uses the leftmost + // pixel + // in that row as its top-right context pixel. Hence if we change the + // leftmost pixel of current_row, the corresponding change must be + // applied + // to upperRow as well where top-right context is being read from. + if (x == 0 && y != 0) + { + upperRow[width] = currentRow[0]; + } } - } - output[x - xStart] = residual; + output[x - xStart] = residual; + } } } } @@ -486,14 +541,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless Span upperRow = argbScratch; Span currentRow = upperRow.Slice(width + 1); Span currentMaxDiffs = MemoryMarshal.Cast(currentRow.Slice(width + 1)); + + // TODO: This should be wrapped in a condition? Span lowerMaxDiffs = currentMaxDiffs.Slice(width); - for (int y = 0; y < height; y++) + for (int y = 0; y < height; ++y) { Span tmp32 = upperRow; upperRow = currentRow; currentRow = tmp32; Span src = argb.Slice(y * width, width + ((y + 1) < height ? 1 : 0)); src.CopyTo(currentRow); + + // TODO: Near lossless conditional? if (maxQuantization > 1) { // Compute max_diffs for the lower row now, because that needs the @@ -537,77 +596,90 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless } } - private static void PredictBatch(int mode, int xStart, int y, int numPixels, Span current, Span upper, Span output) + private static void PredictBatch( + int mode, + int xStart, + int y, + int numPixels, + Span currentSpan, + Span upperSpan, + Span outputSpan) { - if (xStart == 0) + fixed (uint* current = currentSpan) + fixed (uint* upper = upperSpan) + fixed (uint* outputFixed = outputSpan) { - if (y == 0) + uint* output = outputFixed; + if (xStart == 0) { - // ARGB_BLACK. - LosslessUtils.PredictorSub0(current, 1, output); + if (y == 0) + { + // ARGB_BLACK. + LosslessUtils.PredictorSub0(current, 1, output); + } + else + { + // Top one. + LosslessUtils.PredictorSub2(current, upper, 1, output); + } + + ++xStart; + ++output; + --numPixels; } - else + + if (y == 0) { - // Top one. - LosslessUtils.PredictorSub2(current, 0, upper, 1, output); + // Left one. + LosslessUtils.PredictorSub1(current + xStart, numPixels, output); } - - xStart++; - output = output.Slice(1); - numPixels--; - } - - if (y == 0) - { - // Left one. - LosslessUtils.PredictorSub1(current, xStart, numPixels, output); - } - else - { - switch (mode) + else { - case 0: - LosslessUtils.PredictorSub0(current, numPixels, output); - break; - case 1: - LosslessUtils.PredictorSub1(current, xStart, numPixels, output); - break; - case 2: - LosslessUtils.PredictorSub2(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 3: - LosslessUtils.PredictorSub3(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 4: - LosslessUtils.PredictorSub4(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 5: - LosslessUtils.PredictorSub5(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 6: - LosslessUtils.PredictorSub6(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 7: - LosslessUtils.PredictorSub7(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 8: - LosslessUtils.PredictorSub8(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 9: - LosslessUtils.PredictorSub9(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 10: - LosslessUtils.PredictorSub10(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 11: - LosslessUtils.PredictorSub11(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 12: - LosslessUtils.PredictorSub12(current, xStart, upper.Slice(xStart), numPixels, output); - break; - case 13: - LosslessUtils.PredictorSub13(current, xStart, upper.Slice(xStart), numPixels, output); - break; + switch (mode) + { + case 0: + LosslessUtils.PredictorSub0(current, numPixels, output); + break; + case 1: + LosslessUtils.PredictorSub1(current + xStart, numPixels, output); + break; + case 2: + LosslessUtils.PredictorSub2(current + xStart, upper + xStart, numPixels, output); + break; + case 3: + LosslessUtils.PredictorSub3(current + xStart, upper + xStart, numPixels, output); + break; + case 4: + LosslessUtils.PredictorSub4(current + xStart, upper + xStart, numPixels, output); + break; + case 5: + LosslessUtils.PredictorSub5(current + xStart, upper + xStart, numPixels, output); + break; + case 6: + LosslessUtils.PredictorSub6(current + xStart, upper + xStart, numPixels, output); + break; + case 7: + LosslessUtils.PredictorSub7(current + xStart, upper + xStart, numPixels, output); + break; + case 8: + LosslessUtils.PredictorSub8(current + xStart, upper + xStart, numPixels, output); + break; + case 9: + LosslessUtils.PredictorSub9(current + xStart, upper + xStart, numPixels, output); + break; + case 10: + LosslessUtils.PredictorSub10(current + xStart, upper + xStart, numPixels, output); + break; + case 11: + LosslessUtils.PredictorSub11(current + xStart, upper + xStart, numPixels, output); + break; + case 12: + LosslessUtils.PredictorSub12(current + xStart, upper + xStart, numPixels, output); + break; + case 13: + LosslessUtils.PredictorSub13(current + xStart, upper + xStart, numPixels, output); + break; + } } } } @@ -627,7 +699,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless right = AddGreenToBlueAndRed(right); } - for (int x = 1; x < width - 1; x++) + for (int x = 1; x < width - 1; ++x) { uint up = argb[-stride + x]; uint down = argb[stride + x]; @@ -884,7 +956,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless private static float PredictionCostSpatialHistogram(int[][] accumulated, int[][] tile) { double retVal = 0.0d; - for (int i = 0; i < 4; i++) + for (int i = 0; i < 4; ++i) { double kExpValue = 0.94; retVal += PredictionCostSpatial(tile[i], 1, kExpValue); diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs index 9df6591f3a..8b1a44fe57 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs @@ -607,12 +607,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless { int cacheBits = 0; var histogramSymbols = new short[1]; // Only one tree, one symbol. + + // TODO: Can HuffmanTreeCode be struct var huffmanCodes = new HuffmanTreeCode[5]; for (int i = 0; i < huffmanCodes.Length; i++) { huffmanCodes[i] = new HuffmanTreeCode(); } + // TODO: Can HuffmanTree be struct var huffTree = new HuffmanTree[3UL * WebPConstants.CodeLengthCodes]; for (int i = 0; i < huffTree.Length; i++) {