blob: 94d76e471d44870c1befbdc5fbade1f1ed3a9ac3 [file] [log] [blame]
import 'dart:typed_data';
import '../../image_exception.dart';
import '../../util/input_buffer.dart';
class ExrHuffman {
static void uncompress(
InputBuffer compressed, int nCompressed, Uint16List raw, int nRaw) {
if (nCompressed == 0) {
if (nRaw != 0) {
throw new ImageException('Incomplete huffman data');
}
return;
}
int start = compressed.offset;
int im = compressed.readUint32();
int iM = compressed.readUint32();
compressed.skip(4); // tableLength
int nBits = compressed.readUint32();
if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) {
throw new ImageException('Invalid huffman table size');
}
compressed.skip(4);
List<int> freq = List<int>(HUF_ENCSIZE);
freq.fillRange(0, HUF_ENCSIZE, 0);
List<ExrHufDec> hdec = List<ExrHufDec>(HUF_DECSIZE);
for (int i = 0; i < HUF_DECSIZE; ++i) {
hdec[i] = ExrHufDec();
}
unpackEncTable(compressed, nCompressed - 20, im, iM, freq);
if (nBits > 8 * (nCompressed - (compressed.offset - start))) {
throw new ImageException("Error in header for Huffman-encoded data "
"(invalid number of bits).");
}
buildDecTable(freq, im, iM, hdec);
decode(freq, hdec, compressed, nBits, iM, nRaw, raw);
}
static void decode(List<int> hcode, List<ExrHufDec> hdecod, InputBuffer input,
int ni, int rlc, int no, Uint16List out) {
List<int> c_lc = [0, 0];
int ie = input.offset + (ni + 7) ~/ 8; // input byte size
int oi = 0;
// Loop on input bytes
while (input.offset < ie) {
getChar(c_lc, input);
// Access decoding table
while (c_lc[1] >= HUF_DECBITS) {
ExrHufDec pl =
hdecod[(c_lc[0] >> (c_lc[1] - HUF_DECBITS)) & HUF_DECMASK];
if (pl.len != 0) {
// Get short code
c_lc[1] -= pl.len;
oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
} else {
if (pl.p == null) {
throw new ImageException("Error in Huffman-encoded data "
"(invalid code).");
}
// Search long code
int j;
for (j = 0; j < pl.lit; j++) {
int l = hufLength(hcode[pl.p[j]]);
while (c_lc[1] < l && input.offset < ie) {
// get more bits
getChar(c_lc, input);
}
if (c_lc[1] >= l) {
if (hufCode(hcode[pl.p[j]]) ==
((c_lc[0] >> (c_lc[1] - l)) & ((1 << l) - 1))) {
// Found : get long code
c_lc[1] -= l;
oi = getCode(pl.p[j], rlc, c_lc, input, out, oi, no);
break;
}
}
}
if (j == pl.lit) {
throw new ImageException("Error in Huffman-encoded data "
"(invalid code).");
}
}
}
}
// Get remaining (short) codes
int i = (8 - ni) & 7;
c_lc[0] >>= i;
c_lc[1] -= i;
while (c_lc[1] > 0) {
ExrHufDec pl = hdecod[(c_lc[0] << (HUF_DECBITS - c_lc[1])) & HUF_DECMASK];
if (pl.len != 0) {
c_lc[1] -= pl.len;
oi = getCode(pl.lit, rlc, c_lc, input, out, oi, no);
} else {
throw new ImageException("Error in Huffman-encoded data "
"(invalid code).");
}
}
if (oi != no) {
throw new ImageException("Error in Huffman-encoded data "
"(decoded data are shorter than expected).");
}
}
static int getCode(int po, int rlc, List<int> c_lc, InputBuffer input,
Uint16List out, int oi, int oe) {
if (po == rlc) {
if (c_lc[1] < 8) {
getChar(c_lc, input);
}
c_lc[1] -= 8;
int cs = (c_lc[0] >> c_lc[1]) & 0xff;
if (oi + cs > oe) {
throw new ImageException("Error in Huffman-encoded data "
"(decoded data are longer than expected).");
}
int s = out[oi - 1];
while (cs-- > 0) {
out[oi++] = s;
}
} else if (oi < oe) {
out[oi++] = po;
} else {
throw new ImageException("Error in Huffman-encoded data "
"(decoded data are longer than expected).");
}
return oi;
}
static void buildDecTable(
List<int> hcode, int im, int iM, List<ExrHufDec> hdecod) {
// Init hashtable & loop on all codes.
// Assumes that hufClearDecTable(hdecod) has already been called.
for (; im <= iM; im++) {
int c = hufCode(hcode[im]);
int l = hufLength(hcode[im]);
if (c >> l != 0) {
// Error: c is supposed to be an l-bit code,
// but c contains a value that is greater
// than the largest l-bit number.
throw new ImageException("Error in Huffman-encoded data "
"(invalid code table entry).");
}
if (l > HUF_DECBITS) {
// Long code: add a secondary entry
ExrHufDec pl = hdecod[(c >> (l - HUF_DECBITS))];
if (pl.len != 0) {
// Error: a short code has already
// been stored in table entry *pl.
throw new ImageException("Error in Huffman-encoded data "
"(invalid code table entry).");
}
pl.lit++;
if (pl.p != null) {
List<int> p = pl.p;
pl.p = List<int>(pl.lit);
for (int i = 0; i < pl.lit - 1; ++i) {
pl.p[i] = p[i];
}
} else {
pl.p = [0];
}
pl.p[pl.lit - 1] = im;
} else if (l != 0) {
// Short code: init all primary entries
int pi = (c << (HUF_DECBITS - l));
ExrHufDec pl = hdecod[pi];
for (int i = 1 << (HUF_DECBITS - l); i > 0; i--, pi++) {
pl = hdecod[pi];
if (pl.len != 0 || pl.p != null) {
// Error: a short code or a long code has
// already been stored in table entry *pl.
throw new ImageException("Error in Huffman-encoded data "
"(invalid code table entry).");
}
pl.len = l;
pl.lit = im;
}
}
}
}
static void unpackEncTable(
InputBuffer p, int ni, int im, int iM, List<int> hcode) {
int pcode = p.offset;
List<int> c_lc = [0, 0];
for (; im <= iM; im++) {
if (p.offset - pcode > ni) {
throw new ImageException("Error in Huffman-encoded data "
"(unexpected end of code table data).");
}
int l = hcode[im] = getBits(6, c_lc, p); // code length
if (l == LONG_ZEROCODE_RUN) {
if (p.offset - pcode > ni) {
throw new ImageException("Error in Huffman-encoded data "
"(unexpected end of code table data).");
}
int zerun = getBits(8, c_lc, p) + SHORTEST_LONG_RUN;
if (im + zerun > iM + 1) {
throw new ImageException("Error in Huffman-encoded data "
"(code table is longer than expected).");
}
while (zerun-- != 0) {
hcode[im++] = 0;
}
im--;
} else if (l >= SHORT_ZEROCODE_RUN) {
int zerun = l - SHORT_ZEROCODE_RUN + 2;
if (im + zerun > iM + 1) {
throw new ImageException("Error in Huffman-encoded data "
"(code table is longer than expected).");
}
while (zerun-- != 0) {
hcode[im++] = 0;
}
im--;
}
}
canonicalCodeTable(hcode);
}
static int hufLength(int code) => code & 63;
static int hufCode(int code) => code >> 6;
static void canonicalCodeTable(List<int> hcode) {
List<int> n = List<int>(59);
n.fillRange(0, 59, 0);
// For each i from 0 through 58, count the
// number of different codes of length i, and
// store the count in n[i].
for (int i = 0; i < HUF_ENCSIZE; ++i) {
n[hcode[i]] += 1;
}
// For each i from 58 through 1, compute the
// numerically lowest code with length i, and
// store that code in n[i].
int c = 0;
for (int i = 58; i > 0; --i) {
int nc = ((c + n[i]) >> 1);
n[i] = c;
c = nc;
}
// hcode[i] contains the length, l, of the
// code for symbol i. Assign the next available
// code of length l to the symbol and store both
// l and the code in hcode[i].
for (int i = 0; i < HUF_ENCSIZE; ++i) {
int l = hcode[i];
if (l > 0) {
hcode[i] = l | (n[l]++ << 6);
}
}
}
static void getChar(List<int> c_lc, InputBuffer input) {
c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
c_lc[1] = (c_lc[1] + 8) & MASK_32;
}
static int getBits(int nBits, List<int> c_lc, InputBuffer input) {
while (c_lc[1] < nBits) {
c_lc[0] = ((c_lc[0] << 8) | input.readByte()) & MASK_64;
c_lc[1] = (c_lc[1] + 8) & MASK_32;
}
c_lc[1] -= nBits;
return (c_lc[0] >> c_lc[1]) & ((1 << nBits) - 1);
}
static const int MASK_32 = (1 << 32) - 1;
static const int MASK_64 = (1 << 64) - 1;
static const int HUF_ENCBITS = 16; // literal (value) bit length
static const int HUF_DECBITS = 14; // decoding bit size (>= 8)
static const int HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size
static const int HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
static const int HUF_DECMASK = HUF_DECSIZE - 1;
static const int SHORT_ZEROCODE_RUN = 59;
static const int LONG_ZEROCODE_RUN = 63;
static const int SHORTEST_LONG_RUN =
2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
static const int LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
}
class ExrHufDec {
int len = 0;
int lit = 0;
List<int> p;
}