mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 216 additions and 102 deletions
@ -0,0 +1,49 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.WebP |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Huffman table group.
|
||||
|
/// Includes special handling for the following cases:
|
||||
|
/// - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN)
|
||||
|
/// - is_trivial_code: only 1 code (no bit is read from bitstream)
|
||||
|
/// - use_packed_table: few enough literal symbols, so all the bit codes
|
||||
|
/// can fit into a small look-up table packed_table[]
|
||||
|
/// The common literal base, if applicable, is stored in 'literal_arb'.
|
||||
|
/// </summary>
|
||||
|
internal class HTreeGroup |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This has a maximum of HuffmanCodesPerMetaCode (5) entrys.
|
||||
|
/// </summary>
|
||||
|
public List<HuffmanCode> HTree { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// True, if huffman trees for Red, Blue & Alpha Symbols are trivial (have a single code).
|
||||
|
/// </summary>
|
||||
|
public bool IsTrivialLiteral { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If is_trivial_literal is true, this is the ARGB value of the pixel, with Green channel being set to zero.
|
||||
|
/// </summary>
|
||||
|
public int LiteralArb { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// True if is_trivial_literal with only one code.
|
||||
|
/// </summary>
|
||||
|
public bool IsTrivialCode { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// use packed table below for short literal code
|
||||
|
/// </summary>
|
||||
|
public bool UsePackedTable { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Table mapping input bits to a packed values, or escape case to literal code.
|
||||
|
/// </summary>
|
||||
|
public HuffmanCode PackedTable { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.WebP |
||||
|
{ |
||||
|
internal static class HuffmanUtils |
||||
|
{ |
||||
|
public static List<HuffmanCode> BuildHuffmanTable(HuffmanCode[] table, int rootBits, int[] codeLengths, int codeLengthsSize) |
||||
|
{ |
||||
|
Guard.MustBeGreaterThan(rootBits, 0, nameof(rootBits)); |
||||
|
Guard.NotNull(codeLengths, nameof(codeLengths)); |
||||
|
Guard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize)); |
||||
|
|
||||
|
// TODO: not sure yet howto store the codes properly
|
||||
|
var huffmanCodes = new List<HuffmanCode>(); |
||||
|
|
||||
|
// sorted[code_lengths_size] is a pre-allocated array for sorting symbols by code length.
|
||||
|
var sorted = new int[codeLengthsSize]; |
||||
|
// total size root table + 2nd level table
|
||||
|
int totalSize = 1 << rootBits; |
||||
|
// current code length
|
||||
|
int len; |
||||
|
// symbol index in original or sorted table
|
||||
|
int symbol; |
||||
|
// number of codes of each length:
|
||||
|
var count = new int[WebPConstants.MaxAllowedCodeLength + 1]; |
||||
|
// offsets in sorted table for each length
|
||||
|
var offset = new int[WebPConstants.MaxAllowedCodeLength + 1]; |
||||
|
|
||||
|
// Build histogram of code lengths.
|
||||
|
for (symbol = 0; symbol < codeLengthsSize; ++symbol) |
||||
|
{ |
||||
|
if (codeLengths[symbol] > WebPConstants.MaxAllowedCodeLength) |
||||
|
{ |
||||
|
return huffmanCodes; |
||||
|
} |
||||
|
|
||||
|
++count[codeLengths[symbol]]; |
||||
|
} |
||||
|
|
||||
|
// Generate offsets into sorted symbol table by code length.
|
||||
|
offset[1] = 0; |
||||
|
for (len = 1; len < WebPConstants.MaxAllowedCodeLength; ++len) |
||||
|
{ |
||||
|
if (count[len] > (1 << len)) |
||||
|
{ |
||||
|
return huffmanCodes; |
||||
|
} |
||||
|
|
||||
|
offset[len + 1] = offset[len] + count[len]; |
||||
|
} |
||||
|
|
||||
|
// Sort symbols by length, by symbol order within each length.
|
||||
|
for (symbol = 0; symbol < codeLengthsSize; ++symbol) |
||||
|
{ |
||||
|
int symbolCodeLength = codeLengths[symbol]; |
||||
|
if (codeLengths[symbol] > 0) |
||||
|
{ |
||||
|
sorted[offset[symbolCodeLength]++] = symbol; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Special case code with only one value.
|
||||
|
if (offset[WebPConstants.MaxAllowedCodeLength] is 1) |
||||
|
{ |
||||
|
var huffmanCode = new HuffmanCode() |
||||
|
{ |
||||
|
BitsUsed = 0, |
||||
|
Value = sorted[0] |
||||
|
}; |
||||
|
huffmanCodes.Add(huffmanCode); |
||||
|
|
||||
|
return huffmanCodes; |
||||
|
} |
||||
|
|
||||
|
int step; // step size to replicate values in current table
|
||||
|
int low = -1; // low bits for current root entry
|
||||
|
int mask = totalSize - 1; // mask for low bits
|
||||
|
int key = 0; // reversed prefix code
|
||||
|
int numNodes = 1; // number of Huffman tree nodes
|
||||
|
int numOpen = 1; // number of open branches in current tree level
|
||||
|
int tableBits = rootBits; // key length of current table
|
||||
|
int tableSize = 1 << tableBits; // size of current table
|
||||
|
symbol = 0; |
||||
|
// Fill in root table.
|
||||
|
for (len = 1, step = 2; len <= rootBits; ++len, step <<= 1) |
||||
|
{ |
||||
|
numOpen <<= 1; |
||||
|
numNodes += numOpen; |
||||
|
numOpen -= count[len]; |
||||
|
if (numOpen < 0) |
||||
|
{ |
||||
|
return huffmanCodes; |
||||
|
} |
||||
|
|
||||
|
for (; count[len] > 0; --count[len]) |
||||
|
{ |
||||
|
var huffmanCode = new HuffmanCode() |
||||
|
{ |
||||
|
BitsUsed = len, |
||||
|
Value = sorted[symbol++] |
||||
|
}; |
||||
|
huffmanCodes.Add(huffmanCode); |
||||
|
ReplicateValue(table, step, tableSize, huffmanCode); |
||||
|
key = GetNextKey(key, len); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return huffmanCodes; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stores code in table[0], table[step], table[2*step], ..., table[end].
|
||||
|
/// Assumes that end is an integer multiple of step.
|
||||
|
/// </summary>
|
||||
|
private static void ReplicateValue(HuffmanCode[] table, int step, int end, HuffmanCode code) |
||||
|
{ |
||||
|
Guard.IsTrue(end % step == 0, nameof(end), "end must be a multiple of step"); |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
end -= step; |
||||
|
table[end] = code; |
||||
|
} |
||||
|
while (end > 0); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the
|
||||
|
/// bit-wise reversal of the len least significant bits of key.
|
||||
|
/// </summary>
|
||||
|
private static int GetNextKey(int key, int len) |
||||
|
{ |
||||
|
int step = 1 << (len - 1); |
||||
|
while ((key & step) != 0) |
||||
|
{ |
||||
|
step >>= 1; |
||||
|
} |
||||
|
|
||||
|
return step != 0 ? (key & (step - 1)) + step : key; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue