diff --git a/src/ImageSharp/Formats/WebP/ColorCache.cs b/src/ImageSharp/Formats/WebP/ColorCache.cs
index 1fc47180f7..d5579cbf1e 100644
--- a/src/ImageSharp/Formats/WebP/ColorCache.cs
+++ b/src/ImageSharp/Formats/WebP/ColorCache.cs
@@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.WebP
{
+ ///
+ /// A small hash-addressed array to store recently used colors, to be able to recall them with shorter codes.
+ ///
internal class ColorCache
{
private const uint KHashMul = 0x1e35a7bdu;
@@ -22,6 +25,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public int HashBits { get; private set; }
+ ///
+ /// Initializes a new color cache.
+ ///
+ /// The hashBits determine the size of cache. It will be 1 left shifted by hashBits.
public void Init(int hashBits)
{
int hashSize = 1 << hashBits;
@@ -30,6 +37,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.HashShift = 32 - hashBits;
}
+ ///
+ /// Inserts a new color into the cache.
+ ///
+ /// The color to insert.
public void Insert(uint argb)
{
int key = this.HashPix(argb, this.HashShift);
diff --git a/src/ImageSharp/Formats/WebP/HuffmanCode.cs b/src/ImageSharp/Formats/WebP/HuffmanCode.cs
index b76f41d23f..ac6c5bec46 100644
--- a/src/ImageSharp/Formats/WebP/HuffmanCode.cs
+++ b/src/ImageSharp/Formats/WebP/HuffmanCode.cs
@@ -5,6 +5,9 @@ using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.WebP
{
+ ///
+ /// A classic way to do entropy coding where a smaller number of bits are used for more frequent codes.
+ ///
[DebuggerDisplay("BitsUsed: {BitsUsed}, Value: {Value}")]
internal class HuffmanCode
{
diff --git a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs b/src/ImageSharp/Formats/WebP/HuffmanUtils.cs
index a52ec3984a..3cab68dd12 100644
--- a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs
+++ b/src/ImageSharp/Formats/WebP/HuffmanUtils.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Guard.NotNull(codeLengths, nameof(codeLengths));
Guard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize));
- // sorted[code_lengths_size] is a pre-allocated array for sorting symbols by code length.
+ // sorted[codeLengthsSize] is a pre-allocated array for sorting symbols by code length.
var sorted = new int[codeLengthsSize];
int totalSize = 1 << rootBits; // total size root table + 2nd level table.
int len; // current code length.
diff --git a/src/ImageSharp/Formats/WebP/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/LosslessUtils.cs
index 7480318606..a0b11121d3 100644
--- a/src/ImageSharp/Formats/WebP/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/WebP/LosslessUtils.cs
@@ -30,6 +30,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ ///
+ /// If there are not many unique pixel values, it may be more efficient to create a color index array and replace the pixel values by the array's indices.
+ /// This will reverse the color index transform.
+ ///
+ /// The transform data contains color table size and the entries in the color table.
+ /// The pixel data to apply the reverse transform on.
public static void ColorIndexInverseTransform(Vp8LTransform transform, Span pixelData)
{
int bitsPerPixel = 8 >> transform.Bits;
@@ -80,6 +86,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ ///
+ /// The goal of the color transform is to de-correlate the R, G and B values of each pixel.
+ /// Color transform keeps the green (G) value as it is, transforms red (R) based on green and transforms blue (B) based on green and then based on red.
+ ///
+ /// The transform data.
+ /// The pixel data to apply the inverse transform on.
public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span pixelData)
{
int width = transform.XSize;
@@ -124,6 +136,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ ///
+ /// Reverses the color space transform.
+ ///
+ /// The color transform element.
+ /// The pixel data to apply the inverse transform on.
+ /// The start index of reverse transform.
+ /// The number of pixels to apply the transform.
public static void TransformColorInverse(Vp8LMultipliers m, Span pixelData, int start, int numPixels)
{
int end = start + numPixels;
@@ -144,24 +163,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
- public static void ExpandColorMap(int numColors, Span transformData, Span newColorMap)
- {
- newColorMap[0] = transformData[0];
- Span data = MemoryMarshal.Cast(transformData);
- Span newData = MemoryMarshal.Cast(newColorMap);
- int i;
- for (i = 4; i < 4 * numColors; ++i)
- {
- // Equivalent to AddPixelEq(), on a byte-basis.
- newData[i] = (byte)((data[i] + newData[i - 4]) & 0xff);
- }
-
- for (; i < 4 * newColorMap.Length; ++i)
- {
- newData[i] = 0; // black tail.
- }
- }
-
+ ///
+ /// The predictor transform can be used to reduce entropy by exploiting the fact that neighboring pixels are often correlated.
+ /// In the predictor transform, the current pixel value is predicted from the pixels already decoded (in scan-line order) and only the residual value (actual - predicted) is encoded.
+ /// The prediction mode determines the type of prediction to use. We divide the image into squares and all the pixels in a square use same prediction mode.
+ ///
+ /// The transform data.
+ /// The pixel data to apply the inverse transform.
+ /// The resulting pixel data with the reversed transformation data.
public static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span output)
{
int processedPixels = 0;
@@ -198,6 +207,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
xEnd = width;
}
+ // There are 14 different prediction modes.
+ // In each prediction mode, the current pixel value is predicted from one or more neighboring pixels whose values are already known.
int startIdx = processedPixels + x;
int numberOfPixels = xEnd - x;
switch (predictorMode)
@@ -602,9 +613,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
return (idx >> 8) & 0xff;
}
+ public static void ExpandColorMap(int numColors, Span transformData, Span newColorMap)
+ {
+ newColorMap[0] = transformData[0];
+ Span data = MemoryMarshal.Cast(transformData);
+ Span newData = MemoryMarshal.Cast(newColorMap);
+ int i;
+ for (i = 4; i < 4 * numColors; ++i)
+ {
+ // Equivalent to AddPixelEq(), on a byte-basis.
+ newData[i] = (byte)((data[i] + newData[i - 4]) & 0xff);
+ }
+
+ for (; i < 4 * newColorMap.Length; ++i)
+ {
+ newData[i] = 0; // black tail.
+ }
+ }
+
private static int ColorTransformDelta(sbyte colorPred, sbyte color)
{
- int delta = ((sbyte)colorPred * color) >> 5;
return ((int)colorPred * color) >> 5;
}
diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index b12ed0e726..39d7ecdfc2 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -162,13 +162,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
return new WebPImageInfo();
}
+ ///
+ /// Reads an the extended webp file header. An extended file header consists of:
+ /// - 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.
+ /// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow.
+ ///
+ /// Information about this webp image.
private WebPImageInfo ReadVp8XHeader()
{
uint chunkSize = this.ReadChunkSize();
- // This byte contains information about the image features used.
- // The first two bit should and the last bit should be 0.
- // TODO: should an exception be thrown if its not the case, or just ignore it?
+ // The first byte contains information about the image features used.
+ // The first two bit of it are reserved and should be 0. TODO: should an exception be thrown if its not the case, or just ignore it?
byte imageFeatures = (byte)this.currentStream.ReadByte();
// If bit 3 is set, a ICC Profile Chunk should be present.
@@ -349,7 +356,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
///
- /// Parses optional metadata chunks.
+ /// Parses optional metadata chunks. There SHOULD be at most one chunk of each type ('EXIF' and 'XMP ').
+ /// If there are more such chunks, readers MAY ignore all except the first one.
+ /// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks.
///
/// The webp features.
private void ParseOptionalChunks(WebPFeatures features)
diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
index ecdaf606c3..64bbf5cc18 100644
--- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
@@ -108,6 +108,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
transform.Data?.Dispose();
}
+
decoder.Metadata?.HuffmanImage?.Dispose();
}
@@ -644,12 +645,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform:
{
+ // The first 3 bits of prediction data define the block width and height in number of bits.
transform.Bits = (int)this.bitReader.ReadBits(3) + 2;
- IMemoryOwner transformData = this.DecodeImageStream(
- decoder,
- LosslessUtils.SubSampleSize(transform.XSize, transform.Bits),
- LosslessUtils.SubSampleSize(transform.YSize, transform.Bits),
- false);
+ int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
+ int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
+ IMemoryOwner transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
transform.Data = transformData;
break;
}
@@ -659,7 +659,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
///
- /// Reverses the transformations, if any are present.
+ /// A WebP lossless image can go through four different types of transformation before being entropy encoded.
+ /// This will reverses the transformations, if any are present.
///
/// The decoder holding the transformation infos.
/// The pixel data to apply the transformation.