| 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 | |