1/*
2 Packbits compression used on many legacy formats (IFF, PSD, TIFF).
3
4 SPDX-FileCopyrightText: 2025 Mirco Miranda <mircomir@outlook.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8#ifndef PACKBITS_P_H
9#define PACKBITS_P_H
10
11#include <QIODevice>
12
13/*!
14 * \brief packbitsDecompress
15 * Fast PackBits decompression.
16 * \param input The compressed input buffer.
17 * \param ilen The input buffer size.
18 * \param output The uncompressed target buffer.
19 * \param olen The target buffer size.
20 * \param allowN128 If true, -128 is a valid run length size (false for PSD / TIFF, true for IFF) .
21 * \return The number of valid bytes in the target buffer.
22 */
23inline qint64 packbitsDecompress(const char *input, qint64 ilen, char *output, qint64 olen, bool allowN128 = false)
24{
25 qint64 j = 0;
26 for (qint64 ip = 0, rr = 0, available = olen; j < olen && ip < ilen; available = olen - j) {
27 signed char n = static_cast<signed char>(input[ip++]);
28 if (n == -128 && !allowN128)
29 continue;
30
31 if (n >= 0) {
32 rr = qint64(n) + 1;
33 if (available < rr) {
34 --ip;
35 break;
36 }
37
38 if (ip + rr > ilen)
39 return -1;
40 memcpy(dest: output + j, src: input + ip, n: size_t(rr));
41 ip += rr;
42 } else if (ip < ilen) {
43 rr = qint64(1-n);
44 if (available < rr) {
45 --ip;
46 break;
47 }
48 memset(s: output + j, c: input[ip++], n: size_t(rr));
49 }
50
51 j += rr;
52 }
53 return j;
54}
55
56/*!
57 * \brief packbitsDecompress
58 * PackBits decompression.
59 * \param input The input device.
60 * \param output The uncompressed target buffer.
61 * \param olen The target buffer size.
62 * \param allowN128 If true, -128 is a valid run length size (false for PSD / TIFF, true for IFF) .
63 * \return The number of valid bytes in the target buffer.
64 */
65inline qint64 packbitsDecompress(QIODevice *input, char *output, qint64 olen, bool allowN128 = false)
66{
67 qint64 j = 0;
68 for (qint64 rr = 0, available = olen; j < olen; available = olen - j) {
69 char n;
70
71 // check the output buffer space for the next run
72 if (available < 129) {
73 if (input->peek(data: &n, maxlen: 1) != 1) { // end of data (or error)
74 break;
75 }
76 if (static_cast<signed char>(n) != -128 || allowN128)
77 if ((static_cast<signed char>(n) >= 0 ? qint64(n) + 1 : qint64(1 - n)) > available)
78 break;
79 }
80
81 // decompress
82 if (input->read(data: &n, maxlen: 1) != 1) { // end of data (or error)
83 break;
84 }
85
86 if (static_cast<signed char>(n) == -128 && !allowN128) {
87 continue;
88 }
89
90 if (static_cast<signed char>(n) >= 0) {
91 rr = input->read(data: output + j, maxlen: qint64(n) + 1);
92 if (rr == -1) {
93 return -1;
94 }
95 }
96 else {
97 char b;
98 if (input->read(data: &b, maxlen: 1) != 1) {
99 break;
100 }
101 rr = qint64(1 - static_cast<signed char>(n));
102 std::memset(s: output + j, c: b, n: size_t(rr));
103 }
104
105 j += rr;
106 }
107 return j;
108}
109
110
111#endif // PACKBITS_P_H
112

source code of kimageformats/src/imageformats/packbits_p.h