diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
index cf92e38762..8a276cebc3 100644
--- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
@@ -2,45 +2,26 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
+using System.Collections.Generic;
-using SixLabors.ImageSharp.Formats.WebP.Filters;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP
{
- internal class AlphaDecoder
+ ///
+ /// Implements decoding for lossy alpha chunks which may be compressed.
+ ///
+ internal class AlphaDecoder : IDisposable
{
- public int Width { get; }
-
- public int Height { get; }
-
- public WebPFilterBase Filter { get; }
-
- public WebPFilterType FilterType { get; }
-
- public int CropTop { get; }
-
- public int LastRow { get; set; }
-
- public Vp8LDecoder Vp8LDec { get; }
-
- public byte[] Alpha { get; }
-
- private int PreProcessing { get; }
-
- private bool Compressed { get; }
-
- private byte[] Data { get; }
-
- private WebPLosslessDecoder LosslessDecoder { get; }
-
///
- /// Although Alpha Channel requires only 1 byte per pixel,
- /// sometimes Vp8LDecoder may need to allocate
- /// 4 bytes per pixel internally during decode.
+ /// Initializes a new instance of the class.
///
- public bool Use8BDecode { get; set; }
-
+ /// The width of the image.
+ /// The height of the image.
+ /// The (maybe compressed) alpha data.
+ /// The first byte of the alpha image stream contains information on ow to decode the stream.
+ /// Used for allocating memory during decoding.
public AlphaDecoder(int width, int height, byte[] data, byte alphaChunkHeader, MemoryAllocator memoryAllocator)
{
this.Width = width;
@@ -64,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {filter} found");
}
- this.FilterType = (WebPFilterType)filter;
+ this.AlphaFilterType = (WebPAlphaFilterType)filter;
// These INFORMATIVE bits are used to signal the pre-processing that has been performed during compression.
// The decoder can use this information to e.g. dither the values or smooth the gradients prior to display.
@@ -73,8 +54,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.Vp8LDec = new Vp8LDecoder(width, height, memoryAllocator);
- // TODO: use memory allocator
- this.Alpha = new byte[width * height];
+ this.Alpha = memoryAllocator.Allocate(width * height);
if (this.Compressed)
{
@@ -84,8 +64,74 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- private int PrevLineOffset { get; set; }
+ ///
+ /// Gets the the width of the image.
+ ///
+ public int Width { get; }
+
+ ///
+ /// Gets the height of the image.
+ ///
+ public int Height { get; }
+
+ ///
+ /// Gets the used filter type.
+ ///
+ public WebPAlphaFilterType AlphaFilterType { get; }
+
+ ///
+ /// Gets or sets the last decoded row.
+ ///
+ public int LastRow { get; set; }
+
+ ///
+ /// Gets or sets the row before the last decoded row.
+ ///
+ public int PrevRow { get; set; }
+
+ ///
+ /// Gets information for decoding Vp8L compressed alpha data.
+ ///
+ public Vp8LDecoder Vp8LDec { get; }
+
+ ///
+ /// Gets the decoded alpha data.
+ ///
+ public IMemoryOwner Alpha { get; }
+
+ public int CropTop { get; }
+
+ ///
+ /// Gets a value indicating whether pre-processing was used during compression.
+ /// 0: no pre-processing, 1: level reduction.
+ ///
+ private int PreProcessing { get; }
+
+ ///
+ /// Gets a value indicating whether the alpha channel uses compression.
+ ///
+ private bool Compressed { get; }
+
+ ///
+ /// Gets the (maybe compressed) alpha data.
+ ///
+ private byte[] Data { get; }
+
+ ///
+ /// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed.
+ ///
+ private WebPLosslessDecoder LosslessDecoder { get; }
+
+ ///
+ /// Gets or sets a value indicating whether the decoding needs 1 byte per pixel for decoding.
+ /// Although Alpha Channel requires only 1 byte per pixel, sometimes Vp8LDecoder may need to allocate
+ /// 4 bytes per pixel internally during decode.
+ ///
+ public bool Use8BDecode { get; set; }
+ ///
+ /// Decodes and filters the maybe compressed alpha data.
+ ///
public void Decode()
{
if (this.Compressed is false)
@@ -95,26 +141,27 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk");
}
- if (this.FilterType == WebPFilterType.None)
+ Span alphaSpan = this.Alpha.Memory.Span;
+ if (this.AlphaFilterType == WebPAlphaFilterType.None)
{
- this.Data.AsSpan(0, this.Width * this.Height).CopyTo(this.Alpha);
+ this.Data.AsSpan(0, this.Width * this.Height).CopyTo(alphaSpan);
return;
}
Span deltas = this.Data.AsSpan();
- Span dst = this.Alpha.AsSpan();
+ Span dst = alphaSpan;
Span prev = null;
for (int y = 0; y < this.Height; ++y)
{
- switch (this.FilterType)
+ switch (this.AlphaFilterType)
{
- case WebPFilterType.Horizontal:
+ case WebPAlphaFilterType.Horizontal:
HorizontalUnfilter(prev, deltas, dst, this.Width);
break;
- case WebPFilterType.Vertical:
+ case WebPAlphaFilterType.Vertical:
VerticalUnfilter(prev, deltas, dst, this.Width);
break;
- case WebPFilterType.Gradient:
+ case WebPAlphaFilterType.Gradient:
GradientUnfilter(prev, deltas, dst, this.Width);
break;
}
@@ -130,6 +177,44 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ ///
+ /// Applies filtering to a set of rows.
+ ///
+ /// The first row index to start filtering.
+ /// The last row index for filtering.
+ /// The destination to store the filtered data.
+ /// The stride to use.
+ public void AlphaApplyFilter(int firstRow, int lastRow, Span dst, int stride)
+ {
+ if (this.AlphaFilterType is WebPAlphaFilterType.None)
+ {
+ return;
+ }
+
+ Span alphaSpan = this.Alpha.Memory.Span;
+ Span prev = this.PrevRow == 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow);
+ for (int y = firstRow; y < lastRow; ++y)
+ {
+ switch (this.AlphaFilterType)
+ {
+ case WebPAlphaFilterType.Horizontal:
+ HorizontalUnfilter(prev, dst, dst, this.Width);
+ break;
+ case WebPAlphaFilterType.Vertical:
+ VerticalUnfilter(prev, dst, dst, this.Width);
+ break;
+ case WebPAlphaFilterType.Gradient:
+ GradientUnfilter(prev, dst, dst, this.Width);
+ break;
+ }
+
+ prev = dst;
+ dst = dst.Slice(stride);
+ }
+
+ this.PrevRow = lastRow - 1;
+ }
+
private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width)
{
byte pred = (byte)(prev == null ? 0 : prev[0]);
@@ -177,42 +262,48 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ private static bool Is8bOptimizable(Vp8LMetadata hdr)
+ {
+ if (hdr.ColorCacheSize > 0)
+ {
+ return false;
+ }
+
+ // When the Huffman tree contains only one symbol, we can skip the
+ // call to ReadSymbol() for red/blue/alpha channels.
+ for (int i = 0; i < hdr.NumHTreeGroups; ++i)
+ {
+ List htrees = hdr.HTreeGroups[i].HTrees;
+ if (htrees[HuffIndex.Red][0].Value > 0)
+ {
+ return false;
+ }
+
+ if (htrees[HuffIndex.Blue][0].Value > 0)
+ {
+ return false;
+ }
+
+ if (htrees[HuffIndex.Alpha][0].Value > 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static int GradientPredictor(byte a, byte b, byte c)
{
int g = a + b - c;
return ((g & ~0xff) is 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
}
- // Taken from vp8l_dec.c AlphaApplyFilter
- public void AlphaApplyFilter(
- int firstRow,
- int lastRow,
- Span prevLine,
- Span output,
- int outputOffset,
- int stride)
+ ///
+ public void Dispose()
{
- if (this.Filter is WebPFilterNone)
- {
- return;
- }
-
- int prevLineOffset = this.PrevLineOffset;
-
- for (int y = firstRow; y < lastRow; y++)
- {
- this.Filter
- .Unfilter(
- prevLine,
- prevLineOffset,
- output,
- outputOffset,
- output,
- outputOffset,
- stride);
- prevLineOffset = outputOffset;
- outputOffset += stride;
- }
+ this.Vp8LDec?.Dispose();
+ this.Alpha?.Dispose();
}
}
}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs
deleted file mode 100644
index 74b69f43ab..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterBase.cs
+++ /dev/null
@@ -1,104 +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.Filters
-{
- // TODO from dsp.h
- // public enum WebPFilterType
- // {
- // None = 0,
- // Horizontal,
- // Vertical,
- // Gradient,
- // Last = Gradient + 1, // end marker
- // Best, // meta types
- // Fast
- // }
-
- internal 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
deleted file mode 100644
index 27bca57706..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterGradient.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-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;
- }
- }
-}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs
deleted file mode 100644
index fdec37e5de..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterHorizontal.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-
-namespace SixLabors.ImageSharp.Formats.WebP.Filters
-{
- internal 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 is 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;
- }
- }
- }
-}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs
deleted file mode 100644
index 04dfafe241..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterNone.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-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/WebPFilterType.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterType.cs
deleted file mode 100644
index 4b79ea5f54..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterType.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-namespace SixLabors.ImageSharp.Formats.WebP.Filters
-{
- internal enum WebPFilterType
- {
- None = 0,
-
- Horizontal = 1,
-
- Vertical = 2,
-
- Gradient = 3,
- }
-}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs
deleted file mode 100644
index 04eb2a5874..0000000000
--- a/src/ImageSharp/Formats/WebP/Filters/WebPFilterVertical.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-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;
- }
- }
- }
-}
diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs
index 21835c3d45..e004d86f25 100644
--- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs
@@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private int bits;
///
- /// Max packed-read position on buffer.
+ /// Max packed-read position of the buffer.
///
private uint bufferMax;
@@ -54,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// The input stream to read from.
/// The raw image data size in bytes.
/// Used for allocating memory during reading data from the stream.
+ /// The partition length.
/// Start index in the data array. Defaults to 0.
public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0)
{
@@ -63,6 +64,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.InitBitreader(partitionLength, startPos);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The raw encoded image data.
+ /// The partition length.
+ /// Start index in the data array. Defaults to 0.
public Vp8BitReader(byte[] imageData, uint partitionLength, int startPos = 0)
{
this.Data = imageData;
diff --git a/src/ImageSharp/Formats/WebP/Vp8Io.cs b/src/ImageSharp/Formats/WebP/Vp8Io.cs
index 82a8cbba8a..36b561847b 100644
--- a/src/ImageSharp/Formats/WebP/Vp8Io.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8Io.cs
@@ -37,17 +37,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int MbH { get; set; }
///
- /// Rows to copy (in YUV format).
+ /// Gets or sets the luma component.
///
public Span Y { get; set; }
///
- /// Rows to copy (in YUV format).
+ /// Gets or sets the U chroma component.
///
public Span U { get; set; }
///
- /// Rows to copy (in YUV format).
+ /// Gets or sets the V chroma component.
///
public Span V { get; set; }
@@ -76,10 +76,5 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int ScaledWidth { get; set; }
public int ScaledHeight { get; set; }
-
- ///
- /// User data
- ///
- private object Opaque { get; set; }
}
}
diff --git a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
index f4114f5be9..38c5ffb64d 100644
--- a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
@@ -8,7 +8,7 @@ using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP
{
///
- /// A bit reader for VP8L streams.
+ /// A bit reader for reading lossless webp streams.
///
internal class Vp8LBitReader : BitReaderBase
{
@@ -20,12 +20,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// Number of bits prefetched.
///
- private const int Vp8LLbits = 64;
+ private const int Lbits = 64;
///
/// Minimum number of bytes ready after VP8LFillBitWindow.
///
- private const int Vp8LWbits = 32;
+ private const int Wbits = 32;
private readonly uint[] bitMask =
{
@@ -58,11 +58,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
private int bitPos;
- ///
- /// True if a bit was read past the end of buffer.
- ///
- public bool Eos;
-
///
/// Initializes a new instance of the class.
///
@@ -117,6 +112,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.pos = length;
}
+ ///
+ /// Gets or sets a value indicating whether a bit was read past the end of buffer.
+ ///
+ public bool Eos { get; set; }
+
///
/// Reads a unsigned short value from the buffer. The bits of each byte are read in least-significant-bit-first order.
///
@@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// The pre-fetched bits.
public ulong PrefetchBits()
{
- return this.value >> (this.bitPos & (Vp8LLbits - 1));
+ return this.value >> (this.bitPos & (Lbits - 1));
}
///
@@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public void FillBitWindow()
{
- if (this.bitPos >= Vp8LWbits)
+ if (this.bitPos >= Wbits)
{
this.DoFillBitWindow();
}
@@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// True, if end of buffer was reached.
public bool IsEndOfStream()
{
- return this.Eos || ((this.pos == this.len) && (this.bitPos > Vp8LLbits));
+ return this.Eos || ((this.pos == this.len) && (this.bitPos > Lbits));
}
private void DoFillBitWindow()
@@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (this.bitPos >= 8 && this.pos < this.len)
{
this.value >>= 8;
- this.value |= (ulong)this.Data[this.pos] << (Vp8LLbits - 8);
+ this.value |= (ulong)this.Data[this.pos] << (Lbits - 8);
++this.pos;
this.bitPos -= 8;
}
diff --git a/src/ImageSharp/Formats/WebP/Vp8LDecoder.cs b/src/ImageSharp/Formats/WebP/Vp8LDecoder.cs
index 968d98b9d4..69b3c3ed74 100644
--- a/src/ImageSharp/Formats/WebP/Vp8LDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8LDecoder.cs
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public IMemoryOwner Pixels { get; }
- ///
+ ///
public void Dispose()
{
this.Pixels.Dispose();
diff --git a/src/ImageSharp/Formats/WebP/WebPAlphaFilterType.cs b/src/ImageSharp/Formats/WebP/WebPAlphaFilterType.cs
new file mode 100644
index 0000000000..dfdc8281cf
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/WebPAlphaFilterType.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.WebP
+{
+ ///
+ /// Enum for the different alpha filter types.
+ ///
+ internal enum WebPAlphaFilterType
+ {
+ ///
+ /// No filtering.
+ ///
+ None = 0,
+
+ ///
+ /// Horizontal filter.
+ ///
+ Horizontal = 1,
+
+ ///
+ /// Vertical filter.
+ ///
+ Vertical = 2,
+
+ ///
+ /// Gradient filter.
+ ///
+ Gradient = 3,
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
index b64cc0a173..6ca54690fc 100644
--- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
@@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.Formats.WebP.Filters;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -23,6 +22,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
internal sealed class WebPLosslessDecoder
{
+ ///
+ /// A bit reader for reading lossless webp streams.
+ ///
private readonly Vp8LBitReader bitReader;
private static readonly int BitsSpecialMarker = 0x100;
@@ -751,12 +753,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
int dist = this.PlaneCodeToDistance(width, distCode);
if (pos >= dist && end - pos >= length)
{
- //CopyBlock8b(data + pos, dist, length);
+ data.Slice(pos - dist, length).CopyTo(data.Slice(pos));
}
else
{
- // TODO: error?
- break;
+ WebPThrowHelper.ThrowImageFormatException("error while decoding alpha data");
}
pos += length;
@@ -792,14 +793,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
// For vertical and gradient filtering, we need to decode the part above the
// cropTop row, in order to have the correct spatial predictors.
- int topRow = (dec.FilterType is WebPFilterType.None || dec.FilterType is WebPFilterType.Horizontal)
+ int topRow = (dec.AlphaFilterType is WebPAlphaFilterType.None || dec.AlphaFilterType is WebPAlphaFilterType.Horizontal)
? dec.CropTop
: dec.LastRow;
int firstRow = (dec.LastRow < topRow) ? topRow : dec.LastRow;
if (lastRow > firstRow)
{
// Special method for paletted alpha data. We only process the cropped area.
- Span output = dec.Alpha.AsSpan();
+ Span output = dec.Alpha.Memory.Span;
Span pixelData = dec.Vp8LDec.Pixels.Memory.Span;
Span pixelDataAsBytes = MemoryMarshal.Cast(pixelData);
Span dst = output.Slice(dec.Width * firstRow);
@@ -808,7 +809,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO: check if any and the correct transform is present
Vp8LTransform transform = dec.Vp8LDec.Transforms[0];
this.ColorIndexInverseTransformAlpha(transform, firstRow, lastRow, input, dst);
- //dec.AlphaApplyFilter(firstRow, lastRow, dst, width);
+ dec.AlphaApplyFilter(firstRow, lastRow, dst, dec.Width);
}
dec.LastRow = lastRow;
@@ -867,37 +868,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- private static bool Is8bOptimizable(Vp8LMetadata hdr)
- {
- if (hdr.ColorCacheSize > 0)
- {
- return false;
- }
-
- // When the Huffman tree contains only one symbol, we can skip the
- // call to ReadSymbol() for red/blue/alpha channels.
- for (int i = 0; i < hdr.NumHTreeGroups; ++i)
- {
- List htrees = hdr.HTreeGroups[i].HTrees;
- if (htrees[HuffIndex.Red][0].Value > 0)
- {
- return false;
- }
-
- if (htrees[HuffIndex.Blue][0].Value > 0)
- {
- return false;
- }
-
- if (htrees[HuffIndex.Alpha][0].Value > 0)
- {
- return false;
- }
- }
-
- return true;
- }
-
private void UpdateDecoder(Vp8LDecoder decoder, int width, int height)
{
int numBits = decoder.Metadata.HuffmanSubSampleBits;
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index 9c906a8281..2a2effd921 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Linq;
using System.Runtime.InteropServices;
@@ -13,10 +14,21 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
internal sealed class WebPLossyDecoder
{
+ ///
+ /// A bit reader for reading lossy webp streams.
+ ///
private readonly Vp8BitReader bitReader;
+ ///
+ /// Used for allocating memory during processing operations.
+ ///
private readonly MemoryAllocator memoryAllocator;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Bitreader to read from the stream.
+ /// Used for allocating memory during processing operations.
public WebPLossyDecoder(Vp8BitReader bitReader, MemoryAllocator memoryAllocator)
{
this.memoryAllocator = memoryAllocator;
@@ -77,11 +89,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels, byte[] alpha = null)
+ private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels, IMemoryOwner alpha = null)
where TPixel : struct, IPixel
{
TPixel color = default;
- bool hasAlpha = alpha != null;
+ bool hasAlpha = false;
+ Span alphaSpan = null;
+ if (alpha != null)
+ {
+ hasAlpha = true;
+ alphaSpan = alpha.Memory.Span;
+ }
+
for (int y = 0; y < height; y++)
{
Span pixelRow = pixels.GetRowSpan(y);
@@ -96,7 +115,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO: use bulk conversion here.
if (hasAlpha)
{
- byte a = alpha[offset];
+ byte a = alphaSpan[offset];
color.FromBgra32(new Bgra32(r, g, b, a));
}
else
@@ -781,26 +800,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- private Vp8Profile DecodeProfile(int version)
- {
- switch (version)
- {
- case 0:
- return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bicubic, LoopFilter = LoopFilter.Complex };
- case 1:
- return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bilinear, LoopFilter = LoopFilter.Simple };
- case 2:
- return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bilinear, LoopFilter = LoopFilter.None };
- case 3:
- return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.None, LoopFilter = LoopFilter.None };
- default:
- // Reserved for future use in Spec.
- // https://tools.ietf.org/html/rfc6386#page-30
- WebPThrowHelper.ThrowNotSupportedException($"unsupported VP8 version {version} found");
- return new Vp8Profile();
- }
- }
-
private void DecodeMacroBlock(Vp8Decoder dec, Vp8BitReader bitreader)
{
Vp8MacroBlock left = dec.LeftMacroBlock;
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
index 5041f35ae7..80362ec72a 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
@@ -175,6 +175,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
[WithFile(Lossy.AlphaNoCompressionHorizontalFilter, PixelTypes.Rgba32)]
[WithFile(Lossy.AlphaNoCompressionVerticalFilter, PixelTypes.Rgba32)]
[WithFile(Lossy.AlphaNoCompressionGradientFilter, PixelTypes.Rgba32)]
+ [WithFile(Lossy.AlphaCompressedHorizontalFilter, PixelTypes.Rgba32)]
+ [WithFile(Lossy.AlphaCompressedVerticalFilter, PixelTypes.Rgba32)]
+ [WithFile(Lossy.AlphaCompressedGradientFilter, PixelTypes.Rgba32)]
public void WebpDecoder_CanDecode_Lossy_WithAlpha(TestImageProvider provider)
where TPixel : struct, IPixel
{