Browse Source

Introduce bitreader base class

pull/1147/head
Brian Popow 7 years ago
parent
commit
4d5ceca326
  1. 63
      src/ImageSharp/Formats/WebP/BitReaderBase.cs
  2. 40
      src/ImageSharp/Formats/WebP/Vp8BitReader.cs
  3. 66
      src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
  4. 2
      src/ImageSharp/Formats/WebP/WebPDecoderBase.cs
  5. 12
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  6. 7
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs
  7. 28
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
  8. 36
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

63
src/ImageSharp/Formats/WebP/BitReaderBase.cs

@ -0,0 +1,63 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Base class for VP8 and VP8L bitreader.
/// </summary>
internal abstract class BitReaderBase
{
/// <summary>
/// Gets raw encoded image data.
/// </summary>
protected byte[] Data { get; private set; }
/// <summary>
/// Reads a single bit from the stream.
/// </summary>
/// <returns>True if the bit read was 1, false otherwise.</returns>
public abstract bool ReadBit();
/// <summary>
/// Reads a unsigned short value from the inputStream. The bits of each byte are read in least-significant-bit-first order.
/// </summary>
/// <param name="nBits">The number of bits to read (should not exceed 16).</param>
/// <returns>A ushort value.</returns>
public abstract uint ReadValue(int nBits);
/// <summary>
/// Copies the raw encoded image data from the stream into a byte array.
/// </summary>
/// <param name="input">The input stream.</param>
/// <param name="bytesToRead">Number of bytes to read as indicated from the chunk size.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
{
using (var ms = new MemoryStream())
using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096))
{
Span<byte> bufferSpan = buffer.GetSpan();
int read;
while (bytesToRead > 0 && (read = input.Read(buffer.Array, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0)
{
ms.Write(buffer.Array, 0, read);
bytesToRead -= read;
}
if (bytesToRead > 0)
{
WebPThrowHelper.ThrowImageFormatException("image file has insufficient data");
}
this.Data = ms.ToArray();
}
}
}
}

40
src/ImageSharp/Formats/WebP/Vp8BitReader.cs

@ -2,10 +2,16 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.IO;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal class Vp8BitReader /// <summary>
/// A bit reader for VP8 streams.
/// </summary>
internal class Vp8BitReader : BitReaderBase
{ {
/// <summary> /// <summary>
/// Current value. /// Current value.
@ -43,14 +49,34 @@ namespace SixLabors.ImageSharp.Formats.WebP
private bool eof; private bool eof;
/// <summary> /// <summary>
/// Reads the specified number of bits from read buffer. /// Initializes a new instance of the <see cref="Vp8BitReader"/> class.
/// Flags an error in case end_of_stream or n_bits is more than the allowed limit
/// of VP8L_MAX_NUM_BIT_READ (inclusive).
/// Flags eos_ if this read attempt is going to cross the read buffer.
/// </summary> /// </summary>
/// <param name="nBits">The number of bits to read.</param> /// <param name="inputStream">The input stream to read from.</param>
public int ReadBits(int nBits) /// <param name="imageDataSize">The raw image data size in bytes.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator)
{
this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
}
/// <inheritdoc/>
public override bool ReadBit()
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public override uint ReadValue(int nBits)
{ {
Guard.MustBeGreaterThan(nBits, 0, nameof(nBits));
throw new NotImplementedException();
}
public int ReadSignedValue(int nBits)
{
Guard.MustBeGreaterThan(nBits, 0, nameof(nBits));
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }

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

@ -1,18 +1,16 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary> /// <summary>
/// A bit reader for VP8 streams. /// A bit reader for VP8L streams.
/// </summary> /// </summary>
internal class Vp8LBitReader internal class Vp8LBitReader : BitReaderBase
{ {
/// <summary> /// <summary>
/// Maximum number of bits (inclusive) the bit-reader can handle. /// Maximum number of bits (inclusive) the bit-reader can handle.
@ -20,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private const int Vp8LMaxNumBitRead = 24; private const int Vp8LMaxNumBitRead = 24;
/// <summary> /// <summary>
/// Number of bits prefetched (= bit-size of vp8l_val_t). /// Number of bits prefetched.
/// </summary> /// </summary>
private const int Vp8LLbits = 64; private const int Vp8LLbits = 64;
@ -40,8 +38,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff
}; };
private readonly byte[] data;
/// <summary> /// <summary>
/// Pre-fetched bits. /// Pre-fetched bits.
/// </summary> /// </summary>
@ -71,17 +67,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Initializes a new instance of the <see cref="Vp8LBitReader"/> class. /// Initializes a new instance of the <see cref="Vp8LBitReader"/> class.
/// </summary> /// </summary>
/// <param name="inputStream">The input stream to read from.</param> /// <param name="inputStream">The input stream to read from.</param>
/// <param name="imageDataSize">The image data size in bytes.</param> /// <param name="imageDataSize">The raw image data size in bytes.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param> /// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator) public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator)
{ {
long length = imageDataSize; long length = imageDataSize;
using (var ms = new MemoryStream()) this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
{
CopyStream(inputStream, ms, (int)imageDataSize, memoryAllocator);
this.data = ms.ToArray();
}
this.len = length; this.len = length;
this.value = 0; this.value = 0;
@ -96,19 +88,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
ulong currentValue = 0; ulong currentValue = 0;
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
{ {
currentValue |= (ulong)this.data[i] << (8 * i); currentValue |= (ulong)this.Data[i] << (8 * i);
} }
this.value = currentValue; this.value = currentValue;
this.pos = length; this.pos = length;
} }
/// <summary> /// <inheritdoc/>
/// Reads a unsigned short value from the inputStream. The bits of each byte are read in least-significant-bit-first order. public override uint ReadValue(int nBits)
/// </summary>
/// <param name="nBits">The number of bits to read (should not exceed 16).</param>
/// <returns>A ushort value.</returns>
public uint ReadBits(int nBits)
{ {
Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); Guard.MustBeGreaterThan(nBits, 0, nameof(nBits));
@ -125,13 +113,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
return 0; return 0;
} }
/// <summary> /// <inheritdoc/>
/// Reads a single bit from the stream. public override bool ReadBit()
/// </summary>
/// <returns>True if the bit read was 1, false otherwise.</returns>
public bool ReadBit()
{ {
uint bit = this.ReadBits(1); uint bit = this.ReadValue(1);
return bit != 0; return bit != 0;
} }
@ -153,14 +138,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public void DoFillBitWindow() public bool IsEndOfStream()
{ {
this.ShiftBytes(); return this.eos || ((this.pos == this.len) && (this.bitPos > Vp8LLbits));
} }
public bool IsEndOfStream() private void DoFillBitWindow()
{ {
return this.eos || ((this.pos == this.len) && (this.bitPos > Vp8LLbits)); this.ShiftBytes();
} }
/// <summary> /// <summary>
@ -171,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (this.bitPos >= 8 && this.pos < this.len) while (this.bitPos >= 8 && this.pos < this.len)
{ {
this.value >>= 8; this.value >>= 8;
this.value |= (ulong)this.data[this.pos] << (Vp8LLbits - 8); this.value |= (ulong)this.Data[this.pos] << (Vp8LLbits - 8);
++this.pos; ++this.pos;
this.bitPos -= 8; this.bitPos -= 8;
} }
@ -187,24 +172,5 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.eos = true; this.eos = true;
this.bitPos = 0; // To avoid undefined behaviour with shifts. this.bitPos = 0; // To avoid undefined behaviour with shifts.
} }
private static void CopyStream(Stream input, Stream output, int bytesToRead, MemoryAllocator memoryAllocator)
{
using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096))
{
Span<byte> bufferSpan = buffer.GetSpan();
int read;
while (bytesToRead > 0 && (read = input.Read(buffer.Array, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0)
{
output.Write(buffer.Array, 0, read);
bytesToRead -= read;
}
if (bytesToRead > 0)
{
WebPThrowHelper.ThrowImageFormatException("image file has insufficient data");
}
}
}
} }
} }

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

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int extraBits = (distanceSymbol - 2) >> 1; int extraBits = (distanceSymbol - 2) >> 1;
int offset = (2 + (distanceSymbol & 1)) << extraBits; int offset = (2 + (distanceSymbol & 1)) << extraBits;
return (int)(offset + bitReader.ReadBits(extraBits) + 1); return (int)(offset + bitReader.ReadValue(extraBits) + 1);
} }
// TODO: copied from WebPLosslessDecoder // TODO: copied from WebPLosslessDecoder

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

@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
else else
{ {
var lossyDecoder = new WebPLossyDecoder(this.configuration, this.currentStream); var lossyDecoder = new WebPLossyDecoder(this.configuration, this.currentStream);
lossyDecoder.Decode(pixels, image.Width, image.Height, (int)imageInfo.ImageDataSize); lossyDecoder.Decode(pixels, image.Width, image.Height, imageInfo.ImageDataSize, imageInfo.Vp8Profile);
} }
// There can be optional chunks after the image data, like EXIF and XMP. // There can be optional chunks after the image data, like EXIF and XMP.
@ -261,7 +261,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// A VP8 or VP8L chunk should follow here. // A VP8 or VP8L chunk should follow here.
chunkType = this.ReadChunkType(); chunkType = this.ReadChunkType();
// TOOD: image width and height from VP8X should overrule VP8 or VP8L info, because its 3 bytes instead of just 14 bit. // TOOD: check if VP8 or VP8L info about the dimensions match VP8X info
switch (chunkType) switch (chunkType)
{ {
case WebPChunkType.Vp8: case WebPChunkType.Vp8:
@ -361,15 +361,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
var bitReader = new Vp8LBitReader(this.currentStream, imageDataSize, this.memoryAllocator); var bitReader = new Vp8LBitReader(this.currentStream, imageDataSize, this.memoryAllocator);
// One byte signature, should be 0x2f. // One byte signature, should be 0x2f.
uint signature = bitReader.ReadBits(8); uint signature = bitReader.ReadValue(8);
if (signature != WebPConstants.Vp8LMagicByte) if (signature != WebPConstants.Vp8LMagicByte)
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid VP8L signature"); WebPThrowHelper.ThrowImageFormatException("Invalid VP8L signature");
} }
// The first 28 bits of the bitstream specify the width and height of the image. // The first 28 bits of the bitstream specify the width and height of the image.
uint width = bitReader.ReadBits(WebPConstants.Vp8LImageSizeBits) + 1; uint width = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1;
uint height = bitReader.ReadBits(WebPConstants.Vp8LImageSizeBits) + 1; uint height = bitReader.ReadValue(WebPConstants.Vp8LImageSizeBits) + 1;
if (width is 0 || height is 0) if (width is 0 || height is 0)
{ {
WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); WebPThrowHelper.ThrowImageFormatException("width or height can not be zero");
@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// The next 3 bits are the version. The version_number is a 3 bit code that must be set to 0. // The next 3 bits are the version. The version_number is a 3 bit code that must be set to 0.
// Any other value should be treated as an error. // Any other value should be treated as an error.
// TODO: should we throw here when version number is != 0? // TODO: should we throw here when version number is != 0?
uint version = bitReader.ReadBits(WebPConstants.Vp8LVersionBits); uint version = bitReader.ReadValue(WebPConstants.Vp8LVersionBits);
return new WebPImageInfo() return new WebPImageInfo()
{ {

7
src/ImageSharp/Formats/WebP/WebPImageInfo.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public WebPBitsPerPixel BitsPerPixel { get; set; } public WebPBitsPerPixel BitsPerPixel { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image uses a lossless compression. /// Gets or sets a value indicating whether this image uses lossless compression.
/// </summary> /// </summary>
public bool IsLossLess { get; set; } public bool IsLossLess { get; set; }
@ -35,6 +35,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public uint ImageDataSize { get; set; } public uint ImageDataSize { get; set; }
/// <summary>
/// Gets or sets the VP8 profile / version. Valid values are between 0 and 3.
/// </summary>
public byte Vp8Profile { get; set; }
/// <summary> /// <summary>
/// Gets or sets Vp8L bitreader. Will be null if its not lossless image. /// Gets or sets Vp8L bitreader. Will be null if its not lossless image.
/// </summary> /// </summary>

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

@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int colorCacheSize = 0; int colorCacheSize = 0;
if (colorCachePresent) if (colorCachePresent)
{ {
colorCacheBits = (int)this.bitReader.ReadBits(4); colorCacheBits = (int)this.bitReader.ReadValue(4);
bool coloCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits; bool coloCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits;
if (!coloCacheBitsIsValid) if (!coloCacheBitsIsValid)
{ {
@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (allowRecursion && this.bitReader.ReadBit()) if (allowRecursion && this.bitReader.ReadBit())
{ {
// Use meta Huffman codes. // Use meta Huffman codes.
uint huffmanPrecision = this.bitReader.ReadBits(3) + 2; uint huffmanPrecision = this.bitReader.ReadValue(3) + 2;
int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision); int huffmanXSize = LosslessUtils.SubSampleSize(xSize, (int)huffmanPrecision);
int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision); int huffmanYSize = LosslessUtils.SubSampleSize(ySize, (int)huffmanPrecision);
int huffmanPixels = huffmanXSize * huffmanYSize; int huffmanPixels = huffmanXSize * huffmanYSize;
@ -487,17 +487,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
// and are in the range of[0, 255].All other Huffman code lengths are implicitly zeros. // and are in the range of[0, 255].All other Huffman code lengths are implicitly zeros.
// Read symbols, codes & code lengths directly. // Read symbols, codes & code lengths directly.
uint numSymbols = this.bitReader.ReadBits(1) + 1; uint numSymbols = this.bitReader.ReadValue(1) + 1;
uint firstSymbolLenCode = this.bitReader.ReadBits(1); uint firstSymbolLenCode = this.bitReader.ReadValue(1);
// The first code is either 1 bit or 8 bit code. // The first code is either 1 bit or 8 bit code.
uint symbol = this.bitReader.ReadBits((firstSymbolLenCode is 0) ? 1 : 8); uint symbol = this.bitReader.ReadValue((firstSymbolLenCode is 0) ? 1 : 8);
codeLengths[symbol] = 1; codeLengths[symbol] = 1;
// The second code (if present), is always 8 bit long. // The second code (if present), is always 8 bit long.
if (numSymbols is 2) if (numSymbols is 2)
{ {
symbol = this.bitReader.ReadBits(8); symbol = this.bitReader.ReadValue(8);
codeLengths[symbol] = 1; codeLengths[symbol] = 1;
} }
} }
@ -507,7 +507,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// The code lengths of a Huffman code are read as follows: num_code_lengths specifies the number of code lengths; // The code lengths of a Huffman code are read as follows: num_code_lengths specifies the number of code lengths;
// the rest of the code lengths (according to the order in kCodeLengthCodeOrder) are zeros. // the rest of the code lengths (according to the order in kCodeLengthCodeOrder) are zeros.
var codeLengthCodeLengths = new int[NumCodeLengthCodes]; var codeLengthCodeLengths = new int[NumCodeLengthCodes];
uint numCodes = this.bitReader.ReadBits(4) + 4; uint numCodes = this.bitReader.ReadValue(4) + 4;
if (numCodes > NumCodeLengthCodes) if (numCodes > NumCodeLengthCodes)
{ {
WebPThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value"); WebPThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value");
@ -515,7 +515,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int i = 0; i < numCodes; i++) for (int i = 0; i < numCodes; i++)
{ {
codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3); codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadValue(3);
} }
this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths); this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths);
@ -539,8 +539,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (this.bitReader.ReadBit()) if (this.bitReader.ReadBit())
{ {
int lengthNBits = 2 + (2 * (int)this.bitReader.ReadBits(3)); int lengthNBits = 2 + (2 * (int)this.bitReader.ReadValue(3));
maxSymbol = 2 + (int)this.bitReader.ReadBits(lengthNBits); maxSymbol = 2 + (int)this.bitReader.ReadValue(lengthNBits);
} }
else else
{ {
@ -574,7 +574,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint slot = codeLen - WebPConstants.KCodeLengthLiterals; uint slot = codeLen - WebPConstants.KCodeLengthLiterals;
int extraBits = WebPConstants.KCodeLengthExtraBits[slot]; int extraBits = WebPConstants.KCodeLengthExtraBits[slot];
int repeatOffset = WebPConstants.KCodeLengthRepeatOffsets[slot]; int repeatOffset = WebPConstants.KCodeLengthRepeatOffsets[slot];
int repeat = (int)(this.bitReader.ReadBits(extraBits) + repeatOffset); int repeat = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset);
if (symbol + repeat > numSymbols) if (symbol + repeat > numSymbols)
{ {
// TODO: not sure, if this should be treated as an error here // TODO: not sure, if this should be treated as an error here
@ -598,7 +598,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param> /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder) private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
{ {
var transformType = (Vp8LTransformType)this.bitReader.ReadBits(2); var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
var transform = new Vp8LTransform(transformType, xSize, ySize); var transform = new Vp8LTransform(transformType, xSize, ySize);
// Each transform is allowed to be used only once. // Each transform is allowed to be used only once.
@ -615,7 +615,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
case Vp8LTransformType.ColorIndexingTransform: case Vp8LTransformType.ColorIndexingTransform:
// The transform data contains color table size and the entries in the color table. // The transform data contains color table size and the entries in the color table.
// 8 bit value for color table size. // 8 bit value for color table size.
uint numColors = this.bitReader.ReadBits(8) + 1; uint numColors = this.bitReader.ReadValue(8) + 1;
int bits = (numColors > 16) ? 0 int bits = (numColors > 16) ? 0
: (numColors > 4) ? 1 : (numColors > 4) ? 1
: (numColors > 2) ? 2 : (numColors > 2) ? 2
@ -636,7 +636,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
case Vp8LTransformType.CrossColorTransform: case Vp8LTransformType.CrossColorTransform:
{ {
// The first 3 bits of prediction data define the block width and height in number of bits. // 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.ReadValue(3) + 2;
int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits); int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits); int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false); IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);

36
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -18,48 +18,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
private MemoryAllocator memoryAllocator; private MemoryAllocator memoryAllocator;
public WebPLossyDecoder( public WebPLossyDecoder(Configuration configuration, Stream currentStream)
Configuration configuration,
Stream currentStream)
{ {
this.configuration = configuration; this.configuration = configuration;
this.currentStream = currentStream; this.currentStream = currentStream;
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = configuration.MemoryAllocator;
} }
public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height, int imageDataSize) public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height, uint imageDataSize, byte vp8Version)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// TODO: implement decoding. For simulating the decoding: skipping the chunk size bytes.
this.currentStream.Skip(imageDataSize - 10); // TODO: Not sure why we need to skip 10 bytes less here
// we need buffers for Y U and V in size of the image // we need buffers for Y U and V in size of the image
// TODO: increase size to enable using all prediction blocks? (see https://tools.ietf.org/html/rfc6386#page-9 ) // TODO: increase size to enable using all prediction blocks? (see https://tools.ietf.org/html/rfc6386#page-9 )
Buffer2D<YUVPixel> yuvBufferCurrentFrame = Buffer2D<YUVPixel> yuvBufferCurrentFrame = this.memoryAllocator.Allocate2D<YUVPixel>(width, height);
this.memoryAllocator
.Allocate2D<YUVPixel>(width, height);
// TODO: var predictionBuffer - macro-block-sized with approximation of the portion of the image being reconstructed. // TODO: var predictionBuffer - macro-block-sized with approximation of the portion of the image being reconstructed.
// those prediction values are the base, the values from DCT processing are added to that // those prediction values are the base, the values from DCT processing are added to that
// TODO residue signal from DCT: 4x4 blocks of DCT transforms, 16Y, 4U, 4V // TODO residue signal from DCT: 4x4 blocks of DCT transforms, 16Y, 4U, 4V
// TODO weiter bei S.11 var bitReader = new Vp8BitReader(this.currentStream, imageDataSize, this.memoryAllocator);
(ReconstructionFilter rec, LoopFilter loop) = this.DecodeVersion(vp8Version);
// bit STREAM: See https://tools.ietf.org/html/rfc6386#page-29 ("Frame Header")
// TODO: Vp8BitReader should be used here instead
/*Vp8LBitReader bitReader = new Vp8LBitReader(this.currentStream);
bool isInterframe = bitReader.ReadBit();
if (isInterframe)
{
throw new NotImplementedException("only key frames supported yet");
}
byte version = (byte)((bitReader.ReadBit() ? 2 : 0) | (bitReader.ReadBit() ? 1 : 0));
(ReconstructionFilter rec, LoopFilter loop) = this.DecodeVersion(version);
bool isShowFrame = bitReader.ReadBit();
uint firstPartitionSize = (bitReader.ReadBits(16) << 3) | bitReader.ReadBits(3);*/
} }
private (ReconstructionFilter, LoopFilter) DecodeVersion(byte version) private (ReconstructionFilter, LoopFilter) DecodeVersion(byte version)
@ -78,8 +56,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
case 3: case 3:
return (ReconstructionFilter.None, LoopFilter.None); return (ReconstructionFilter.None, LoopFilter.None);
default: default:
// Reserved for future use in Spec.
// https://tools.ietf.org/html/rfc6386#page-30 // https://tools.ietf.org/html/rfc6386#page-30
throw new NotSupportedException("reserved for future use in Spec"); WebPThrowHelper.ThrowNotSupportedException($"unsupported VP8 version {version} found");
return (rec, loop);
} }
} }
} }

Loading…
Cancel
Save