7#include <QtQuick3DTextureHelpers/private/qssgexrcodecs_p.h>
9#include <QtCore/qendian.h>
10#include <QtCore/qnumeric.h>
11#include <QtCore/qvarlengtharray.h>
21constexpr int HufEncSize = UShortRange + 1;
22constexpr int HufDecBits = 14;
23constexpr int HufDecSize = 1 << HufDecBits;
24constexpr int HufDecMask = HufDecSize - 1;
26constexpr int ShortZeroCodeRun = 59;
27constexpr int LongZeroCodeRun = 63;
28constexpr int ShortestLongRun = 2 + LongZeroCodeRun - ShortZeroCodeRun;
29constexpr int MaxCodeLength = ShortZeroCodeRun - 1;
30constexpr int MaxDecodableCodeLength = 57;
35 BitReader(
const char *begin,
const char *end) : m_begin(begin), m_p(begin), m_end(end) { }
37 bool getBits(
int n, quint32 *out)
39 Q_ASSERT(n > 0 && n <= 24);
42 *out = quint32(peek(n));
50 while (m_lc < n && m_lc <= 56 && m_p < m_end) {
51 m_acc = (m_acc << 8) | quint8(*m_p++);
61 m_acc = (m_acc << 8) | quint8(*m_p++);
66 quint64 peek(
int n)
const
68 Q_ASSERT(n > 0 && n <= m_lc && m_lc <= 64);
69 return (m_acc >> (m_lc - n)) & ((quint64(1) << n) - 1);
72 void drop(
int n) { m_lc -= n; }
74 void dropTrailing(
int n)
80 int bitCount()
const {
return m_lc; }
81 quint64 accumulator()
const {
return m_acc; }
82 bool atEnd()
const {
return m_p >= m_end; }
83 qsizetype bytesConsumed()
const {
return m_p - m_begin; }
93quint16 buildReverseLut(
const quint8 *bitmap, quint16 *lut)
97 for (
int i = 0; i < UShortRange; ++i) {
98 if (i == 0 || (bitmap[i >> 3] & (1 << (i & 7))))
99 lut[k++] = quint16(i);
102 while (k < UShortRange)
107void hufCanonicalCodeTable(qint64 *hcode)
110 for (
int i = 0; i < HufEncSize; ++i) {
111 Q_ASSERT(hcode[i] >= 0 && hcode[i] <= MaxCodeLength);
116 for (
int i = MaxCodeLength; i > 0; --i) {
117 const qint64 nc = (c + n[i]) >> 1;
122 for (
int i = 0; i < HufEncSize; ++i) {
123 const int l =
int(hcode[i]);
125 hcode[i] = l | (n[l]++ << 6);
129bool hufUnpackEncTable(BitReader &br,
int im,
int iM, qint64 *hcode, QString *error)
131 memset(hcode, 0,
sizeof(qint64) * HufEncSize);
133 for (
int i = im; i <= iM; ++i) {
135 if (!br.getBits(6, &l)) {
136 *error = QStringLiteral(
"truncated PIZ code length table");
140 if (l == quint32(LongZeroCodeRun)) {
142 if (!br.getBits(8, &n)) {
143 *error = QStringLiteral(
"truncated PIZ code length table");
146 const int zerun =
int(n) + ShortestLongRun;
147 if (i + zerun > iM + 1) {
148 *error = QStringLiteral(
"PIZ code length run overruns the symbol range");
151 for (
int k = 0; k < zerun; ++k)
154 }
else if (l >= quint32(ShortZeroCodeRun)) {
155 const int zerun =
int(l) - ShortZeroCodeRun + 2;
156 if (i + zerun > iM + 1) {
157 *error = QStringLiteral(
"PIZ code length run overruns the symbol range");
160 for (
int k = 0; k < zerun; ++k)
168 hufCanonicalCodeTable(hcode);
172bool hufBuildDecTable(
const qint64 *hcode,
int im,
int iM, PizScratch *scratch, QString *error)
174 std::vector<HufDecEntry> &dec = scratch->dec;
175 std::vector<quint32> &longSymbols = scratch->longSymbols;
176 std::fill(dec.begin(), dec.end(), HufDecEntry {});
178 for (
int i = im; i <= iM; ++i) {
179 const qint64 hc = hcode[i];
180 const int l =
int(hc & 63);
183 if (l > MaxDecodableCodeLength) {
184 *error = QStringLiteral(
"invalid PIZ code length");
187 const quint64 code = quint64(hc >> 6);
189 *error = QStringLiteral(
"invalid PIZ code table");
193 if (l > HufDecBits) {
194 const quint32 idx = quint32(code >> (l - HufDecBits));
195 Q_ASSERT(idx < quint32(HufDecSize));
196 if (dec[idx].len != 0) {
197 *error = QStringLiteral(
"conflicting PIZ codes");
200 ++dec[idx].longCount;
202 const quint32 base = quint32(code << (HufDecBits - l));
203 const quint32 count = 1u << (HufDecBits - l);
204 Q_ASSERT(quint64(base) + count <= quint64(HufDecSize));
205 for (quint32 j = 0; j < count; ++j) {
206 HufDecEntry &e = dec[base + j];
207 if (e.len != 0 || e.longCount != 0) {
208 *error = QStringLiteral(
"conflicting PIZ codes");
218 for (HufDecEntry &e : dec) {
220 total += e.longCount;
222 longSymbols.assign(total, 0);
224 std::vector<quint32> &filled = scratch->longSymbolsFilled;
225 filled.assign(dec.size(), 0);
226 for (
int i = im; i <= iM; ++i) {
227 const qint64 hc = hcode[i];
228 const int l =
int(hc & 63);
231 const quint64 code = quint64(hc >> 6);
232 const quint32 idx = quint32(code >> (l - HufDecBits));
233 longSymbols[dec[idx].longOfs + filled[idx]++] = quint32(i);
239bool hufDecode(
const qint64 *hcode,
240 const std::vector<HufDecEntry> &dec,
241 const std::vector<quint32> &longSymbols,
248 const qsizetype byteCount = (nBits + 7) / 8;
249 Q_ASSERT(byteCount <= bits.size());
250 BitReader br(bits.data(), bits.data() + byteCount);
252 quint16 *o = out.data();
253 qsizetype produced = 0;
255 const auto emitSymbol = [&](quint32 sym) ->
bool {
256 if (sym == quint32(rlc)) {
258 *error = QStringLiteral(
"truncated PIZ run length");
261 const quint32 count = quint32(br.peek(8));
264 *error = QStringLiteral(
"PIZ run with nothing to repeat");
267 if (count > quint32(out.size() - produced)) {
268 *error = QStringLiteral(
"PIZ run overruns the chunk");
271 const quint16 s = o[produced - 1];
272 for (quint32 k = 0; k < count; ++k)
276 if (produced >= out.size()) {
277 *error = QStringLiteral(
"PIZ stream overruns the chunk");
281 *error = QStringLiteral(
"invalid PIZ symbol");
284 o[produced++] = quint16(sym);
288 while (!br.atEnd()) {
291 while (br.bitCount() >= HufDecBits) {
292 const HufDecEntry &e = dec[br.peek(HufDecBits) & HufDecMask];
295 if (!emitSymbol(e.lit))
298 if (e.longCount == 0) {
299 *error = QStringLiteral(
"invalid PIZ code");
303 for (quint32 j = 0; j < e.longCount; ++j) {
304 const quint32 sym = longSymbols[e.longOfs + j];
305 const int l =
int(hcode[sym] & 63);
308 if (quint64(hcode[sym] >> 6) == br.peek(l)) {
310 if (!emitSymbol(sym))
317 *error = QStringLiteral(
"invalid PIZ code");
325 br.dropTrailing(
int((8 - (nBits & 7)) & 7));
326 while (br.bitCount() > 0) {
327 const int lc = br.bitCount();
328 Q_ASSERT(lc < HufDecBits);
329 const HufDecEntry &e = dec[(br.accumulator() << (HufDecBits - lc)) & HufDecMask];
331 *error = QStringLiteral(
"invalid PIZ code in the trailing bits");
335 if (!emitSymbol(e.lit))
339 if (produced != out.size()) {
340 *error = QStringLiteral(
"PIZ stream does not fill the chunk");
346inline void wdec14(quint16 l, quint16 h, quint16 *a, quint16 *b)
348 const int ls = qint16(l);
349 const int hi = qint16(h);
350 const int ai = ls + (hi & 1) + (hi >> 1);
351 *a = quint16(qint16(ai));
352 *b = quint16(qint16(ai - hi));
355inline void wdec16(quint16 l, quint16 h, quint16 *a, quint16 *b)
357 constexpr int ModMask = 0xffff;
358 constexpr int AOffset = 1 << 15;
361 const int bb = (m - (d >> 1)) & ModMask;
362 const int aa = (d + bb - AOffset) & ModMask;
376void wav2Decode(QSpan<quint16> base, qint64 offset, qint64 nx, qint64 ox, qint64 ny, qint64 oy, quint16 maxValue)
378 quint16 *in = base.data();
379 const bool w14 = maxValue < (1 << 14);
380 const qint64 n = qMin(nx, ny);
390 const qint64 oy1 = oy * p;
391 const qint64 oy2 = oy * p2;
392 const qint64 ox1 = ox * p;
393 const qint64 ox2 = ox * p2;
396 const qint64 ey = offset + oy * (ny - p2);
397 quint16 i00 = 0, i01 = 0, i10 = 0, i11 = 0;
399 for (; py <= ey; py += oy2) {
401 const qint64 ex = py + ox * (nx - p2);
402 for (; px <= ex; px += ox2) {
403 const qint64 p01 = px + ox1;
404 const qint64 p10 = px + oy1;
405 const qint64 p11 = p10 + ox1;
406 Q_ASSERT(px >= 0 && p11 < base.size());
408 wdec14(in[px], in[p10], &i00, &i10);
409 wdec14(in[p01], in[p11], &i01, &i11);
410 wdec14(i00, i01, &in[px], &in[p01]);
411 wdec14(i10, i11, &in[p10], &in[p11]);
413 wdec16(in[px], in[p10], &i00, &i10);
414 wdec16(in[p01], in[p11], &i01, &i11);
415 wdec16(i00, i01, &in[px], &in[p01]);
416 wdec16(i10, i11, &in[p10], &in[p11]);
421 const qint64 p10 = px + oy1;
422 Q_ASSERT(px >= 0 && p10 < base.size());
424 wdec14(in[px], in[p10], &i00, &in[p10]);
426 wdec16(in[px], in[p10], &i00, &in[p10]);
433 const qint64 ex = py + ox * (nx - p2);
434 for (; px <= ex; px += ox2) {
435 const qint64 p01 = px + ox1;
436 Q_ASSERT(px >= 0 && p01 < base.size());
438 wdec14(in[px], in[p01], &i00, &in[p01]);
440 wdec16(in[px], in[p01], &i00, &in[p01]);
450bool hufUncompress(QByteArrayView in, QSpan<quint16> out, PizScratch *scratch, QString *error)
452 if (in.size() < 20) {
453 *error = QStringLiteral(
"truncated PIZ Huffman header");
457 const quint32 im = qFromLittleEndian<quint32>(in.data());
458 const quint32 iM = qFromLittleEndian<quint32>(in.data() + 4);
460 const quint32 nBits = qFromLittleEndian<quint32>(in.data() + 12);
462 if (im > quint32(HufEncSize - 1) || iM > quint32(HufEncSize - 1) || im > iM) {
463 *error = QStringLiteral(
"invalid PIZ symbol range");
467 BitReader tableReader(in.data() + 20, in.data() + in.size());
468 if (!hufUnpackEncTable(tableReader,
int(im),
int(iM), scratch->hcode.data(), error))
471 if (!hufBuildDecTable(scratch->hcode.data(),
int(im),
int(iM), scratch, error))
474 const qsizetype dataStart = 20 + tableReader.bytesConsumed();
475 const qsizetype avail = in.size() - dataStart;
476 if (avail < 0 || qint64(nBits) > qint64(avail) * 8) {
477 *error = QStringLiteral(
"PIZ bit stream extends past the chunk");
482 return hufDecode(scratch->hcode.data(), scratch->dec, scratch->longSymbols, in.sliced(dataStart), qint64(nBits),
int(iM), out, error);
485bool pizDecode(QByteArrayView in, QSpan<
char> out,
const QList<PizChannel> &channels, qint64 nx, qint64 ny, qint64 bytesPerPixel, PizScratch *scratch, QString *error)
487 if (out.size() % 2 != 0) {
488 *error = QStringLiteral(
"PIZ chunk size is not a whole number of 16-bit words");
494 *error = QStringLiteral(
"truncated PIZ chunk");
497 const quint16 minNonZero = qFromLittleEndian<quint16>(in.data());
498 const quint16 maxNonZero = qFromLittleEndian<quint16>(in.data() + 2);
501 scratch->bitmap.fill(0);
502 if (minNonZero <= maxNonZero) {
503 if (maxNonZero >= BitmapSize) {
504 *error = QStringLiteral(
"PIZ bitmap range is out of bounds");
507 const qsizetype n = qsizetype(maxNonZero) - minNonZero + 1;
508 if (in.size() - pos < n) {
509 *error = QStringLiteral(
"truncated PIZ bitmap");
512 memcpy(scratch->bitmap.data() + minNonZero, in.data() + pos, size_t(n));
514 }
else if (!(minNonZero == BitmapSize - 1 && maxNonZero == 0)) {
516 *error = QStringLiteral(
"invalid PIZ bitmap range");
520 const quint16 maxValue = buildReverseLut(scratch->bitmap.data(), scratch->lut.data());
522 if (in.size() - pos < 4) {
523 *error = QStringLiteral(
"truncated PIZ chunk");
526 const qint64 huffLength = qint64(qFromLittleEndian<qint32>(in.data() + pos));
528 if (huffLength < 0 || huffLength > in.size() - pos) {
529 *error = QStringLiteral(
"PIZ Huffman block extends past the chunk");
533 const qint64 totalWords = out.size() / 2;
534 QVarLengthArray<qint64, 8> wordOffset(channels.size() + 1);
536 for (qsizetype c = 0; c < channels.size(); ++c) {
537 qint64 planeWords = 0;
538 if (qMulOverflow(nx, ny, &planeWords) || qMulOverflow(planeWords, channels[c].wordsPerSample, &planeWords)
539 || qAddOverflow(wordOffset[c], planeWords, &wordOffset[c + 1])) {
540 *error = QStringLiteral(
"PIZ channel layout is too large");
544 if (wordOffset[channels.size()] != totalWords) {
545 *error = QStringLiteral(
"PIZ channel layout does not match the chunk size");
549 scratch->planar.assign(size_t(totalWords), 0);
550 QSpan<quint16> planar(scratch->planar.data(), qsizetype(totalWords));
552 if (!hufUncompress(in.sliced(pos, huffLength), planar, scratch, error))
555 for (qsizetype c = 0; c < channels.size(); ++c) {
556 const qint64 words = channels[c].wordsPerSample;
557 for (qint64 j = 0; j < words; ++j)
558 wav2Decode(planar, wordOffset[c] + j, nx, words, ny, nx * words, maxValue);
562 const quint16 *lut = scratch->lut.data();
563 for (qint64 i = 0; i < totalWords; ++i)
564 planar[i] = qToLittleEndian(lut[planar[i]]);
566 const qint64 rowStride = nx * bytesPerPixel;
567 for (qint64 r = 0; r < ny; ++r) {
568 for (qsizetype c = 0; c < channels.size(); ++c) {
569 const qint64 words = channels[c].wordsPerSample;
570 const qint64 runWords = nx * words;
571 char *dst = out.data() + r * rowStride + channels[c].byteOffsetInPixel * nx;
572 const quint16 *src = planar.data() + wordOffset[c] + r * runWords;
573 Q_ASSERT(dst + runWords * 2 <= out.data() + out.size());
574 memcpy(dst, src, size_t(runWords) * 2);
bool pizDecode(QByteArrayView in, QSpan< char > out, const QList< PizChannel > &channels, qint64 nx, qint64 ny, qint64 bytesPerPixel, PizScratch *scratch, QString *error)
void wav2Decode(QSpan< quint16 > base, qint64 offset, qint64 nx, qint64 ox, qint64 ny, qint64 oy, quint16 maxValue)
bool hufUncompress(QByteArrayView in, QSpan< quint16 > out, PizScratch *scratch, QString *error)