diff --git a/src/ImageSharp/Formats/WebP/LossyUtils.cs b/src/ImageSharp/Formats/WebP/LossyUtils.cs
index b880e6781..760f1bb1a 100644
--- a/src/ImageSharp/Formats/WebP/LossyUtils.cs
+++ b/src/ImageSharp/Formats/WebP/LossyUtils.cs
@@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 2, 0, bc);
Dst(dst, 3, 2, bc);
Dst(dst, 3, 0, Avg2(C, D));
- Dst(dst, 0, 3, Avg3(K, I, J));
+ Dst(dst, 0, 3, Avg3(K, J, I));
Dst(dst, 0, 2, Avg3(J, I, X));
byte ixa = Avg3(I, X, A);
Dst(dst, 0, 1, ixa);
@@ -447,14 +447,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int i = 0; i < 4; ++i)
{
// vertical pass
- int a = src[srcOffset] + src[srcOffset + 8]; // [-4096, 4094]
- int b = src[srcOffset] - src[srcOffset + 8]; // [-4095, 4095]
- int c = Mul2(src[srcOffset + 4]) - Mul1(src[srcOffset + 12]); // [-3783, 3783]
- int d = Mul1(src[srcOffset + 4]) + Mul2(src[srcOffset + 12]); // [-3785, 3781]
- tmp[tmpOffset] = a + d; // [-7881, 7875]
- tmp[tmpOffset + 1] = b + c; // [-7878, 7878]
- tmp[tmpOffset + 2] = b - c; // [-7878, 7878]
- tmp[tmpOffset + 3] = a - d; // [-7877, 7879]
+ 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++;
}
@@ -462,10 +462,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Each pass is expanding the dynamic range by ~3.85 (upper bound).
// The exact value is (2. + (20091 + 35468) / 65536).
// After the second pass, maximum interval is [-3794, 3794], assuming
- // an input in [-2048, 2047] interval. We then need to add a dst value
- // in the [0, 255] range.
- // In the worst case scenario, the input to clip_8b() can be as large as
- // [-60713, 60968].
+ // an input in [-2048, 2047] interval. We then need to add a dst value in the [0, 255] range.
+ // In the worst case scenario, the input to clip_8b() can be as large as [-60713, 60968].
tmpOffset = 0;
for (int i = 0; i < 4; ++i)
{
@@ -560,9 +558,105 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- // We process u and v together stashed into 32bit(16bit each).
+ // Simple In-loop filtering (Paragraph 15.2)
+ public static void SimpleVFilter16(byte[] p, int offset, int stride, int thresh)
+ {
+ int thresh2 = (2 * thresh) + 1;
+ for (int i = 0; i < 16; ++i)
+ {
+ if (NeedsFilter(p, offset + i, stride, thresh2))
+ {
+ DoFilter2(p, offset + i, stride);
+ }
+ }
+ }
+
+ public static void SimpleHFilter16(byte[] p, int offset, int stride, int thresh)
+ {
+ int thresh2 = (2 * thresh) + 1;
+ for (int i = 0; i < 16; ++i)
+ {
+ if (NeedsFilter(p, offset + (i * stride), 1, thresh2))
+ {
+ DoFilter2(p, offset + (i * stride), 1);
+ }
+ }
+ }
+
+ public static void SimpleVFilter16i(byte[] p, int offset, int stride, int thresh)
+ {
+ for (int k = 3; k > 0; --k)
+ {
+ offset += 4 * stride;
+ SimpleVFilter16(p, offset, stride, thresh);
+ }
+ }
+
+ public static void SimpleHFilter16i(byte[] p, int offset, int stride, int thresh)
+ {
+ for (int k = 3; k > 0; --k)
+ {
+ offset += stride;
+ SimpleHFilter16(p, offset, stride, thresh);
+ }
+ }
+
+ public static void VFilter16(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh);
+ }
+
+ public static void HFilter16(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh);
+ }
+
+ public static void VFilter16i(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ for (int k = 3; k > 0; --k)
+ {
+ offset += 4 * stride;
+ FilterLoop24(p, offset, stride, 1, 16, thresh, ithresh, hevThresh);
+ }
+ }
+
+ public static void HFilter16i(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ for (int k = 3; k > 0; --k)
+ {
+ offset += 4;
+ FilterLoop24(p, offset, 1, stride, 16, thresh, ithresh, hevThresh);
+ }
+ }
+
+ // 8-pixels wide variant, for chroma filtering.
+ public static void VFilter8(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh);
+ FilterLoop26(v, offset, stride, 1, 8, thresh, ithresh, hevThresh);
+ }
+
+ public static void HFilter8(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh)
+ {
+ FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh);
+ FilterLoop26(v, offset, 1, stride, 8, thresh, ithresh, hevThresh);
+ }
+
+ public static void VFilter8i(byte[] u, byte[] 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);
+ }
+
+ public static void HFilter8i(byte[] u, byte[] 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);
+ }
+
public static uint LoadUv(byte u, byte v)
{
+ // We process u and v together stashed into 32bit(16bit each).
return (uint)(u | (v << 16));
}
@@ -571,7 +665,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
bgr[0] = (byte)YuvToB(y, u);
bgr[1] = (byte)YuvToG(y, u, v);
bgr[2] = (byte)YuvToR(y, v);
- int tmp = 0;
}
public static int YuvToR(int y, int v)
@@ -589,6 +682,157 @@ namespace SixLabors.ImageSharp.Formats.WebP
return Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
}
+ // Complex In-loop filtering (Paragraph 15.3)
+ private static void FilterLoop24(
+ byte[] p,
+ int offset,
+ int hStride,
+ int vStride,
+ int size,
+ int thresh,
+ int ithresh,
+ int hevThresh)
+ {
+ int thresh2 = (2 * thresh) + 1;
+ while (size-- > 0)
+ {
+ if (NeedsFilter2(p, offset, hStride, thresh2, ithresh))
+ {
+ if (Hev(p, offset, hStride, hevThresh))
+ {
+ DoFilter2(p, offset, hStride);
+ }
+ else
+ {
+ DoFilter4(p, offset, hStride);
+ }
+ }
+
+ offset += vStride;
+ }
+ }
+
+ private static void FilterLoop26(
+ byte[] p,
+ int offset,
+ int hStride,
+ int vStride,
+ int size,
+ int thresh,
+ int ithresh,
+ int hevThresh)
+ {
+ int thresh2 = (2 * thresh) + 1;
+ while (size-- > 0)
+ {
+ if (NeedsFilter2(p, offset, hStride, thresh2, ithresh))
+ {
+ if (Hev(p, offset, hStride, hevThresh))
+ {
+ DoFilter2(p, offset, hStride);
+ }
+ else
+ {
+ DoFilter6(p, offset, hStride);
+ }
+ }
+
+ offset += vStride;
+ }
+ }
+
+ private static void DoFilter2(byte[] p, int offset, int step)
+ {
+ // 4 pixels in, 2 pixels out
+ int p1 = p[offset - (2 * step)];
+ int p0 = p[offset - step];
+ int q0 = p[offset];
+ int q1 = p[offset + step];
+ int a = (3 * (q0 - p0)) + Vp8LookupTables.Sclip1(p1 - q1);
+ int a1 = Vp8LookupTables.Sclip2((a + 4) >> 3);
+ int a2 = Vp8LookupTables.Sclip2((a + 3) >> 3);
+ p[offset - step] = Vp8LookupTables.Clip1(p0 + a2);
+ p[offset] = Vp8LookupTables.Clip1(q0 - a1);
+ }
+
+ private static void DoFilter4(byte[] p, int offset, int step)
+ {
+ // 4 pixels in, 4 pixels out
+ int p1 = p[offset - (2 * step)];
+ int p0 = p[offset - step];
+ int q0 = p[offset];
+ int q1 = p[offset + step];
+ int a = 3 * (q0 - p0);
+ int a1 = Vp8LookupTables.Sclip2((a + 4) >> 3);
+ int a2 = Vp8LookupTables.Sclip2((a + 3) >> 3);
+ int a3 = (a1 + 1) >> 1;
+ p[offset - (2 * step)] = Vp8LookupTables.Clip1(p1 + a3);
+ p[offset - step] = Vp8LookupTables.Clip1(p0 + a2);
+ p[offset] = Vp8LookupTables.Clip1(q0 - a1);
+ p[offset + step] = Vp8LookupTables.Clip1(q1 - a3);
+ }
+
+ private static void DoFilter6(byte[] 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 q0 = p[offset];
+ int q1 = p[offset + step];
+ int q2 = p[offset + (2 * step)];
+ int a = Vp8LookupTables.Clip1((3 * (q0 - p0)) + Vp8LookupTables.Clip1(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)] = Vp8LookupTables.Clip1(p2 + a3);
+ p[offset - (2 * step)] = Vp8LookupTables.Clip1(p1 + a2);
+ p[offset - step] = Vp8LookupTables.Clip1(p0 + a1);
+ p[offset] = Vp8LookupTables.Clip1(q0 - a1);
+ p[offset + step] = Vp8LookupTables.Clip1(q1 - a2);
+ p[offset + (2 * step)] = Vp8LookupTables.Clip1(q2 - a3);
+ }
+
+ private static bool NeedsFilter(byte[] p, int offset, int step, int thresh)
+ {
+ int p1 = p[offset + (-2 * step)];
+ int p0 = p[offset - step];
+ int q0 = p[offset];
+ int q1 = p[offset + step];
+ return (Vp8LookupTables.Abs0(p1 - p0) > thresh) || (Vp8LookupTables.Abs0(q1 - q0) > thresh);
+ }
+
+ private static bool NeedsFilter2(byte[] p, int offset, int step, int t, int it)
+ {
+ int p3 = p[offset - (4 * step)];
+ int p2 = p[offset - (3 * step)];
+ int p1 = p[offset - (2 * step)];
+ 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)];
+ if (((4 * Vp8LookupTables.Abs0(p0 - q0)) + Vp8LookupTables.Abs0(p1 - q1)) > t)
+ {
+ return false;
+ }
+
+ return Vp8LookupTables.Abs0(p3 - p2) <= it && Vp8LookupTables.Abs0(p2 - p1) <= it &&
+ Vp8LookupTables.Abs0(p1 - p0) <= it && Vp8LookupTables.Abs0(q3 - q2) <= it &&
+ Vp8LookupTables.Abs0(q2 - q1) <= it && Vp8LookupTables.Abs0(q1 - q0) <= it;
+ }
+
+ private static bool Hev(byte[] p, int offset, int step, int thresh)
+ {
+ int p1 = p[offset -(2 * step)];
+ int p0 = p[offset - step];
+ int q0 = p[offset];
+ int q1 = p[offset + step];
+ return (Vp8LookupTables.Abs0(p1 - p0) > thresh) || (Vp8LookupTables.Abs0(q1 - q0) > thresh);
+ }
+
private static int MultHi(int v, int coeff)
{
return (v * coeff) >> 8;
diff --git a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs
index 57e163ccf..6f0f07374 100644
--- a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
internal class Vp8Decoder
{
- public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities, Vp8Io io)
+ public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities)
{
this.FilterHeader = new Vp8FilterHeader();
this.FrameHeader = frameHeader;
@@ -71,7 +71,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions];
- this.Init(io);
}
public Vp8FrameHeader FrameHeader { get; }
@@ -219,53 +218,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- public void Init(Vp8Io io)
- {
- int intraPredModeSize = 4 * this.MbWidth;
- this.IntraT = new byte[intraPredModeSize];
-
- int extraPixels = WebPConstants.FilterExtraRows[(int)this.Filter];
- if (this.Filter is LoopFilter.Complex)
- {
- // For complex filter, we need to preserve the dependency chain.
- this.TopLeftMbX = 0;
- this.TopLeftMbY = 0;
- }
- else
- {
- // For simple filter, we can filter only the cropped region. We include 'extraPixels' on
- // the other side of the boundary, since vertical or horizontal filtering of the previous
- // macroblock can modify some abutting pixels.
- this.TopLeftMbX = (io.CropLeft - extraPixels) >> 4;
- this.TopLeftMbY = (io.CropTop - extraPixels) >> 4;
- if (this.TopLeftMbX < 0)
- {
- this.TopLeftMbX = 0;
- }
-
- if (this.TopLeftMbY < 0)
- {
- this.TopLeftMbY = 0;
- }
- }
-
- // We need some 'extra' pixels on the right/bottom.
- this.BottomRightMbY = (io.CropBottom + 15 + extraPixels) >> 4;
- this.BottomRightMbX = (io.CropRight + 15 + extraPixels) >> 4;
- if (this.BottomRightMbX > this.MbWidth)
- {
- this.BottomRightMbX = this.MbWidth;
- }
-
- if (this.BottomRightMbY > this.MbHeight)
- {
- this.BottomRightMbY = this.MbHeight;
- }
-
- this.PrecomputeFilterStrengths();
- }
-
- private void PrecomputeFilterStrengths()
+ public void PrecomputeFilterStrengths()
{
if (this.Filter is LoopFilter.None)
{
diff --git a/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs b/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs
index bb6c6da17..a95e0ba92 100644
--- a/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs
@@ -20,8 +20,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// Gets or sets a value indicating whether to do inner filtering.
+ /// TODO: can this be a bool?
///
- public byte InnerFiltering { get; set; }
+ public byte UseInnerFiltering { get; set; }
///
/// Gets or sets the high edge variance threshold in [0..2].
diff --git a/src/ImageSharp/Formats/WebP/Vp8LookupTables.cs b/src/ImageSharp/Formats/WebP/Vp8LookupTables.cs
new file mode 100644
index 000000000..ec20f2cad
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Vp8LookupTables.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.WebP
+{
+ internal static class Vp8LookupTables
+ {
+ private static readonly byte[] abs0;
+
+ private static readonly byte[] clip1;
+
+ private static readonly sbyte[] sclip1;
+
+ private static readonly sbyte[] sclip2;
+
+ static Vp8LookupTables()
+ {
+ // TODO: maybe use hashset here
+ abs0 = new byte[511];
+ for (int i = -255; i <= 255; ++i)
+ {
+ abs0[255 + i] = (byte)((i < 0) ? -i : i);
+ }
+
+ clip1 = new byte[766];
+ for (int i = -255; i <= 255 + 255; ++i)
+ {
+ clip1[255 + i] = (byte)((i < 0) ? 0 : (i > 255) ? 255 : i);
+ }
+
+ sclip1 = new sbyte[2041];
+ for (int i = -1020; i <= 1020; ++i)
+ {
+ sclip1[1020 + i] = (sbyte)((i < -128) ? -128 : (i > 127) ? 127 : i);
+ }
+
+ sclip2 = new sbyte[225];
+ for (int i = -112; i <= 112; ++i)
+ {
+ sclip2[112 + i] = (sbyte)((i < -16) ? -16 : (i > 15) ? 15 : i);
+ }
+ }
+
+ public static byte Abs0(int v)
+ {
+ return abs0[v + 255];
+ }
+
+ public static byte Clip1(int v)
+ {
+ return clip1[v + 255];
+ }
+
+ public static sbyte Sclip1(int v)
+ {
+ return sclip1[v + 1020];
+ }
+
+ public static sbyte Sclip2(int v)
+ {
+ return sclip2[v + 112];
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs
index 252adc952..179d6d77a 100644
--- a/src/ImageSharp/Formats/WebP/WebPConstants.cs
+++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs
@@ -132,8 +132,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// How many extra lines are needed on the MB boundary for caching, given a filtering level.
- /// Simple filter: up to 2 luma samples are read and 1 is written.
- /// Complex filter: up to 4 luma samples are read and 3 are written. Same for U/V, so it's 8 samples total (because of the 2x upsampling).
+ /// Simple filter(1): up to 2 luma samples are read and 1 is written.
+ /// Complex filter(2): up to 4 luma samples are read and 3 are written. Same for U/V, so it's 8 samples total (because of the 2x upsampling).
///
public static readonly byte[] FilterExtraRows = { 0, 2, 8 };
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index ffeca660c..43d723917 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -57,11 +57,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
var proba = new Vp8Proba();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
- Vp8Io io = InitializeVp8Io(pictureHeader);
- var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba, io);
+ var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba);
+ Vp8Io io = InitializeVp8Io(decoder, pictureHeader);
// Paragraph 9.4: Parse the filter specs.
this.ParseFilterHeader(decoder);
+ decoder.PrecomputeFilterStrengths();
// Paragraph 9.5: Parse partitions.
this.ParsePartitions(decoder);
@@ -94,7 +95,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
byte b = pixelData[idx];
byte g = pixelData[idx + 1];
byte r = pixelData[idx + 2];
- color.FromRgba32(new Rgba32(r, g, b, 255));
+
+ // TODO: use bulk conversion here.
+ color.FromBgr24(new Bgr24(r, g, b));
pixelRow[x] = color;
}
}
@@ -214,11 +217,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
bool filterRow = (dec.Filter != LoopFilter.None) &&
(dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY);
- this.ReconstructRow(dec, filterRow);
+ this.ReconstructRow(dec);
+ if (filterRow)
+ {
+ this.FilterRow(dec);
+ }
+
this.FinishRow(dec, io);
}
- private void ReconstructRow(Vp8Decoder dec, bool filterRow)
+ private void ReconstructRow(Vp8Decoder dec)
{
int mby = dec.MbY;
@@ -313,7 +321,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (block.IsI4x4)
{
// uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
- Span topRight = MemoryMarshal.Cast(yuv.AsSpan(yOff - WebPConstants.Bps + 16));
+ //Span topRight = MemoryMarshal.Cast(yuv.AsSpan(yOff - WebPConstants.Bps + 16));
if (mby > 0)
{
if (mbx >= dec.MbWidth - 1)
@@ -457,7 +465,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
vDst.Slice(7 * WebPConstants.Bps, 8).CopyTo(topYuv.V);
}
- // Transfer reconstructed samples from yuv_b_ cache to final destination.
+ // Transfer reconstructed samples from yuv_buffer cache to final destination.
int cacheId = 0; // TODO: what should be cacheId, always 0?
int yOffset = cacheId * 16 * dec.CacheYStride;
int uvOffset = cacheId * 8 * dec.CacheUvStride;
@@ -477,6 +485,82 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ private void FilterRow(Vp8Decoder dec)
+ {
+ int mby = dec.MbY;
+ for (int mbx = dec.TopLeftMbX; mbx < dec.BottomRightMbX; ++mbx)
+ {
+ //this.DoFilter(dec, mbx, mby);
+ }
+ }
+
+ private void DoFilter(Vp8Decoder dec, int mbx, int mby)
+ {
+ int yBps = dec.CacheYStride;
+ Vp8FilterInfo filterInfo = dec.FilterInfo[dec.MbX];
+ int iLevel = filterInfo.InnerLevel;
+ int limit = filterInfo.Limit;
+
+ if (limit is 0)
+ {
+ return;
+ }
+
+ if (dec.Filter is LoopFilter.Simple)
+ {
+ int offset = mbx * 16;
+ if (mbx > 0)
+ {
+ LossyUtils.SimpleHFilter16(dec.CacheY, offset, yBps, limit + 4);
+ }
+
+ if (filterInfo.UseInnerFiltering > 0)
+ {
+ LossyUtils.SimpleHFilter16i(dec.CacheY, offset, yBps, limit);
+ }
+
+ if (mby > 0)
+ {
+ LossyUtils.SimpleVFilter16(dec.CacheY, offset, yBps, limit + 4);
+ }
+
+ if (filterInfo.UseInnerFiltering > 0)
+ {
+ LossyUtils.SimpleVFilter16i(dec.CacheY, offset, yBps, limit);
+ }
+ }
+ else if (dec.Filter is LoopFilter.Complex)
+ {
+ int uvBps = dec.CacheUvStride;
+ int yOffset = mbx * 16;
+ int uvOffset = mbx * 8;
+ int hevThresh = filterInfo.HighEdgeVarianceThreshold;
+ if (mbx > 0)
+ {
+ LossyUtils.HFilter16(dec.CacheY, yOffset, yBps, limit + 4, iLevel, hevThresh);
+ LossyUtils.HFilter8(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit + 4, iLevel, hevThresh);
+ }
+
+ if (filterInfo.UseInnerFiltering > 0)
+ {
+ LossyUtils.HFilter16i(dec.CacheY, yOffset, yBps, limit, iLevel, hevThresh);
+ LossyUtils.HFilter8i(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit, iLevel, hevThresh);
+ }
+
+ if (mby > 0)
+ {
+ LossyUtils.VFilter16(dec.CacheY, yOffset, yBps, limit + 4, iLevel, hevThresh);
+ LossyUtils.VFilter8(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit + 4, iLevel, hevThresh);
+ }
+
+ if (filterInfo.UseInnerFiltering > 0)
+ {
+ LossyUtils.VFilter16i(dec.CacheY, yOffset, yBps, limit, iLevel, hevThresh);
+ LossyUtils.VFilter8i(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit, iLevel, hevThresh);
+ }
+ }
+ }
+
private void FinishRow(Vp8Decoder dec, Vp8Io io)
{
int cacheId = 0;
@@ -532,10 +616,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Rotate top samples if needed.
if (!isLastRow)
{
- // TODO: double check this.
- yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY);
- uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU);
- vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV);
+ // TODO: double check this. Cache needs extra rows for filtering!
+ //yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY);
+ //uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU);
+ //vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV);
}
}
@@ -744,7 +828,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (dec.Filter != LoopFilter.None)
{
dec.FilterInfo[dec.MbX] = dec.FilterStrength[blockData.Segment, blockData.IsI4x4 ? 1 : 0];
- dec.FilterInfo[dec.MbX].InnerFiltering |= (byte)(skip is 0 ? 1 : 0);
+ dec.FilterInfo[dec.MbX].UseInnerFiltering |= (byte)(skip is 0 ? 1 : 0);
}
}
@@ -760,6 +844,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
Vp8BandProbas[] acProba;
Vp8MacroBlock leftMb = dec.LeftMacroBlock;
short[] dst = block.Coeffs;
+ for (int i = 0; i < dst.Length; i++)
+ {
+ dst[i] = 0;
+ }
if (!block.IsI4x4)
{
@@ -1208,7 +1296,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- private static Vp8Io InitializeVp8Io(Vp8PictureHeader pictureHeader)
+ private static Vp8Io InitializeVp8Io(Vp8Decoder dec, Vp8PictureHeader pictureHeader)
{
var io = default(Vp8Io);
io.Width = (int)pictureHeader.Width;
@@ -1225,6 +1313,48 @@ namespace SixLabors.ImageSharp.Formats.WebP
io.MbH = io.Height;
io.YStride = (int)(16 * ((pictureHeader.Width + 15) >> 4));
io.UvStride = (int)(8 * ((pictureHeader.Width + 15) >> 4));
+
+ int intraPredModeSize = 4 * dec.MbWidth;
+ dec.IntraT = new byte[intraPredModeSize];
+
+ int extraPixels = WebPConstants.FilterExtraRows[(int)dec.Filter];
+ if (dec.Filter is LoopFilter.Complex)
+ {
+ // For complex filter, we need to preserve the dependency chain.
+ dec.TopLeftMbX = 0;
+ dec.TopLeftMbY = 0;
+ }
+ else
+ {
+ // For simple filter, we can filter only the cropped region. 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 = (io.CropLeft - extraPixels) >> 4;
+ dec.TopLeftMbY = (io.CropTop - extraPixels) >> 4;
+ if (dec.TopLeftMbX < 0)
+ {
+ dec.TopLeftMbX = 0;
+ }
+
+ if (dec.TopLeftMbY < 0)
+ {
+ dec.TopLeftMbY = 0;
+ }
+ }
+
+ // We need some 'extra' pixels on the right/bottom.
+ dec.BottomRightMbY = (io.CropBottom + 15 + extraPixels) >> 4;
+ dec.BottomRightMbX = (io.CropRight + 15 + extraPixels) >> 4;
+ if (dec.BottomRightMbX > dec.MbWidth)
+ {
+ dec.BottomRightMbX = dec.MbWidth;
+ }
+
+ if (dec.BottomRightMbY > dec.MbHeight)
+ {
+ dec.BottomRightMbY = dec.MbHeight;
+ }
+
return io;
}
@@ -1298,6 +1428,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return value < 0 ? 0 : value > max ? max : value;
}
+ // TODO: move to LookupTables
private void InitializeModesProbabilities()
{
// Paragraph 11.5