Browse Source

Add additional helper methods for decoding the image, start with decoding the image (still WIP)

pull/1552/head
Brian Popow 6 years ago
parent
commit
d45fe26a69
  1. 16
      src/ImageSharp/Formats/WebP/ColorCache.cs
  2. 15
      src/ImageSharp/Formats/WebP/HTreeGroup.cs
  3. 12
      src/ImageSharp/Formats/WebP/HuffIndex.cs
  4. 2
      src/ImageSharp/Formats/WebP/HuffmanCode.cs
  5. 8
      src/ImageSharp/Formats/WebP/HuffmanUtils.cs
  6. 11
      src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
  7. 28
      src/ImageSharp/Formats/WebP/Vp8LMetadata.cs
  8. 6
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  9. 389
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

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

@ -18,5 +18,21 @@ namespace SixLabors.ImageSharp.Formats.WebP
public uint HashShift { get; set; }
public uint HashBits { get; set; }
public void Init(int colorCacheBits)
{
}
public void Insert()
{
// TODO: implement VP8LColorCacheInsert
}
public int ColorCacheLookup()
{
// TODO: implement VP8LColorCacheLookup
return 0;
}
}
}

15
src/ImageSharp/Formats/WebP/HTreeGroup.cs

@ -16,15 +16,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
internal class HTreeGroup
{
public HTreeGroup()
public HTreeGroup(uint packedTableSize)
{
HTree = new List<HuffmanCode[]>(WebPConstants.HuffmanCodesPerMetaCode);
this.HTrees = new List<HuffmanCode[]>(WebPConstants.HuffmanCodesPerMetaCode);
this.PackedTable = new HuffmanCode[packedTableSize];
for (int i = 0; i < packedTableSize; i++)
{
this.PackedTable[i] = new HuffmanCode();
}
}
/// <summary>
/// This has a maximum of HuffmanCodesPerMetaCode (5) entrys.
/// This has a maximum of HuffmanCodesPerMetaCode (5) entry's.
/// </summary>
public List<HuffmanCode[]> HTree { get; private set; }
public List<HuffmanCode[]> HTrees { get; private set; }
/// <summary>
/// True, if huffman trees for Red, Blue & Alpha Symbols are trivial (have a single code).
@ -49,6 +54,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Table mapping input bits to a packed values, or escape case to literal code.
/// </summary>
public HuffmanCode PackedTable { get; set; }
public HuffmanCode[] PackedTable { get; set; }
}
}

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

@ -6,31 +6,31 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Five Huffman codes are used at each meta code.
/// </summary>
public enum HuffIndex : int
public static class HuffIndex
{
/// <summary>
/// Green + length prefix codes + color cache codes.
/// </summary>
Green = 0,
public const int Green = 0;
/// <summary>
/// Red.
/// </summary>
Red = 1,
public const int Red = 1;
/// <summary>
/// Blue.
/// </summary>
Blue = 2,
public const int Blue = 2;
/// <summary>
/// Alpha.
/// </summary>
Alpha = 3,
public const int Alpha = 3;
/// <summary>
/// Distance prefix codes.
/// </summary>
Dist = 4
public const int Dist = 4;
}
}

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

@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Gets or sets the symbol value or table offset.
/// </summary>
public int Value { get; set; }
public uint Value { get; set; }
}
}

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

@ -13,6 +13,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
public const int HuffmanTableMask = (1 << HuffmanTableBits) - 1;
public const uint HuffmanPackedTableSize = 1u << HuffmanPackedBits;
public static int BuildHuffmanTable(Span<HuffmanCode> table, int rootBits, int[] codeLengths, int codeLengthsSize)
{
Guard.MustBeGreaterThan(rootBits, 0, nameof(rootBits));
@ -71,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
var huffmanCode = new HuffmanCode()
{
BitsUsed = 0,
Value = sorted[0]
Value = (uint)sorted[0]
};
ReplicateValue(table, 1, totalSize, huffmanCode);
return totalSize;
@ -102,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
var huffmanCode = new HuffmanCode()
{
BitsUsed = len,
Value = sorted[symbol++]
Value = (uint)sorted[symbol++]
};
ReplicateValue(table.Slice(key), step, tableSize, huffmanCode);
key = GetNextKey(key, len);
@ -138,7 +140,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
var huffmanCode = new HuffmanCode
{
BitsUsed = len - rootBits,
Value = sorted[symbol++]
Value = (uint)sorted[symbol++]
};
ReplicateValue(tableSpan.Slice(key >> rootBits), step, tableSize, huffmanCode);
key = GetNextKey(key, len);

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.WebP
@ -151,6 +150,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.ShiftBytes();
}
public bool IsEndOfStream()
{
return this.eos || ((this.pos == this.len) && (this.bitPos > VP8L_LBITS));
}
private void ShiftBytes()
{
while (this.bitPos >= 8 && this.pos < this.len)
@ -167,11 +171,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
private bool IsEndOfStream()
{
return this.eos || ((this.pos == this.len) && (this.bitPos > VP8L_LBITS));
}
private void SetEndOfStream()
{
this.eos = true;

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

@ -0,0 +1,28 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class Vp8LMetadata
{
public int ColorCacheSize { get; set; }
public ColorCache ColorCache { get; set; }
public ColorCache SavedColorCache { get; set; }
public int HuffmanMask { get; set; }
public int HuffmanSubSampleBits { get; set; }
public int HuffmanXSize { get; set; }
public int[] HuffmanImage { get; set; }
public int NumHTreeGroups { get; set; }
public HTreeGroup[] HTreeGroups { get; set; }
public HuffmanCode[] HuffmanTables { get; set; }
}
}

6
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -86,11 +86,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static int NumDistanceCodes = 40;
public static int NumCodeLengthCodes = 19;
public static int LengthTableBits = 7;
public static int kCodeLengthLiterals = 16;
public static uint kCodeLengthLiterals = 16;
public static int kCodeLengthRepeatCode = 16;
@ -98,8 +96,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static int[] kCodeLengthRepeatOffsets = { 3, 3, 11 };
public static byte[] KCodeLengthCodeOrder = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
public static int[] kAlphabetSize = {
NumLiteralCodes + NumLengthCodes,
NumLiteralCodes, NumLiteralCodes, NumLiteralCodes,

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

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Memory;
@ -24,25 +23,51 @@ namespace SixLabors.ImageSharp.Formats.WebP
private readonly int imageDataSize;
private static int FIXED_TABLE_SIZE = 630 * 3 + 410;
private static readonly int BitsSpecialMarker = 0x100;
private static readonly int[] kTableSize =
private static readonly uint PackedNonLiteralCode = 0;
private static readonly int NumArgbCacheRows = 16;
private static readonly int FixedTableSize = (630 * 3) + 410;
private static readonly int[] KTableSize =
{
FIXED_TABLE_SIZE + 654,
FIXED_TABLE_SIZE + 656,
FIXED_TABLE_SIZE + 658,
FIXED_TABLE_SIZE + 662,
FIXED_TABLE_SIZE + 670,
FIXED_TABLE_SIZE + 686,
FIXED_TABLE_SIZE + 718,
FIXED_TABLE_SIZE + 782,
FIXED_TABLE_SIZE + 912,
FIXED_TABLE_SIZE + 1168,
FIXED_TABLE_SIZE + 1680,
FIXED_TABLE_SIZE + 2704
FixedTableSize + 654,
FixedTableSize + 656,
FixedTableSize + 658,
FixedTableSize + 662,
FixedTableSize + 670,
FixedTableSize + 686,
FixedTableSize + 718,
FixedTableSize + 782,
FixedTableSize + 912,
FixedTableSize + 1168,
FixedTableSize + 1680,
FixedTableSize + 2704
};
private static readonly byte[] kLiteralMap =
public static int NumCodeLengthCodes = 19;
public static byte[] KCodeLengthCodeOrder = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
private static readonly int CodeToPlaneCodes = 120;
private static readonly int[] KCodeToPlane =
{
0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70
};
private static readonly byte[] KLiteralMap =
{
0, 1, 1, 1, 0
};
@ -51,9 +76,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
this.bitReader = bitReader;
this.imageDataSize = imageDataSize;
// TODO: implement decoding. For simulating the decoding: skipping the chunk size bytes.
//stream.Skip(imageDataSize + 34); // TODO: Not sure why the additional data starts at offset +34 at the moment.
}
public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
@ -80,10 +102,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
colorCache.Colors = new List<uint>(hashSize);
colorCache.HashBits = (uint)colorCacheBits;
colorCache.HashShift = (uint)(32 - colorCacheBits);
colorCache.Init(colorCacheBits);
}
this.ReadHuffmanCodes(xsize, ysize, colorCacheBits);
Vp8LMetadata metadata = this.ReadHuffmanCodes(xsize, ysize, colorCacheBits);
var numBits = 0; // TODO: use huffmanSubsampleBits.
metadata.HuffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1;
metadata.ColorCacheSize = colorCacheSize;
int lastPixel = 0;
int row = lastPixel / width;
int col = lastPixel % width;
@ -91,10 +117,150 @@ namespace SixLabors.ImageSharp.Formats.WebP
int colorCacheLimit = lenCodeLimit + colorCacheSize;
bool decIsIncremental = false; // TODO: determine correct value for decIsIncremental
int nextSyncRow = decIsIncremental ? row : 1 << 24;
int mask = metadata.HuffmanMask;
HTreeGroup[] hTreeGroup = this.GetHtreeGroupForPos(metadata, col, row);
var pixelData = new byte[width * height * 4];
int totalPixels = width * height;
int decodedPixels = 0;
while (decodedPixels < totalPixels)
{
int code = 0;
if ((col & mask) == 0)
{
hTreeGroup = this.GetHtreeGroupForPos(metadata, col, row);
}
this.bitReader.FillBitWindow();
if (hTreeGroup[0].UsePackedTable)
{
code = (int)this.ReadPackedSymbols(hTreeGroup);
if (this.bitReader.IsEndOfStream())
{
break;
}
if (code == PackedNonLiteralCode)
{
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
continue;
}
}
else
{
this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]);
}
if (this.bitReader.IsEndOfStream())
{
break;
}
// Literal
if (code < WebPConstants.NumLiteralCodes)
{
if (hTreeGroup[0].IsTrivialLiteral)
{
long pixel = hTreeGroup[0].LiteralArb | (code << 8);
}
else
{
uint red = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Red]);
this.bitReader.FillBitWindow();
uint blue = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Blue]);
uint alpha = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Alpha]);
if (this.bitReader.IsEndOfStream())
{
break;
}
int pixelIdx = decodedPixels * 4;
pixelData[pixelIdx] = (byte)alpha;
pixelData[pixelIdx + 1] = (byte)red;
pixelData[pixelIdx + 2] = (byte)code;
pixelData[pixelIdx + 3] = (byte)blue;
}
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
}
else if (code < lenCodeLimit)
{
// Backward reference is used.
int lengthSym = code - WebPConstants.NumLiteralCodes;
int length = this.GetCopyLength(lengthSym);
uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]);
this.bitReader.FillBitWindow();
int distCode = this.GetCopyDistance((int)distSymbol);
int dist = this.PlaneCodeToDistance(width, distCode);
if (this.bitReader.IsEndOfStream())
{
break;
}
this.CopyBlock32b(pixelData, dist, length);
decodedPixels += length;
col += length;
while (col >= width)
{
col -= width;
++row;
}
if ((col & mask) != 0)
{
hTreeGroup = this.GetHtreeGroupForPos(metadata, col, row);
}
if (colorCache != null)
{
//while (lastCached < src)
//{
// colorCache.Insert(lastCached);
//}
}
}
else if (code < colorCacheLimit)
{
// Color cache should be used.
int key = code - lenCodeLimit;
/*while (lastCached < src)
{
colorCache.Insert(lastCached);
}*/
//pixelData = colorCache.Lookup(key);
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
}
else
{
// Error
}
}
}
private void ReadHuffmanCodes(int xsize, int ysize, int colorCacheBits, bool allowRecursion = true)
private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels)
{
++col;
decodedPixels++;
if (col >= width)
{
col = 0;
++row;
/*if (row <= lastRow && (row % NumArgbCacheRows == 0))
{
this.ProcessRowFunc(row);
}*/
if (colorCache != null)
{
/*while (lastCached < src)
{
VP8LColorCacheInsert(color_cache, *last_cached++);
}*/
}
}
}
private Vp8LMetadata ReadHuffmanCodes(int xsize, int ysize, int colorCacheBits, bool allowRecursion = true)
{
int maxAlphabetSize = 0;
int numHtreeGroups = 1;
@ -111,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int huffmanYSize = SubSampleSize(ysize, (int)huffmanPrecision);
// TODO: decode entropy image
return;
return new Vp8LMetadata();
}
// Find maximum alphabet size for the htree group.
@ -129,13 +295,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
int tableSize = kTableSize[colorCacheBits];
int tableSize = KTableSize[colorCacheBits];
var huffmanTables = new HuffmanCode[numHtreeGroups * tableSize];
var hTreeGroups = new HTreeGroup[numHtreeGroups];
Span<HuffmanCode> huffmanTable = huffmanTables.AsSpan();
for (int i = 0; i < numHtreeGroupsMax; i++)
{
hTreeGroups[i] = new HTreeGroup();
hTreeGroups[i] = new HTreeGroup(HuffmanUtils.HuffmanPackedTableSize);
HTreeGroup hTreeGroup = hTreeGroups[i];
int size;
int totalSize = 0;
@ -155,9 +321,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero");
}
hTreeGroup.HTree.Add(huffmanTable.ToArray());
if (isTrivialLiteral && kLiteralMap[j] == 1)
hTreeGroup.HTrees.Add(huffmanTable.ToArray());
if (isTrivialLiteral && KLiteralMap[j] == 1)
{
isTrivialLiteral = huffmanTable[0].BitsUsed == 0;
}
@ -185,24 +352,34 @@ namespace SixLabors.ImageSharp.Formats.WebP
hTreeGroup.IsTrivialCode = false;
if (isTrivialLiteral)
{
int red = hTreeGroup.HTree[(int)HuffIndex.Red].First().Value;
int blue = hTreeGroup.HTree[(int)HuffIndex.Blue].First().Value;
int green = hTreeGroup.HTree[(int)HuffIndex.Green].First().Value;
int alpha = hTreeGroup.HTree[(int)HuffIndex.Alpha].First().Value;
hTreeGroup.LiteralArb = (uint)((alpha << 24) | (red << 16) | blue);
uint red = hTreeGroup.HTrees[HuffIndex.Red].First().Value;
uint blue = hTreeGroup.HTrees[HuffIndex.Blue].First().Value;
uint green = hTreeGroup.HTrees[HuffIndex.Green].First().Value;
uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha].First().Value;
hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue;
if (totalSize == 0 && green < WebPConstants.NumLiteralCodes)
{
hTreeGroup.IsTrivialCode = true;
hTreeGroup.LiteralArb |= (uint)green << 8;
hTreeGroup.LiteralArb |= green << 8;
}
}
hTreeGroup.UsePackedTable = hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
if (hTreeGroup.UsePackedTable)
{
throw new NotImplementedException("use packed table is not implemented yet");
this.BuildPackedTable(hTreeGroup);
}
}
var metadata = new Vp8LMetadata()
{
// TODO: initialize huffman_image_
NumHTreeGroups = numHtreeGroups,
HTreeGroups = hTreeGroups,
HuffmanTables = huffmanTables,
};
return metadata;
}
private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span<HuffmanCode> table)
@ -239,16 +416,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
// (ii)Normal Code Length Code:
// 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.
var codeLengthCodeLengths = new int[WebPConstants.NumCodeLengthCodes];
var codeLengthCodeLengths = new int[NumCodeLengthCodes];
uint numCodes = this.bitReader.ReadBits(4) + 4;
if (numCodes > WebPConstants.NumCodeLengthCodes)
if (numCodes > NumCodeLengthCodes)
{
WebPThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value");
}
for (int i = 0; i < numCodes; i++)
{
codeLengthCodeLengths[WebPConstants.KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3);
codeLengthCodeLengths[KCodeLengthCodeOrder[i]] = (int)this.bitReader.ReadBits(3);
}
this.ReadHuffmanCodeLengths(table.ToArray(), codeLengthCodeLengths, alphabetSize, codeLengths);
@ -265,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int maxSymbol;
int symbol = 0;
int prevCodeLen = WebPConstants.DefaultCodeLength;
int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, WebPConstants.NumCodeLengthCodes);
int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes);
if (size is 0)
{
WebPThrowHelper.ThrowImageFormatException("Error building huffman table");
@ -283,7 +460,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
while (symbol < numSymbols)
{
int codeLen;
if (maxSymbol-- == 0)
{
break;
@ -294,19 +470,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
ulong idx = prefetchBits & 127;
HuffmanCode huffmanCode = table[idx];
this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed);
codeLen = huffmanCode.Value;
uint codeLen = huffmanCode.Value;
if (codeLen < WebPConstants.kCodeLengthLiterals)
{
codeLengths[symbol++] = codeLen;
codeLengths[symbol++] = (int)codeLen;
if (codeLen != 0)
{
prevCodeLen = codeLen;
prevCodeLen = (int)codeLen;
}
}
else
{
bool usePrev = codeLen == WebPConstants.kCodeLengthRepeatCode;
int slot = codeLen - WebPConstants.kCodeLengthLiterals;
uint slot = codeLen - WebPConstants.kCodeLengthLiterals;
int extraBits = WebPConstants.kCodeLengthExtraBits[slot];
int repeatOffset = WebPConstants.kCodeLengthRepeatOffsets[slot];
int repeat = (int)(this.bitReader.ReadBits(extraBits) + repeatOffset);
@ -391,5 +567,130 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
return (size + (1 << samplingBits) - 1) >> samplingBits;
}
/// <summary>
/// Decodes the next Huffman code from bit-stream.
/// FillBitWindow(br) needs to be called at minimum every second call
/// to ReadSymbol, in order to pre-fetch enough bits.
/// </summary>
private uint ReadSymbol(Span<HuffmanCode> table)
{
ulong val = this.bitReader.PrefetchBits();
Span<HuffmanCode> tableSpan = table.Slice((int)(val & HuffmanUtils.HuffmanTableMask));
int nBits = tableSpan[0].BitsUsed - HuffmanUtils.HuffmanTableBits;
if (nBits > 0)
{
this.bitReader.AdvanceBitPosition(HuffmanUtils.HuffmanTableBits);
val = this.bitReader.PrefetchBits();
tableSpan = tableSpan.Slice((int)tableSpan[0].Value);
tableSpan = tableSpan.Slice((int)val & ((1 << nBits) - 1));
}
this.bitReader.AdvanceBitPosition(tableSpan[0].BitsUsed);
return tableSpan[0].Value;
}
private uint ReadPackedSymbols(HTreeGroup[] group)
{
uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));
HuffmanCode code = group[0].PackedTable[val];
if (code.BitsUsed < BitsSpecialMarker)
{
this.bitReader.AdvanceBitPosition(code.BitsUsed);
// dest = (uint)code.Value;
return PackedNonLiteralCode;
}
this.bitReader.AdvanceBitPosition(code.BitsUsed - BitsSpecialMarker);
return code.Value;
}
private void CopyBlock32b(byte[] dest, int dist, int length)
{
}
private int GetCopyDistance(int distanceSymbol)
{
if (distanceSymbol < 4)
{
return distanceSymbol + 1;
}
int extraBits = (distanceSymbol - 2) >> 1;
int offset = (2 + (distanceSymbol & 1)) << extraBits;
return (int)(offset + this.bitReader.ReadBits(extraBits) + 1);
}
private int GetCopyLength(int lengthSymbol)
{
// Length and distance prefixes are encoded the same way.
return this.GetCopyDistance(lengthSymbol);
}
private int PlaneCodeToDistance(int xSize, int planeCode)
{
if (planeCode > CodeToPlaneCodes)
{
return planeCode - CodeToPlaneCodes;
}
int distCode = KCodeToPlane[planeCode - 1];
int yOffset = distCode >> 4;
int xOffset = 8 - (distCode & 0xf);
int dist = (yOffset * xSize) + xOffset;
return (dist >= 1) ? dist : 1; // dist<1 can happen if xsize is very small
}
private void BuildPackedTable(HTreeGroup hTreeGroup)
{
for (uint code = 0; code < HuffmanUtils.HuffmanPackedTableSize; ++code)
{
uint bits = code;
HuffmanCode huff = hTreeGroup.PackedTable[bits];
HuffmanCode hCode = hTreeGroup.HTrees[HuffIndex.Green][bits];
if (hCode.Value >= WebPConstants.NumLiteralCodes)
{
huff.BitsUsed = hCode.BitsUsed + BitsSpecialMarker;
huff.Value = hCode.Value;
}
else
{
huff.BitsUsed = 0;
huff.Value = 0;
bits >>= this.AccumulateHCode(hCode, 8, huff);
bits >>= this.AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Red][bits], 16, huff);
bits >>= this.AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Blue][bits], 0, huff);
bits >>= this.AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Alpha][bits], 24, huff);
}
}
}
private int AccumulateHCode(HuffmanCode hCode, int shift, HuffmanCode huff)
{
huff.BitsUsed += hCode.BitsUsed;
huff.Value |= hCode.Value << shift;
return hCode.BitsUsed;
}
private int GetMetaIndex(int[] image, int xSize, int bits, int x, int y)
{
if (bits == 0)
{
return 0;
}
return image[(xSize * (y >> bits)) + (x >> bits)];
}
private HTreeGroup[] GetHtreeGroupForPos(Vp8LMetadata metadata, int x, int y)
{
int metaIndex = this.GetMetaIndex(metadata.HuffmanImage, metadata.HuffmanXSize, metadata.HuffmanSubSampleBits, x, y);
return metadata.HTreeGroups.AsSpan(metaIndex).ToArray();
}
}
}

Loading…
Cancel
Save