Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgexrcodecs.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <QtQuick3DTextureHelpers/private/qssgexrcodecs_p.h>
6
7#include <QtCore/qnumeric.h>
8
9#include <zlib.h>
10
11#include <cstring>
12
13QT_BEGIN_NAMESPACE
14
15namespace QSSGExr {
16
17bool undoPredictorAndDeinterleave(QSpan<char> out, QSpan<char> scratch, QString *error)
18{
19 const qsizetype n = out.size();
20 if (scratch.size() < n) {
21 *error = QStringLiteral("predictor scratch is smaller than the chunk");
22 return false;
23 }
24 if (n <= 0)
25 return true;
26
27 quint8 *t = reinterpret_cast<quint8 *>(scratch.data());
28 for (qsizetype i = 1; i < n; ++i)
29 t[i] = quint8(quint32(t[i - 1]) + quint32(t[i]) - 128u);
30
31 const qsizetype half = (n + 1) / 2;
32 const quint8 *t1 = t;
33 const quint8 *t2 = t + half;
34 quint8 *s = reinterpret_cast<quint8 *>(out.data());
35 for (qsizetype i = 0; i < n; i += 2) {
36 s[i] = *t1++;
37 if (i + 1 < n)
38 s[i + 1] = *t2++;
39 }
40
41 return true;
42}
43
44bool inflateExact(QByteArrayView in, QSpan<char> out, QString *error)
45{
46 if (in.isEmpty() || out.isEmpty()) {
47 *error = QStringLiteral("empty compressed chunk");
48 return false;
49 }
50 if (quint64(in.size()) > std::numeric_limits<uInt>::max() || quint64(out.size()) > std::numeric_limits<uInt>::max()) {
51 *error = QStringLiteral("compressed chunk is too large");
52 return false;
53 }
54
55 z_stream zs = {};
56 if (inflateInit(&zs) != Z_OK) {
57 *error = QStringLiteral("could not initialise zlib");
58 return false;
59 }
60
61 zs.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(in.data()));
62 zs.avail_in = uInt(in.size());
63 zs.next_out = reinterpret_cast<Bytef *>(out.data());
64 zs.avail_out = uInt(out.size());
65
66 const int ret = inflate(&zs, Z_FINISH);
67 const bool complete = ret == Z_STREAM_END;
68 const bool exact = zs.avail_out == 0 && zs.avail_in == 0;
69 inflateEnd(&zs);
70
71 if (!complete || !exact) {
72 *error = QStringLiteral("corrupt zlib stream in chunk");
73 return false;
74 }
75 return true;
76}
77
78bool rleDecode(QByteArrayView in, QSpan<char> out, QString *error)
79{
80 const qint8 *ip = reinterpret_cast<const qint8 *>(in.data());
81 const qint8 *iend = ip + in.size();
82 char *op = out.data();
83 qsizetype produced = 0;
84
85 while (ip < iend) {
86 const qint8 tag = *ip++;
87 if (tag < 0) {
88 const qsizetype count = -qsizetype(tag);
89 if (count > iend - ip || count > out.size() - produced) {
90 *error = QStringLiteral("corrupt RLE stream in chunk");
91 return false;
92 }
93 memcpy(op + produced, ip, size_t(count));
94 ip += count;
95 produced += count;
96 } else {
97 const qsizetype count = qsizetype(tag) + 1;
98 if (ip >= iend || count > out.size() - produced) {
99 *error = QStringLiteral("corrupt RLE stream in chunk");
100 return false;
101 }
102 memset(op + produced, *reinterpret_cast<const char *>(ip), size_t(count));
103 ++ip;
104 produced += count;
105 }
106 }
107
108 if (produced != out.size()) {
109 *error = QStringLiteral("RLE stream does not fill the chunk");
110 return false;
111 }
112 return true;
113}
114
115} // namespace QSSGExr
116
117QT_END_NAMESPACE
bool undoPredictorAndDeinterleave(QSpan< char > out, QSpan< char > scratch, QString *error)
bool inflateExact(QByteArrayView in, QSpan< char > out, QString *error)
bool rleDecode(QByteArrayView in, QSpan< char > out, QString *error)