image-in-browser
Version:
Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)
215 lines • 7.32 kB
JavaScript
import { VP8L } from './vp8l.js';
export class HuffmanTree {
get numNodes() {
return this._numNodes;
}
get isFull() {
return this._numNodes === this._maxNodes;
}
constructor(numLeaves = 0) {
this._lutBits = new Uint8Array(HuffmanTree.huffmanLut);
this._lutSymbol = new Int16Array(HuffmanTree.huffmanLut);
this._lutJump = new Int16Array(HuffmanTree.huffmanLut);
this._maxNodes = 0;
this._numNodes = 0;
this.init(numLeaves);
}
addSymbol(symbol, code, codeLength) {
let _codeLength = codeLength;
let step = HuffmanTree.huffmanLutBits;
let baseCode = 0;
let node = 0;
if (_codeLength <= HuffmanTree.huffmanLutBits) {
baseCode = this.reverseBitsShort(code, _codeLength);
for (let i = 0; i < 1 << (HuffmanTree.huffmanLutBits - _codeLength); ++i) {
const idx = baseCode | (i << _codeLength);
this._lutSymbol[idx] = symbol;
this._lutBits[idx] = _codeLength;
}
}
else {
baseCode = this.reverseBitsShort(code >>> (_codeLength - HuffmanTree.huffmanLutBits), HuffmanTree.huffmanLutBits);
}
while (_codeLength-- > 0) {
if (node >= this._maxNodes) {
return false;
}
if (this.nodeIsEmpty(node)) {
if (this.isFull) {
return false;
}
this.assignChildren(node);
}
else if (!this.nodeIsNotLeaf(node)) {
return false;
}
node += this.nodeChildren(node) + ((code >>> _codeLength) & 1);
if (--step === 0) {
this._lutJump[baseCode] = node;
}
}
if (this.nodeIsEmpty(node)) {
this.nodeSetChildren(node, 0);
}
else if (this.nodeIsNotLeaf(node)) {
return false;
}
this.nodeSetSymbol(node, symbol);
return true;
}
reverseBitsShort(bits, numBits) {
const v = (HuffmanTree.reversedBits[bits & 0xf] << 4) |
HuffmanTree.reversedBits[bits >>> 4];
return v >>> (8 - numBits);
}
nextNode(node, rightChild) {
return node + this.nodeChildren(node) + rightChild;
}
nodeSymbol(node) {
return this._tree[node << 1];
}
nodeSetSymbol(node, symbol) {
this._tree[node << 1] = symbol;
}
nodeChildren(node) {
return this._tree[(node << 1) + 1];
}
nodeSetChildren(node, children) {
this._tree[(node << 1) + 1] = children;
}
nodeIsNotLeaf(node) {
return this._tree[(node << 1) + 1] !== 0;
}
nodeIsEmpty(node) {
return this._tree[(node << 1) + 1] < 0;
}
assignChildren(node) {
const children = this._numNodes;
this.nodeSetChildren(node, children - node);
this._numNodes += 2;
this.nodeSetChildren(children, -1);
this.nodeSetChildren(children + 1, -1);
}
huffmanCodeLengthsToCodes(codeLengths, codeLengthsSize, huffCodes) {
let symbol = 0;
let codeLen = 0;
const codeLengthHist = new Int32Array(VP8L.maxAllowedCodeLength + 1);
let currCode = 0;
const nextCodes = new Int32Array(VP8L.maxAllowedCodeLength + 1);
let maxCodeLength = 0;
for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
if (codeLengths[symbol] > maxCodeLength) {
maxCodeLength = codeLengths[symbol];
}
}
if (maxCodeLength > VP8L.maxAllowedCodeLength) {
return false;
}
for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
++codeLengthHist[codeLengths[symbol]];
}
codeLengthHist[0] = 0;
currCode = 0;
nextCodes[0] = -1;
for (codeLen = 1; codeLen <= maxCodeLength; ++codeLen) {
currCode = (currCode + codeLengthHist[codeLen - 1]) << 1;
nextCodes[codeLen] = currCode;
}
for (symbol = 0; symbol < codeLengthsSize; ++symbol) {
if (codeLengths[symbol] > 0) {
huffCodes[symbol] = nextCodes[codeLengths[symbol]]++;
}
else {
huffCodes[symbol] = -1;
}
}
return true;
}
init(numLeaves) {
if (numLeaves === 0) {
return false;
}
this._maxNodes = (numLeaves << 1) - 1;
this._tree = new Int32Array(this._maxNodes << 1);
this._tree[1] = -1;
this._numNodes = 1;
this._lutBits.fill(255);
return true;
}
buildImplicit(codeLengths, codeLengthsSize) {
let numSymbols = 0;
let rootSymbol = 0;
for (let symbol = 0; symbol < codeLengthsSize; ++symbol) {
if (codeLengths[symbol] > 0) {
++numSymbols;
rootSymbol = symbol;
}
}
if (!this.init(numSymbols)) {
return false;
}
if (numSymbols === 1) {
const maxSymbol = codeLengthsSize;
if (rootSymbol < 0 || rootSymbol >= maxSymbol) {
return false;
}
return this.addSymbol(rootSymbol, 0, 0);
}
const codes = new Int32Array(codeLengthsSize);
if (!this.huffmanCodeLengthsToCodes(codeLengths, codeLengthsSize, codes)) {
return false;
}
for (let symbol = 0; symbol < codeLengthsSize; ++symbol) {
if (codeLengths[symbol] > 0) {
if (!this.addSymbol(symbol, codes[symbol], codeLengths[symbol])) {
return false;
}
}
}
return this.isFull;
}
buildExplicit(codeLengths, codes, symbols, maxSymbol, numSymbols) {
if (!this.init(numSymbols)) {
return false;
}
for (let i = 0; i < numSymbols; ++i) {
if (codes[i] !== -1) {
if (symbols[i] < 0 || symbols[i] >= maxSymbol) {
return this.isFull;
}
if (!this.addSymbol(symbols[i], codes[i], codeLengths[i])) {
return this.isFull;
}
}
}
return this.isFull;
}
readSymbol(br) {
let node = 0;
let bits = br.prefetchBits();
let newBitPos = br.bitPos;
const lutIx = bits & (HuffmanTree.huffmanLut - 1);
const lutBits = this._lutBits[lutIx];
if (lutBits <= HuffmanTree.huffmanLutBits) {
br.bitPos += lutBits;
return this._lutSymbol[lutIx];
}
node += this._lutJump[lutIx];
newBitPos += HuffmanTree.huffmanLutBits;
bits >>>= HuffmanTree.huffmanLutBits;
do {
node = this.nextNode(node, bits & 1);
bits >>>= 1;
++newBitPos;
} while (this.nodeIsNotLeaf(node));
br.bitPos = newBitPos;
return this.nodeSymbol(node);
}
}
HuffmanTree.huffmanLutBits = 7;
HuffmanTree.huffmanLut = 1 << HuffmanTree.huffmanLutBits;
HuffmanTree.reversedBits = [
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7,
0xf,
];
//# sourceMappingURL=webp-huffman-tree.js.map