UNPKG

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
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