From 72f3499146ff223560220f68a23d55c01889fed5 Mon Sep 17 00:00:00 2001 From: Peter Amrehn Date: Thu, 23 Jan 2020 20:51:37 +0100 Subject: [PATCH] WebP: split Filter classes to different files --- .../Formats/WebP/Filters/WebPFilterBase.cs | 94 +++++ .../WebP/Filters/WebPFilterGradient.cs | 103 +++++ .../WebP/Filters/WebPFilterHorizontal.cs | 92 +++++ .../Formats/WebP/Filters/WebPFilterNone.cs | 14 + .../WebP/Filters/WebPFilterVertical.cs | 84 ++++ src/ImageSharp/Formats/WebP/WebPFilterType.cs | 371 ------------------ .../Formats/WebP/WebPLossyDecoder.cs | 1 + 7 files changed, 388 insertions(+), 371 deletions(-) create mode 100644 src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs create mode 100644 src/ImageSharp/Formats/WebP/Filters/WebPFilterGradient.cs create mode 100644 src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs create mode 100644 src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs create mode 100644 src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs delete mode 100644 src/ImageSharp/Formats/WebP/WebPFilterType.cs 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;