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 | */ |
23 | inline 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 | */ |
65 | inline 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 | |