Browse Source

Fix more decoding bugs, lossless_vec_2_0.webp.webp now decodes without error

pull/1552/head
Brian Popow 7 years ago
parent
commit
4ec83f8036
  1. 21
      src/ImageSharp/Formats/WebP/Vp8LDecoder.cs
  2. 98
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

21
src/ImageSharp/Formats/WebP/Vp8LDecoder.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class Vp8LDecoder
{
public Vp8LDecoder(int width, int height)
{
this.Width = width;
this.Height = height;
this.Metadata = new Vp8LMetadata();
}
public int Width { get; set; }
public int Height { get; set; }
public Vp8LMetadata Metadata { get; set; }
}
}

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

@ -81,7 +81,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height) public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
uint[] pixelData = this.DecodeImageStream(width, height, true); var decoder = new Vp8LDecoder(width, height);
uint[] pixelData = this.DecodeImageStream(decoder, width, height, true);
this.DecodePixelValues(width, height, pixelData, pixels); this.DecodePixelValues(width, height, pixelData, pixels);
} }
@ -96,33 +97,44 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
int idx = (y * width) + x; int idx = (y * width) + x;
uint pixel = pixelData[idx]; uint pixel = pixelData[idx];
uint a = (pixel & 0xFF000000) >> 24; byte a = (byte)((pixel & 0xFF000000) >> 24);
uint r = (pixel & 0xFF0000) >> 16; byte r = (byte)((pixel & 0xFF0000) >> 16);
uint g = (pixel & 0xFF00) >> 8; byte g = (byte)((pixel & 0xFF00) >> 8);
uint b = pixel & 0xFF; byte b = (byte)(pixel & 0xFF);
color.FromRgba32(new Rgba32(r, g, b, a)); color.FromRgba32(new Rgba32(r, g, b, a));
pixelRow[x] = color; pixelRow[x] = color;
} }
} }
} }
private uint[] DecodeImageStream(int xSize, int ySize, bool isLevel0) private uint[] DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0)
{ {
if (isLevel0) if (isLevel0)
{ {
this.ReadTransformations(); this.ReadTransformations();
} }
// Read color cache, if present. // Color cache.
bool colorCachePresent = this.bitReader.ReadBit(); bool colorCachePresent = this.bitReader.ReadBit();
int colorCacheBits = 0; int colorCacheBits = 0;
int colorCacheSize = 0; int colorCacheSize = 0;
if (colorCachePresent)
{
colorCacheBits = (int)this.bitReader.ReadBits(4);
// TODO: error check color cache bits
}
// Read the Huffman codes (may recurse).
this.ReadHuffmanCodes(decoder, xSize, ySize, colorCacheBits, isLevel0);
decoder.Metadata.ColorCacheSize = colorCacheSize;
// Finish setting up the color-cache
ColorCache colorCache = null; ColorCache colorCache = null;
if (colorCachePresent) if (colorCachePresent)
{ {
colorCache = new ColorCache(); colorCache = new ColorCache();
colorCacheBits = (int)this.bitReader.ReadBits(4);
colorCacheSize = 1 << colorCacheBits; colorCacheSize = 1 << colorCacheBits;
decoder.Metadata.ColorCacheSize = colorCacheSize;
if (!(colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits)) if (!(colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits))
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
@ -130,27 +142,31 @@ namespace SixLabors.ImageSharp.Formats.WebP
colorCache.Init(colorCacheBits); colorCache.Init(colorCacheBits);
} }
else
{
decoder.Metadata.ColorCacheSize = 0;
}
Vp8LMetadata metadata = this.ReadHuffmanCodes(xSize, ySize, colorCacheBits, isLevel0); this.UpdateDecoder(decoder, xSize, ySize);
var numBits = 0; // TODO: use huffmanSubsampleBits.
metadata.HuffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1; uint[] pixelData = this.DecodeImageData(decoder, xSize, ySize, colorCacheSize, colorCache);
metadata.ColorCacheSize = colorCacheSize; if (!isLevel0)
{
decoder.Metadata = new Vp8LMetadata();
}
uint[] pixelData = this.DecodeImageData(xSize, ySize, colorCacheSize, metadata, colorCache);
return pixelData; return pixelData;
} }
private uint[] DecodeImageData(int width, int height, int colorCacheSize, Vp8LMetadata metadata, ColorCache colorCache) private uint[] DecodeImageData(Vp8LDecoder decoder, int width, int height, int colorCacheSize, ColorCache colorCache)
{ {
int lastPixel = 0; int lastPixel = 0;
int row = lastPixel / width; int row = lastPixel / width;
int col = lastPixel % width; int col = lastPixel % width;
int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes;
int colorCacheLimit = lenCodeLimit + colorCacheSize; int colorCacheLimit = lenCodeLimit + colorCacheSize;
bool decIsIncremental = false; // TODO: determine correct value for decIsIncremental int mask = decoder.Metadata.HuffmanMask;
int nextSyncRow = decIsIncremental ? row : 1 << 24; HTreeGroup[] hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row);
int mask = metadata.HuffmanMask;
HTreeGroup[] hTreeGroup = this.GetHTreeGroupForPos(metadata, col, row);
var pixelData = new uint[width * height]; var pixelData = new uint[width * height];
int totalPixels = width * height; int totalPixels = width * height;
@ -161,7 +177,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int code = 0; int code = 0;
if ((col & mask) == 0) if ((col & mask) == 0)
{ {
hTreeGroup = this.GetHTreeGroupForPos(metadata, col, row); hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row);
} }
if (hTreeGroup[0].IsTrivialCode) if (hTreeGroup[0].IsTrivialCode)
@ -214,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
break; break;
} }
int pixelIdx = decodedPixels * 4; int pixelIdx = decodedPixels;
pixelData[pixelIdx] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue); pixelData[pixelIdx] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
} }
@ -245,7 +261,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if ((col & mask) != 0) if ((col & mask) != 0)
{ {
hTreeGroup = this.GetHTreeGroupForPos(metadata, col, row); hTreeGroup = this.GetHTreeGroupForPos(decoder.Metadata, col, row);
} }
if (colorCache != null) if (colorCache != null)
@ -287,10 +303,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
col = 0; col = 0;
++row; ++row;
/*if (row <= lastRow && (row % NumArgbCacheRows == 0))
{
this.ProcessRowFunc(row);
}*/
if (colorCache != null) if (colorCache != null)
{ {
@ -303,9 +315,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private Vp8LMetadata ReadHuffmanCodes(int xSize, int ySize, int colorCacheBits, bool allowRecursion) private void ReadHuffmanCodes(Vp8LDecoder decoder, int xSize, int ySize, int colorCacheBits, bool allowRecursion)
{ {
var metadata = new Vp8LMetadata();
int maxAlphabetSize = 0; int maxAlphabetSize = 0;
int numHTreeGroups = 1; int numHTreeGroups = 1;
int numHTreeGroupsMax = 1; int numHTreeGroupsMax = 1;
@ -319,8 +330,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
int huffmanXSize = this.SubSampleSize(xSize, (int)huffmanPrecision); int huffmanXSize = this.SubSampleSize(xSize, (int)huffmanPrecision);
int huffmanYSize = this.SubSampleSize(ySize, (int)huffmanPrecision); int huffmanYSize = this.SubSampleSize(ySize, (int)huffmanPrecision);
int huffmanPixs = huffmanXSize * huffmanYSize; int huffmanPixs = huffmanXSize * huffmanYSize;
uint[] huffmanImage = this.DecodeImageStream(huffmanXSize, huffmanYSize, false); uint[] huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
metadata.HuffmanSubSampleBits = (int)huffmanPrecision; decoder.Metadata.HuffmanSubSampleBits = (int)huffmanPrecision;
for (int i = 0; i < huffmanPixs; ++i) for (int i = 0; i < huffmanPixs; ++i)
{ {
// The huffman data is stored in red and green bytes. // The huffman data is stored in red and green bytes.
@ -333,9 +344,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
numHTreeGroups = numHTreeGroupsMax; numHTreeGroups = numHTreeGroupsMax;
metadata.HuffmanImage = huffmanImage; decoder.Metadata.HuffmanImage = huffmanImage;
metadata.HuffmanXSize = this.SubSampleSize(huffmanXSize, metadata.HuffmanSubSampleBits);
metadata.HuffmanMask = (metadata.HuffmanSubSampleBits == 0) ? ~0 : (1 << metadata.HuffmanSubSampleBits) - 1;
} }
// Find maximum alphabet size for the hTree group. // Find maximum alphabet size for the hTree group.
@ -373,7 +382,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
alphabetSize += 1 << colorCacheBits; alphabetSize += 1 << colorCacheBits;
} }
int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable); int size = this.ReadHuffmanCode(decoder, alphabetSize, codeLengths, huffmanTable);
if (size is 0) if (size is 0)
{ {
WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero");
@ -428,14 +437,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
metadata.NumHTreeGroups = numHTreeGroups; decoder.Metadata.NumHTreeGroups = numHTreeGroups;
metadata.HTreeGroups = hTreeGroups; decoder.Metadata.HTreeGroups = hTreeGroups;
metadata.HuffmanTables = huffmanTables; decoder.Metadata.HuffmanTables = huffmanTables;
return metadata;
} }
private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span<HuffmanCode> table) private int ReadHuffmanCode(Vp8LDecoder decoder, int alphabetSize, int[] codeLengths, Span<HuffmanCode> table)
{ {
bool simpleCode = this.bitReader.ReadBit(); bool simpleCode = this.bitReader.ReadBit();
for (int i = 0; i < alphabetSize; i++) for (int i = 0; i < alphabetSize; i++)
@ -481,7 +488,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3); codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3);
} }
this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths); this.ReadHuffmanCodeLengths(decoder, table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths);
} }
int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize); int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);
@ -489,7 +496,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return size; return size;
} }
private void ReadHuffmanCodeLengths(HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) private void ReadHuffmanCodeLengths(Vp8LDecoder decoder, HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths)
{ {
int maxSymbol; int maxSymbol;
int symbol = 0; int symbol = 0;
@ -612,6 +619,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO: return transformation in an appropriate form. // TODO: return transformation in an appropriate form.
} }
private void UpdateDecoder(Vp8LDecoder decoder, int width, int height)
{
int numBits = decoder.Metadata.HuffmanSubSampleBits;
decoder.Width = width;
decoder.Height = height;
decoder.Metadata.HuffmanXSize = this.SubSampleSize(width, numBits);
decoder.Metadata.HuffmanMask = (numBits is 0) ? ~0 : (1 << numBits) - 1;
}
/// <summary> /// <summary>
/// Computes sampled size of 'size' when sampling using 'sampling bits'. /// Computes sampled size of 'size' when sampling using 'sampling bits'.
/// </summary> /// </summary>

Loading…
Cancel
Save