diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
index d2c8d583f1..f6ee5ec16a 100644
--- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs
@@ -4,24 +4,27 @@
using System;
using SixLabors.ImageSharp.Formats.WebP.Filters;
+using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp.Formats.WebP
{
- internal ref struct AlphaDecoder
+ internal class AlphaDecoder
{
public int Width { get; set; }
public int Height { get; set; }
- public int Method { get; set; }
-
public WebPFilterBase Filter { get; set; }
- public int PreProcessing { get; set; }
+ private WebPFilterType FilterType { get; }
+
+ private int PreProcessing { get; }
+
+ private bool Compressed { get; }
- public Vp8LDecoder Vp8LDec { get; set; }
+ private byte[] Data { get; }
- public Vp8Io Io { get; set; }
+ private Vp8LDecoder Vp8LDec { get; set; }
///
/// Although Alpha Channel requires only 1 byte per pixel,
@@ -30,22 +33,57 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public bool Use8BDecode { get; set; }
- // last output row (or null)
- private Span PrevLine { get; set; }
+ public AlphaDecoder(int width, int height, byte[] data)
+ {
+ this.Width = width;
+ this.Height = height;
+ this.Data = data;
+
+ // Compression method: Either 0 (no compression) or 1 (Compressed using the WebP lossless format)
+ int method = data[0] & 0x03;
+ if (method < 0 || method > 1)
+ {
+ WebPThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {method} found");
+ }
+
+ this.Compressed = !(method is 0);
+
+ // The filtering method used. Only values between 0 and 3 are valid.
+ int filter = (data[0] >> 2) & 0x03;
+ if (filter < 0 || filter > 3)
+ {
+ WebPThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {filter} found");
+ }
+
+ this.FilterType = (WebPFilterType)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.
+ // 0: no pre-processing, 1: level reduction
+ this.PreProcessing = (data[0] >> 4) & 0x03;
+ }
private int PrevLineOffset { get; set; }
+ public void Decode(Vp8Decoder dec, Span dst)
+ {
+ if (this.Compressed is false)
+ {
+ this.Data.AsSpan(1, this.Width * this.Height).CopyTo(dst);
+ }
+ }
+
// Taken from vp8l_dec.c AlphaApplyFilter
public void AlphaApplyFilter(
int firstRow,
int lastRow,
+ Span prevLine,
Span output,
int outputOffset,
int stride)
{
if (!(this.Filter is WebPFilterNone))
{
- Span prevLine = this.PrevLine;
int prevLineOffset = this.PrevLineOffset;
for (int y = firstRow; y < lastRow; y++)
@@ -62,8 +100,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
prevLineOffset = outputOffset;
outputOffset += stride;
}
-
- this.PrevLine = prevLine;
}
}
}
diff --git a/src/ImageSharp/Formats/WebP/Filters/WebPFilterType.cs b/src/ImageSharp/Formats/WebP/Filters/WebPFilterType.cs
new file mode 100644
index 0000000000..4b79ea5f54
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Filters/WebPFilterType.cs
@@ -0,0 +1,16 @@
+// 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/LoopFilter.cs b/src/ImageSharp/Formats/WebP/LoopFilter.cs
index 2f67661080..e48d89cea2 100644
--- a/src/ImageSharp/Formats/WebP/LoopFilter.cs
+++ b/src/ImageSharp/Formats/WebP/LoopFilter.cs
@@ -6,7 +6,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
internal enum LoopFilter
{
None = 0,
+
Simple = 1,
+
Complex = 2,
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index 6e9631729d..f55bc0ed03 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -168,6 +168,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// - A 'VP8X' chunk with information about features used in the file.
/// - An optional 'ICCP' chunk with color profile.
/// - An optional 'ANIM' chunk with animation control data.
+ /// - An optional 'ALPH' chunk with alpha channel data.
/// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow.
///
/// Information about this webp image.
@@ -240,19 +241,25 @@ namespace SixLabors.ImageSharp.Formats.WebP
};
}
+ byte[] alphaData = null;
if (isAlphaPresent)
{
chunkType = this.ReadChunkType();
- uint alphaChunkSize = this.ReadChunkSize();
+ if (chunkType != WebPChunkType.Alpha)
+ {
+ WebPThrowHelper.ThrowImageFormatException($"unexpected chunk type {chunkType}, expected ALPH chunk is missing");
+ }
- // ALPH chunks will be skipped for now.
- this.currentStream.Skip((int)alphaChunkSize);
+ uint alphaChunkSize = this.ReadChunkSize();
+ alphaData = new byte[alphaChunkSize];
+ this.currentStream.Read(alphaData, 0, (int)alphaChunkSize);
}
var features = new WebPFeatures()
{
Animation = isAnimationPresent,
Alpha = isAlphaPresent,
+ AlphaData = alphaData,
ExifProfile = isExifPresent,
IccProfile = isIccPresent,
XmpMetaData = isXmpPresent
diff --git a/src/ImageSharp/Formats/WebP/WebPFeatures.cs b/src/ImageSharp/Formats/WebP/WebPFeatures.cs
index 6ae4f1e9d4..f0d7284020 100644
--- a/src/ImageSharp/Formats/WebP/WebPFeatures.cs
+++ b/src/ImageSharp/Formats/WebP/WebPFeatures.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// Image features of a VP8X image.
///
- public class WebPFeatures
+ internal class WebPFeatures
{
///
/// Gets or sets a value indicating whether this image has a ICC Profile.
@@ -18,6 +18,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public bool Alpha { get; set; }
+ ///
+ /// Gets or sets the alpha data, if an ALPH chunk is present.
+ ///
+ public byte[] AlphaData { get; set; }
+
///
/// Gets or sets a value indicating whether this image has a EXIF Profile.
///
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index 42402e025f..34ddade1e5 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -66,25 +66,46 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Decode image data.
this.ParseFrame(decoder, io);
- this.DecodePixelValues(width, height, decoder.Bgr, pixels);
+ byte[] decodedAlpha = null;
+ if (info.Features?.Alpha is true)
+ {
+ var alphaDecoder = new AlphaDecoder(width, height, info.Features.AlphaData);
+
+ // TODO: use memory allocator.
+ decodedAlpha = new byte[width * height];
+ alphaDecoder.Decode(decoder, decodedAlpha.AsSpan());
+ }
+
+ this.DecodePixelValues(width, height, decoder.Bgr, decodedAlpha, pixels);
}
- private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D pixels)
+ private void DecodePixelValues(int width, int height, Span pixelData, byte[] alpha, Buffer2D pixels)
where TPixel : struct, IPixel
{
TPixel color = default;
+ bool hasAlpha = alpha != null;
for (int y = 0; y < height; y++)
{
Span pixelRow = pixels.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
- int idx = ((y * width) + x) * 3;
- byte b = pixelData[idx];
- byte g = pixelData[idx + 1];
- byte r = pixelData[idx + 2];
+ int offset = (y * width) + x;
+ int idxBgr = offset * 3;
+ byte b = pixelData[idxBgr];
+ byte g = pixelData[idxBgr + 1];
+ byte r = pixelData[idxBgr + 2];
// TODO: use bulk conversion here.
- color.FromBgr24(new Bgr24(r, g, b));
+ if (hasAlpha)
+ {
+ byte a = alpha[offset];
+ color.FromBgra32(new Bgra32(r, g, b, a));
+ }
+ else
+ {
+ color.FromBgr24(new Bgr24(r, g, b));
+ }
+
pixelRow[x] = color;
}
}