mirror of https://github.com/SixLabors/ImageSharp
7 changed files with 388 additions and 371 deletions
@ -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 |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// </summary>
|
||||
|
/// <param name="prevLine"></param>
|
||||
|
/// <param name="prevLineOffset">nullable as prevLine is nullable in the original but Span'T can't be null.</param>
|
||||
|
/// <param name="preds"></param>
|
||||
|
/// <param name="predsOffset"></param>
|
||||
|
/// <param name="currentLine"></param>
|
||||
|
/// <param name="currentLineOffset"></param>
|
||||
|
/// <param name="width"></param>
|
||||
|
public abstract void Unfilter( |
||||
|
Span<byte> prevLine, |
||||
|
int? prevLineOffset, |
||||
|
Span<byte> preds, |
||||
|
int predsOffset, |
||||
|
Span<byte> currentLine, |
||||
|
int currentLineOffset, |
||||
|
int width); |
||||
|
|
||||
|
public abstract void Filter( |
||||
|
Span<byte> input, int inputOffset, |
||||
|
int width, int height, int stride, |
||||
|
Span<byte> output, int outputOffset); |
||||
|
|
||||
|
protected static void SanityCheck( |
||||
|
Span<byte> input, Span<byte> 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<byte> src, int srcOffset, |
||||
|
Span<byte> pred, int predOffset, |
||||
|
Span<byte> 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<byte> input, int inputOffset, |
||||
|
Span<byte> output, int outputOffset, |
||||
|
int width) |
||||
|
{ |
||||
|
for (int i = 0; i < width; i++) |
||||
|
{ |
||||
|
output[outputOffset + i] = (byte)(pred + input[inputOffset + i]); |
||||
|
pred = output[i]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.WebP.Filters |
||||
|
{ |
||||
|
class WebPFilterGradient : WebPFilterBase |
||||
|
{ |
||||
|
|
||||
|
public override void Unfilter( |
||||
|
Span<byte> prevLine, |
||||
|
int? prevLineOffsetNullable, |
||||
|
Span<byte> input, |
||||
|
int inputOffset, |
||||
|
Span<byte> 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<byte> input, int inputOffset, |
||||
|
int width, int height, int stride, |
||||
|
Span<byte> 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<byte> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.WebP.Filters |
||||
|
{ |
||||
|
class WebPFilterHorizontal : WebPFilterBase |
||||
|
{ |
||||
|
public override void Unfilter( |
||||
|
Span<byte> prevLine, int? prevLineOffsetNullable, |
||||
|
Span<byte> input, int inputOffset, |
||||
|
Span<byte> 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<byte> input, int inputOffset, |
||||
|
int width, int height, int stride, |
||||
|
Span<byte> 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<byte> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<byte> prevLine, int? prevLineOffset, Span<byte> input, int inputOffset, Span<byte> output, int outputOffset, int width) |
||||
|
{ } |
||||
|
|
||||
|
public override void Filter(Span<byte> input, int inputOffset, int width, int height, int stride, Span<byte> output, int outputOffset) |
||||
|
{ } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.WebP.Filters |
||||
|
{ |
||||
|
class WebPFilterVertical : WebPFilterBase |
||||
|
{ |
||||
|
public override void Unfilter(Span<byte> prevLine, int? prevLineOffsetNullable, Span<byte> input, int inputOffset, Span<byte> 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<byte> input, int inputOffset, |
||||
|
int width, int height, int stride, |
||||
|
Span<byte> 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<byte> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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 |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// </summary>
|
|
||||
/// <param name="prevLine"></param>
|
|
||||
/// <param name="prevLineOffset">nullable as prevLine is nullable in the original but Span'T can't be null.</param>
|
|
||||
/// <param name="preds"></param>
|
|
||||
/// <param name="predsOffset"></param>
|
|
||||
/// <param name="currentLine"></param>
|
|
||||
/// <param name="currentLineOffset"></param>
|
|
||||
/// <param name="width"></param>
|
|
||||
public abstract void Unfilter( |
|
||||
Span<byte> prevLine, |
|
||||
int? prevLineOffset, |
|
||||
Span<byte> preds, |
|
||||
int predsOffset, |
|
||||
Span<byte> currentLine, |
|
||||
int currentLineOffset, |
|
||||
int width); |
|
||||
|
|
||||
public abstract void Filter( |
|
||||
Span<byte> input, int inputOffset, |
|
||||
int width, int height, int stride, |
|
||||
Span<byte> output, int outputOffset); |
|
||||
|
|
||||
protected static void SanityCheck( |
|
||||
Span<byte> input, Span<byte> 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<byte> src, int srcOffset, |
|
||||
Span<byte> pred, int predOffset, |
|
||||
Span<byte> 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<byte> input, int inputOffset, |
|
||||
Span<byte> 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<byte> prevLine, int? prevLineOffset, Span<byte> input, int inputOffset, Span<byte> output, int outputOffset, int width) |
|
||||
{ } |
|
||||
|
|
||||
public override void Filter(Span<byte> input, int inputOffset, int width, int height, int stride, Span<byte> output, int outputOffset) |
|
||||
{ } |
|
||||
} |
|
||||
|
|
||||
class WebPFilterHorizontal : WebPFilterBase |
|
||||
{ |
|
||||
public override void Unfilter( |
|
||||
Span<byte> prevLine, int? prevLineOffsetNullable, |
|
||||
Span<byte> input, int inputOffset, |
|
||||
Span<byte> 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<byte> input, int inputOffset, |
|
||||
int width, int height, int stride, |
|
||||
Span<byte> 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<byte> 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<byte> prevLine, int? prevLineOffsetNullable, Span<byte> input, int inputOffset, Span<byte> 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<byte> input, int inputOffset, |
|
||||
int width, int height, int stride, |
|
||||
Span<byte> 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<byte> 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<byte> prevLine, |
|
||||
int? prevLineOffsetNullable, |
|
||||
Span<byte> input, |
|
||||
int inputOffset, |
|
||||
Span<byte> 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<byte> input, int inputOffset, |
|
||||
int width, int height, int stride, |
|
||||
Span<byte> 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<byte> 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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue