diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs
new file mode 100644
index 0000000000..9e1a031254
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Diagnostics;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Filters
+{
+ // TODO from dsp.h
+ // public enum WebPFilterType
+ // {
+ // None = 0,
+ // Horizontal,
+ // Vertical,
+ // Gradient,
+ // Last = Gradient + 1, // end marker
+ // Best, // meta types
+ // Fast
+ // }
+
+ abstract class WebPFilterBase
+ {
+ ///
+ ///
+ ///
+ /// nullable as prevLine is nullable in the original but Span'T can't be null.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public abstract void Unfilter(
+ Span prevLine,
+ int? prevLineOffset,
+ Span preds,
+ int predsOffset,
+ Span currentLine,
+ int currentLineOffset,
+ int width);
+
+ public abstract void Filter(
+ Span input, int inputOffset,
+ int width, int height, int stride,
+ Span output, int outputOffset);
+
+ protected static void SanityCheck(
+ Span input, Span output, int width, int numRows, int height, int stride, int row)
+ {
+ Debug.Assert(input != null);
+ Debug.Assert(output != null);
+ Debug.Assert(width > 0);
+ Debug.Assert(height > 0);
+ Debug.Assert(stride > width);
+ Debug.Assert(row >= 0);
+ Debug.Assert(height > 0);
+ Debug.Assert(row + numRows <= height);
+ }
+
+ protected static void PredictLine(
+ Span src, int srcOffset,
+ Span pred, int predOffset,
+ Span dst, int dstOffset,
+ int length, bool inverse)
+ {
+ if (inverse)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ dst[i] = (byte)(src[i] + pred[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < length; i++)
+ {
+ dst[i] = (byte)(src[i] - pred[i]);
+ }
+ }
+ }
+
+ protected void UnfilterHorizontalOrVerticalCore(
+ byte pred,
+ Span input, int inputOffset,
+ Span output, int outputOffset,
+ int width)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ output[outputOffset + i] = (byte)(pred + input[inputOffset + i]);
+ pred = output[i];
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterGradient.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterGradient.cs
new file mode 100644
index 0000000000..ce751b1f59
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterGradient.cs
@@ -0,0 +1,103 @@
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Filters
+{
+ class WebPFilterGradient : WebPFilterBase
+ {
+
+ public override void Unfilter(
+ Span prevLine,
+ int? prevLineOffsetNullable,
+ Span input,
+ int inputOffset,
+ Span output,
+ int outputOffset,
+ int width)
+ {
+ if (prevLineOffsetNullable is int prevLineOffset)
+ {
+ byte top = prevLine[prevLineOffset];
+ byte topLeft = top;
+ byte left = top;
+ for (int i = 0; i < width; i++)
+ {
+ top = prevLine[prevLineOffset + i]; // need to read this first in case prev==out
+ left = (byte)(input[inputOffset + i] + GradientPredictor(left, top, topLeft));
+ topLeft = top;
+ output[outputOffset + i] = left;
+ }
+ }
+ else
+ {
+ this.UnfilterHorizontalOrVerticalCore(0, input, inputOffset, output, outputOffset, width);
+ }
+ }
+
+ public override void Filter(
+ Span input, int inputOffset,
+ int width, int height, int stride,
+ Span output, int outputOffset)
+ {
+ // calling (input, width, height, stride, 0, height, 0, output
+ int row = 0;
+ int numRows = height;
+ bool inverse = false;
+
+ int startOffset = row * stride;
+ int lastRow = row + numRows;
+ SanityCheck(input, output, width, numRows, height, stride, row);
+ inputOffset += startOffset;
+ outputOffset += startOffset;
+ Span preds;
+ int predsOffset;
+ if (inverse)
+ {
+ preds = output;
+ predsOffset = outputOffset;
+ }
+ else
+ {
+ preds = input;
+ predsOffset = inputOffset;
+ }
+
+ if (row == 0)
+ {
+ output[outputOffset] = input[inputOffset];
+ PredictLine(
+ input, inputOffset+1,
+ preds, predsOffset,
+ output, outputOffset+1,
+ width-1,
+ inverse);
+ }
+
+ while (row < lastRow)
+ {
+ PredictLine(
+ input, inputOffset,
+ preds, predsOffset-stride,
+ output, outputOffset,
+ 1, inverse);
+
+ for (int w = 1; w < width; w++)
+ {
+ int pred = GradientPredictor(preds[w - 1], preds[w - stride], preds[w - stride - 1]);
+ int signedPred = inverse ? pred : -pred;
+ output[outputOffset + w] = (byte)(input[inputOffset + w] + signedPred);
+ }
+
+ row++;
+ predsOffset += stride;
+ inputOffset += stride;
+ outputOffset += stride;
+ }
+ }
+
+ private static int GradientPredictor(byte a, byte b, byte c)
+ {
+ int g = a + b + c;
+ return (g & ~0xff) == 0 ? g : (g < 0) ? 0 : 255;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs
new file mode 100644
index 0000000000..61c384d7d4
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs
@@ -0,0 +1,92 @@
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Filters
+{
+ class WebPFilterHorizontal : WebPFilterBase
+ {
+ public override void Unfilter(
+ Span prevLine, int? prevLineOffsetNullable,
+ Span input, int inputOffset,
+ Span output, int outputOffset,
+ int width)
+ {
+ byte pred = prevLineOffsetNullable is int prevLineOffset
+ ? prevLine[prevLineOffset]
+ : (byte)0;
+
+ this.UnfilterHorizontalOrVerticalCore(
+ pred,
+ input, inputOffset,
+ output, outputOffset,
+ width);
+ }
+
+ public override void Filter(
+ Span input, int inputOffset,
+ int width, int height, int stride,
+ Span output, int outputOffset)
+ {
+ int numRows = height;
+ int row = 0;
+
+ const bool inverse = false;
+
+ int startOffset = row * stride;
+ int lastRow = row + height;
+ SanityCheck(input, output, width, height, numRows, stride, row);
+ inputOffset += startOffset;
+ outputOffset += startOffset;
+
+ Span preds;
+ int predsOffset;
+
+ if (inverse)
+ {
+ preds = output;
+ predsOffset = outputOffset;
+ }
+ else
+ {
+ preds = input;
+ predsOffset = inputOffset;
+ }
+
+
+ if (row == 0)
+ {
+ // leftmost pixel is the same as Input for topmost scanline
+ output[0] = input[0];
+ PredictLine(
+ input, inputOffset + 1,
+ preds, predsOffset,
+ output, outputOffset + 1,
+ width - 1, inverse);
+
+ row = 1;
+ predsOffset += stride;
+ inputOffset += stride;
+ outputOffset += stride;
+ }
+
+ // filter line by line
+ while (row < lastRow)
+ {
+ PredictLine(
+ input, inputOffset,
+ preds, predsOffset - stride,
+ output, 0,
+ 1, inverse);
+ PredictLine(
+ input, inputOffset,
+ preds, predsOffset,
+ output,outputOffset + 1,
+ width - 1, inverse);
+
+ row++;
+ predsOffset += stride;
+ inputOffset += stride;
+ outputOffset += stride;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs
new file mode 100644
index 0000000000..fcecadfb09
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Filters
+{
+ // TODO: check if this is a filter or just a placeholder from the C implementation details
+ class WebPFilterNone : WebPFilterBase
+ {
+ public override void Unfilter(Span prevLine, int? prevLineOffset, Span input, int inputOffset, Span output, int outputOffset, int width)
+ { }
+
+ public override void Filter(Span input, int inputOffset, int width, int height, int stride, Span output, int outputOffset)
+ { }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs
new file mode 100644
index 0000000000..59ab12caf3
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs
@@ -0,0 +1,84 @@
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Filters
+{
+ class WebPFilterVertical : WebPFilterBase
+ {
+ public override void Unfilter(Span prevLine, int? prevLineOffsetNullable, Span input, int inputOffset, Span output, int outputOffset, int width)
+ {
+ if (prevLineOffsetNullable is int prevLineOffset)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ output[outputOffset + i] = (byte)(prevLine[prevLineOffset + i] + input[inputOffset + i]);
+ }
+ }
+ else
+ {
+ this.UnfilterHorizontalOrVerticalCore(0, input, inputOffset, output, outputOffset, width);
+ }
+ }
+
+ public override void Filter(
+ Span input, int inputOffset,
+ int width, int height, int stride,
+ Span output, int outputOffset)
+ {
+ int row = 0;
+ bool inverse = false;
+
+ // TODO: DoVerticalFilter_C with parameters after stride and after height set to 0
+ int startOffset = row * stride;
+ int lastRow = row + height;
+ SanityCheck(input, output, width, height, height, stride, row);
+ inputOffset += startOffset;
+ outputOffset += startOffset;
+ Span preds;
+ int predsOffset;
+
+ if (inverse)
+ {
+ preds = output;
+ predsOffset = outputOffset;
+ }
+ else
+ {
+ preds = input;
+ predsOffset = inputOffset;
+ }
+
+ if (row == 0)
+ {
+ // very first top-left pixel is copied.
+ output[0] = input[0];
+ // rest of top scan-line is left-predicted:
+ PredictLine(
+ input, inputOffset + 1,
+ preds, predsOffset,
+ output, outputOffset + 1,
+ width - 1, inverse);
+ row = 1;
+ inputOffset += stride;
+ outputOffset += stride;
+ }
+ else
+ {
+ predsOffset -= stride;
+ }
+
+ // filter line-by-line
+ while (row < lastRow)
+ {
+ PredictLine(
+ input, inputOffset,
+ preds, predsOffset,
+ output, outputOffset,
+ width, inverse);
+ row++;
+ predsOffset += stride;
+ inputOffset += stride;
+ outputOffset += stride;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/WebP/WebPFilterType.cs b/src/ImageSharp/Formats/WebP/WebPFilterType.cs
deleted file mode 100644
index f373da7275..0000000000
--- a/src/ImageSharp/Formats/WebP/WebPFilterType.cs
+++ /dev/null
@@ -1,371 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Diagnostics;
-
-namespace SixLabors.ImageSharp.Formats.WebP
-{
- // TODO from dsp.h
- // public enum WebPFilterType
- // {
- // None = 0,
- // Horizontal,
- // Vertical,
- // Gradient,
- // Last = Gradient + 1, // end marker
- // Best, // meta types
- // Fast
- // }
-
- abstract class WebPFilterBase
- {
- ///
- ///
- ///
- /// nullable as prevLine is nullable in the original but Span'T can't be null.
- ///
- ///
- ///
- ///
- ///
- public abstract void Unfilter(
- Span prevLine,
- int? prevLineOffset,
- Span preds,
- int predsOffset,
- Span currentLine,
- int currentLineOffset,
- int width);
-
- public abstract void Filter(
- Span input, int inputOffset,
- int width, int height, int stride,
- Span output, int outputOffset);
-
- protected static void SanityCheck(
- Span input, Span output, int width, int numRows, int height, int stride, int row)
- {
- Debug.Assert(input != null);
- Debug.Assert(output != null);
- Debug.Assert(width > 0);
- Debug.Assert(height > 0);
- Debug.Assert(stride > width);
- Debug.Assert(row >= 0);
- Debug.Assert(height > 0);
- Debug.Assert(row + numRows <= height);
- }
-
- protected static void PredictLine(
- Span src, int srcOffset,
- Span pred, int predOffset,
- Span dst, int dstOffset,
- int length, bool inverse)
- {
- if (inverse)
- {
- for (int i = 0; i < length; i++)
- {
- dst[i] = (byte)(src[i] + pred[i]);
- }
- }
- else
- {
- for (int i = 0; i < length; i++)
- {
- dst[i] = (byte)(src[i] - pred[i]);
- }
- }
- }
-
- protected void UnfilterHorizontalOrVerticalCore(
- byte pred,
- Span input, int inputOffset,
- Span output, int outputOffset,
- int width)
- {
- for (int i = 0; i < width; i++)
- {
- output[outputOffset + i] = (byte)(pred + input[inputOffset + i]);
- pred = output[i];
- }
- }
- }
-
- // TODO: check if this is a filter or just a placeholder from the C implementation details
- class WebPFilterNone : WebPFilterBase
- {
- public override void Unfilter(Span prevLine, int? prevLineOffset, Span input, int inputOffset, Span output, int outputOffset, int width)
- { }
-
- public override void Filter(Span input, int inputOffset, int width, int height, int stride, Span output, int outputOffset)
- { }
- }
-
- class WebPFilterHorizontal : WebPFilterBase
- {
- public override void Unfilter(
- Span prevLine, int? prevLineOffsetNullable,
- Span input, int inputOffset,
- Span output, int outputOffset,
- int width)
- {
- byte pred = prevLineOffsetNullable is int prevLineOffset
- ? prevLine[prevLineOffset]
- : (byte)0;
-
- this.UnfilterHorizontalOrVerticalCore(
- pred,
- input, inputOffset,
- output, outputOffset,
- width);
- }
-
- public override void Filter(
- Span input, int inputOffset,
- int width, int height, int stride,
- Span output, int outputOffset)
- {
- int numRows = height;
- int row = 0;
-
- const bool inverse = false;
-
- int startOffset = row * stride;
- int lastRow = row + height;
- SanityCheck(input, output, width, height, numRows, stride, row);
- inputOffset += startOffset;
- outputOffset += startOffset;
-
- Span preds;
- int predsOffset;
-
- if (inverse)
- {
- preds = output;
- predsOffset = outputOffset;
- }
- else
- {
- preds = input;
- predsOffset = inputOffset;
- }
-
-
- if (row == 0)
- {
- // leftmost pixel is the same as Input for topmost scanline
- output[0] = input[0];
- PredictLine(
- input, inputOffset + 1,
- preds, predsOffset,
- output, outputOffset + 1,
- width - 1, inverse);
-
- row = 1;
- predsOffset += stride;
- inputOffset += stride;
- outputOffset += stride;
- }
-
- // filter line by line
- while (row < lastRow)
- {
- PredictLine(
- input, inputOffset,
- preds, predsOffset - stride,
- output, 0,
- 1, inverse);
- PredictLine(
- input, inputOffset,
- preds, predsOffset,
- output,outputOffset + 1,
- width - 1, inverse);
-
- row++;
- predsOffset += stride;
- inputOffset += stride;
- outputOffset += stride;
- }
- }
- }
-
- class WebPFilterVertical : WebPFilterBase
- {
- public override void Unfilter(Span prevLine, int? prevLineOffsetNullable, Span input, int inputOffset, Span output, int outputOffset, int width)
- {
- if (prevLineOffsetNullable is int prevLineOffset)
- {
- for (int i = 0; i < width; i++)
- {
- output[outputOffset + i] = (byte)(prevLine[prevLineOffset + i] + input[inputOffset + i]);
- }
- }
- else
- {
- this.UnfilterHorizontalOrVerticalCore(0, input, inputOffset, output, outputOffset, width);
- }
- }
-
- public override void Filter(
- Span input, int inputOffset,
- int width, int height, int stride,
- Span output, int outputOffset)
- {
- int row = 0;
- bool inverse = false;
-
- // TODO: DoVerticalFilter_C with parameters after stride and after height set to 0
- int startOffset = row * stride;
- int lastRow = row + height;
- SanityCheck(input, output, width, height, height, stride, row);
- inputOffset += startOffset;
- outputOffset += startOffset;
- Span preds;
- int predsOffset;
-
- if (inverse)
- {
- preds = output;
- predsOffset = outputOffset;
- }
- else
- {
- preds = input;
- predsOffset = inputOffset;
- }
-
- if (row == 0)
- {
- // very first top-left pixel is copied.
- output[0] = input[0];
- // rest of top scan-line is left-predicted:
- PredictLine(
- input, inputOffset + 1,
- preds, predsOffset,
- output, outputOffset + 1,
- width - 1, inverse);
- row = 1;
- inputOffset += stride;
- outputOffset += stride;
- }
- else
- {
- predsOffset -= stride;
- }
-
- // filter line-by-line
- while (row < lastRow)
- {
- PredictLine(
- input, inputOffset,
- preds, predsOffset,
- output, outputOffset,
- width, inverse);
- row++;
- predsOffset += stride;
- inputOffset += stride;
- outputOffset += stride;
- }
- }
- }
-
- class WebPFilterGradient : WebPFilterBase
- {
-
- public override void Unfilter(
- Span prevLine,
- int? prevLineOffsetNullable,
- Span input,
- int inputOffset,
- Span output,
- int outputOffset,
- int width)
- {
- if (prevLineOffsetNullable is int prevLineOffset)
- {
- byte top = prevLine[prevLineOffset];
- byte topLeft = top;
- byte left = top;
- for (int i = 0; i < width; i++)
- {
- top = prevLine[prevLineOffset + i]; // need to read this first in case prev==out
- left = (byte)(input[inputOffset + i] + GradientPredictor(left, top, topLeft));
- topLeft = top;
- output[outputOffset + i] = left;
- }
- }
- else
- {
- this.UnfilterHorizontalOrVerticalCore(0, input, inputOffset, output, outputOffset, width);
- }
- }
-
- public override void Filter(
- Span input, int inputOffset,
- int width, int height, int stride,
- Span output, int outputOffset)
- {
- // calling (input, width, height, stride, 0, height, 0, output
- int row = 0;
- int numRows = height;
- bool inverse = false;
-
- int startOffset = row * stride;
- int lastRow = row + numRows;
- SanityCheck(input, output, width, numRows, height, stride, row);
- inputOffset += startOffset;
- outputOffset += startOffset;
- Span preds;
- int predsOffset;
- if (inverse)
- {
- preds = output;
- predsOffset = outputOffset;
- }
- else
- {
- preds = input;
- predsOffset = inputOffset;
- }
-
- if (row == 0)
- {
- output[outputOffset] = input[inputOffset];
- PredictLine(
- input, inputOffset+1,
- preds, predsOffset,
- output, outputOffset+1,
- width-1,
- inverse);
- }
-
- while (row < lastRow)
- {
- PredictLine(
- input, inputOffset,
- preds, predsOffset-stride,
- output, outputOffset,
- 1, inverse);
-
- for (int w = 1; w < width; w++)
- {
- int pred = GradientPredictor(preds[w - 1], preds[w - stride], preds[w - stride - 1]);
- int signedPred = inverse ? pred : -pred;
- output[outputOffset + w] = (byte)(input[inputOffset + w] + signedPred);
- }
-
- row++;
- predsOffset += stride;
- inputOffset += stride;
- outputOffset += stride;
- }
- }
-
- private static int GradientPredictor(byte a, byte b, byte c)
- {
- int g = a + b + c;
- return (g & ~0xff) == 0 ? g : (g < 0) ? 0 : 255;
- }
- }
-}
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index da84079cf7..89f58f633e 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
+using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;