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