Browse Source

Add additional comments

pull/1552/head
Brian Popow 6 years ago
parent
commit
b2b9a28085
  1. 11
      src/ImageSharp/Formats/WebP/ColorCache.cs
  2. 3
      src/ImageSharp/Formats/WebP/HuffmanCode.cs
  3. 2
      src/ImageSharp/Formats/WebP/HuffmanUtils.cs
  4. 66
      src/ImageSharp/Formats/WebP/LosslessUtils.cs
  5. 17
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  6. 13
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

11
src/ImageSharp/Formats/WebP/ColorCache.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary>
/// A small hash-addressed array to store recently used colors, to be able to recall them with shorter codes.
/// </summary>
internal class ColorCache internal class ColorCache
{ {
private const uint KHashMul = 0x1e35a7bdu; private const uint KHashMul = 0x1e35a7bdu;
@ -22,6 +25,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public int HashBits { get; private set; } public int HashBits { get; private set; }
/// <summary>
/// Initializes a new color cache.
/// </summary>
/// <param name="hashBits">The hashBits determine the size of cache. It will be 1 left shifted by hashBits.</param>
public void Init(int hashBits) public void Init(int hashBits)
{ {
int hashSize = 1 << hashBits; int hashSize = 1 << hashBits;
@ -30,6 +37,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.HashShift = 32 - hashBits; this.HashShift = 32 - hashBits;
} }
/// <summary>
/// Inserts a new color into the cache.
/// </summary>
/// <param name="argb">The color to insert.</param>
public void Insert(uint argb) public void Insert(uint argb)
{ {
int key = this.HashPix(argb, this.HashShift); int key = this.HashPix(argb, this.HashShift);

3
src/ImageSharp/Formats/WebP/HuffmanCode.cs

@ -5,6 +5,9 @@ using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary>
/// A classic way to do entropy coding where a smaller number of bits are used for more frequent codes.
/// </summary>
[DebuggerDisplay("BitsUsed: {BitsUsed}, Value: {Value}")] [DebuggerDisplay("BitsUsed: {BitsUsed}, Value: {Value}")]
internal class HuffmanCode internal class HuffmanCode
{ {

2
src/ImageSharp/Formats/WebP/HuffmanUtils.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Guard.NotNull(codeLengths, nameof(codeLengths)); Guard.NotNull(codeLengths, nameof(codeLengths));
Guard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize)); 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]; var sorted = new int[codeLengthsSize];
int totalSize = 1 << rootBits; // total size root table + 2nd level table. int totalSize = 1 << rootBits; // total size root table + 2nd level table.
int len; // current code length. int len; // current code length.

66
src/ImageSharp/Formats/WebP/LosslessUtils.cs

@ -30,6 +30,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
/// <summary>
/// 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.
/// </summary>
/// <param name="transform">The transform data contains color table size and the entries in the color table.</param>
/// <param name="pixelData">The pixel data to apply the reverse transform on.</param>
public static void ColorIndexInverseTransform(Vp8LTransform transform, Span<uint> pixelData) public static void ColorIndexInverseTransform(Vp8LTransform transform, Span<uint> pixelData)
{ {
int bitsPerPixel = 8 >> transform.Bits; int bitsPerPixel = 8 >> transform.Bits;
@ -80,6 +86,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
/// <summary>
/// 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.
/// </summary>
/// <param name="transform">The transform data.</param>
/// <param name="pixelData">The pixel data to apply the inverse transform on.</param>
public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span<uint> pixelData) public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span<uint> pixelData)
{ {
int width = transform.XSize; int width = transform.XSize;
@ -124,6 +136,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
/// <summary>
/// Reverses the color space transform.
/// </summary>
/// <param name="m">The color transform element.</param>
/// <param name="pixelData">The pixel data to apply the inverse transform on.</param>
/// <param name="start">The start index of reverse transform.</param>
/// <param name="numPixels">The number of pixels to apply the transform.</param>
public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData, int start, int numPixels) public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData, int start, int numPixels)
{ {
int end = start + numPixels; int end = start + numPixels;
@ -144,24 +163,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void ExpandColorMap(int numColors, Span<uint> transformData, Span<uint> newColorMap) /// <summary>
{ /// The predictor transform can be used to reduce entropy by exploiting the fact that neighboring pixels are often correlated.
newColorMap[0] = transformData[0]; /// 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.
Span<byte> data = MemoryMarshal.Cast<uint, byte>(transformData); /// 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.
Span<byte> newData = MemoryMarshal.Cast<uint, byte>(newColorMap); /// </summary>
int i; /// <param name="transform">The transform data.</param>
for (i = 4; i < 4 * numColors; ++i) /// <param name="pixelData">The pixel data to apply the inverse transform.</param>
{ /// <param name="output">The resulting pixel data with the reversed transformation data.</param>
// 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.
}
}
public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output) public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output)
{ {
int processedPixels = 0; int processedPixels = 0;
@ -198,6 +207,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
xEnd = width; 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 startIdx = processedPixels + x;
int numberOfPixels = xEnd - x; int numberOfPixels = xEnd - x;
switch (predictorMode) switch (predictorMode)
@ -602,9 +613,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
return (idx >> 8) & 0xff; return (idx >> 8) & 0xff;
} }
public static void ExpandColorMap(int numColors, Span<uint> transformData, Span<uint> newColorMap)
{
newColorMap[0] = transformData[0];
Span<byte> data = MemoryMarshal.Cast<uint, byte>(transformData);
Span<byte> newData = MemoryMarshal.Cast<uint, byte>(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) private static int ColorTransformDelta(sbyte colorPred, sbyte color)
{ {
int delta = ((sbyte)colorPred * color) >> 5;
return ((int)colorPred * color) >> 5; return ((int)colorPred * color) >> 5;
} }

17
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -162,13 +162,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
return new WebPImageInfo(); return new WebPImageInfo();
} }
/// <summary>
/// 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.
/// </summary>
/// <returns>Information about this webp image.</returns>
private WebPImageInfo ReadVp8XHeader() private WebPImageInfo ReadVp8XHeader()
{ {
uint chunkSize = this.ReadChunkSize(); uint chunkSize = this.ReadChunkSize();
// This byte contains information about the image features used. // The first byte contains information about the image features used.
// The first two bit should and the last bit should be 0. // 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?
// TODO: should an exception be thrown if its not the case, or just ignore it?
byte imageFeatures = (byte)this.currentStream.ReadByte(); byte imageFeatures = (byte)this.currentStream.ReadByte();
// If bit 3 is set, a ICC Profile Chunk should be present. // If bit 3 is set, a ICC Profile Chunk should be present.
@ -349,7 +356,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <param name="features">The webp features.</param> /// <param name="features">The webp features.</param>
private void ParseOptionalChunks(WebPFeatures features) private void ParseOptionalChunks(WebPFeatures features)

13
src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

@ -108,6 +108,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
transform.Data?.Dispose(); transform.Data?.Dispose();
} }
decoder.Metadata?.HuffmanImage?.Dispose(); decoder.Metadata?.HuffmanImage?.Dispose();
} }
@ -644,12 +645,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform: 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; transform.Bits = (int)this.bitReader.ReadBits(3) + 2;
IMemoryOwner<uint> transformData = this.DecodeImageStream( int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
decoder, int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
LosslessUtils.SubSampleSize(transform.XSize, transform.Bits), IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
LosslessUtils.SubSampleSize(transform.YSize, transform.Bits),
false);
transform.Data = transformData; transform.Data = transformData;
break; break;
} }
@ -659,7 +659,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <param name="decoder">The decoder holding the transformation infos.</param> /// <param name="decoder">The decoder holding the transformation infos.</param>
/// <param name="pixelData">The pixel data to apply the transformation.</param> /// <param name="pixelData">The pixel data to apply the transformation.</param>

Loading…
Cancel
Save