diff --git a/src/ImageSharp/Formats/WebP/LossyUtils.cs b/src/ImageSharp/Formats/WebP/LossyUtils.cs index 2c1ae62f9d..b880e6781a 100644 --- a/src/ImageSharp/Formats/WebP/LossyUtils.cs +++ b/src/ImageSharp/Formats/WebP/LossyUtils.cs @@ -22,15 +22,15 @@ namespace SixLabors.ImageSharp.Formats.WebP 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[offset - 1 + (j * WebPConstants.Bps)] + yuv[offset + j - WebPConstants.Bps]; } Put16(dc >> 5, dst); } - public static void TM16_C(Span dst) + public static void TM16_C(Span dst, byte[] yuv, int offset) { - + TrueMotion(dst, yuv, offset, 16); } public static void VE16_C(Span dst, byte[] yuv, int offset) @@ -101,9 +101,10 @@ namespace SixLabors.ImageSharp.Formats.WebP Put8x8uv((byte)(dc0 >> 4), dst); } - public static void TM8uv_C(Span dst) + public static void TM8uv_C(Span dst, byte[] yuv, int offset) { // TrueMotion + TrueMotion(dst, yuv, offset, 8); } public static void VE8uv_C(Span dst, byte[] yuv, int offset) @@ -179,9 +180,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - public static void TM4_C(Span dst) + public static void TM4_C(Span dst, byte[] yuv, int offset) { - + TrueMotion(dst, yuv, offset, 4); } public static void VE4_C(Span dst, byte[] yuv, int offset) @@ -538,6 +539,27 @@ namespace SixLabors.ImageSharp.Formats.WebP } } + private static void TrueMotion(Span dst, byte[] yuv, int offset, int size) + { + // For information about how true motion works, see rfc6386, page 52. ff and section 20.14. + int topOffset = offset - WebPConstants.Bps; + Span top = yuv.AsSpan(topOffset); + byte p = yuv[topOffset - 1]; + int leftOffset = offset - 1; + byte left = yuv[leftOffset]; + for (int y = 0; y < size; ++y) + { + for (int x = 0; x < size; ++x) + { + dst[x] = (byte)Clamp255(left + top[x] - p); + } + + leftOffset += WebPConstants.Bps; + left = yuv[leftOffset]; + dst = dst.Slice(WebPConstants.Bps); + } + } + // We process u and v together stashed into 32bit(16bit each). public static uint LoadUv(byte u, byte v) { @@ -637,5 +659,10 @@ namespace SixLabors.ImageSharp.Formats.WebP { dst[x + (y * WebPConstants.Bps)] = v; } + + private static int Clamp255(int x) + { + return x < 0 ? 0 : (x > 255 ? 255 : x); + } } } diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index 64afc2fa00..252adc9527 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -125,10 +125,10 @@ namespace SixLabors.ImageSharp.Formats.WebP public const int Bps = 32; // intra prediction modes (TODO: maybe use an enum for this) - public const int DcPred = 0; - public const int TmPred = 1; - public const int VPred = 2; - public const int HPred = 3; + public const int DcPred = 0; // predict DC using row above and column to the left + public const int TmPred = 1; // propagate second differences a la "True Motion" + public const int VPred = 2; // predict rows using row above + public const int HPred = 3; // predict columns using column to the left /// /// How many extra lines are needed on the MB boundary for caching, given a filtering level. diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index a5dc5dd506..ffeca660cc 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.WebP LossyUtils.DC4_C(dst, yuv, offset); break; case 1: - LossyUtils.TM4_C(dst); + LossyUtils.TM4_C(dst, yuv, offset); break; case 2: LossyUtils.VE4_C(dst, yuv, offset); @@ -383,7 +383,7 @@ namespace SixLabors.ImageSharp.Formats.WebP LossyUtils.DC16_C(yDst, yuv, yOff); break; case 1: - LossyUtils.TM16_C(yDst); + LossyUtils.TM16_C(yDst, yuv, yOff); break; case 2: LossyUtils.VE16_C(yDst, yuv, yOff); @@ -421,8 +421,8 @@ namespace SixLabors.ImageSharp.Formats.WebP LossyUtils.DC8uv_C(vDst, yuv, vOff); break; case 1: - LossyUtils.TM8uv_C(uDst); - LossyUtils.TM8uv_C(vDst); + LossyUtils.TM8uv_C(uDst, yuv, uOff); + LossyUtils.TM8uv_C(vDst, yuv, vOff); break; case 2: LossyUtils.VE8uv_C(uDst, yuv, uOff);