Krita Source Code Documentation
Loading...
Searching...
No Matches
compression.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004-2007 Graphest Software <libpsd@graphest.com>
3 * SPDX-FileCopyrightText: 2007 John Marshall
4 * SPDX-FileCopyrightText: 2010 Boudewijn Rempt <boud@valdyas.org>
5 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include "compression.h"
11
12#include <QBuffer>
13#include <QtEndian>
14#include <algorithm>
15#include <zlib.h>
16
17#include <kis_debug.h>
18#include <psd_utils.h>
19
20namespace KisRLE
21{
22// from gimp's psd-save.c
23int compress(const QByteArray &src, QByteArray &dst)
24{
25 int length = src.size();
26 dst.resize(length * 2);
27 dst.fill(0, length * 2);
28
29 int remaining = length;
30 quint8 i, j;
31 quint32 dest_ptr = 0;
32 const char *start = src.constData();
33
34 length = 0;
35 while (remaining > 0) {
36 /* Look for characters matching the first */
37 i = 0;
38 while ((i < 128) && (remaining - i > 0) && (start[0] == start[i]))
39 i++;
40
41 if (i > 1) /* Match found */
42 {
43 dst[dest_ptr++] = static_cast<char>(-(i - 1));
44 dst[dest_ptr++] = *start;
45
46 start += i;
47 remaining -= i;
48 length += 2;
49 } else { /* Look for characters different from the previous */
50 i = 0;
51 while ((i < 128) && (remaining - (i + 1) > 0) && (start[i] != start[(i + 1)] || remaining - (i + 2) <= 0 || start[i] != start[(i + 2)]))
52 i++;
53
54 /* If there's only 1 remaining, the previous WHILE stmt doesn't
55 catch it */
56
57 if (remaining == 1) {
58 i = 1;
59 }
60
61 if (i > 0) /* Some distinct ones found */
62 {
63 dst[dest_ptr++] = static_cast<char>(i - 1U);
64 for (j = 0; j < i; j++) {
65 dst[dest_ptr++] = start[j];
66 }
67 start += i;
68 remaining -= i;
69 length += i + 1;
70 }
71 }
72 }
73 dst.resize(length);
74 return length;
75}
76
77QByteArray compress(const QByteArray &data)
78{
79 QByteArray output;
80 const int result = KisRLE::compress(data, output);
81 if (result <= 0)
82 return QByteArray();
83 else
84 return output;
85}
86
87QByteArray decompress(const QByteArray &input, int unpacked_len)
88{
89 QByteArray output;
90 output.resize(unpacked_len);
91
92 const auto *src = input.cbegin();
93 auto *dst = output.begin();
94
95 while (src < input.end() && dst < output.end()) {
96 // NOLINTNEXTLINE(*-reinterpret-cast,readability-identifier-length)
97 const int8_t n = *reinterpret_cast<const int8_t *>(src);
98 src += 1;
99
100 if (n >= 0) { // copy next n+1 chars
101 const int bytes = 1 + n;
102 if (src + bytes > input.cend()) {
103 errFile << "Input buffer exhausted in replicate of" << bytes << "chars, left" << (input.cend() - src);
104 return {};
105 }
106 if (dst + bytes > output.end()) {
107 errFile << "Overrun in packbits replicate of" << bytes << "chars, left" << (output.end() - dst);
108 return {};
109 }
110 std::copy_n(src, bytes, dst);
111 src += bytes;
112 dst += bytes;
113 } else if (n >= -127 && n <= -1) { // replicate next char -n+1 times
114 const int bytes = 1 - n;
115 if (src >= input.cend()) {
116 errFile << "Input buffer exhausted in copy";
117 return {};
118 }
119 if (dst + bytes > output.end()) {
120 errFile << "Output buffer exhausted in copy of" << bytes << "chars, left" << (output.end() - dst);
121 return {};
122 }
123 const auto byte = *src;
124 std::fill_n(dst, bytes, byte);
125 src += 1;
126 dst += bytes;
127 } else if (n == -128) {
128 continue;
129 }
130 }
131
132 if (dst < output.end()) {
133 errFile << "Packbits decode - unpack left" << (output.end() - dst);
134 std::fill(dst, output.end(), 0);
135 }
136
137 // If the input line was odd width, there's a padding byte
138 if (src + 1 < input.cend()) {
139 QByteArray leftovers;
140 leftovers.resize(static_cast<int>(input.cend() - src));
141 std::copy(src, input.cend(), leftovers.begin());
142 errFile << "Packbits decode - pack left" << leftovers.size() << leftovers.toHex();
143 }
144
145 return output;
146}
147} // namespace KisRLE
148
149namespace KisZip
150{
151// Based on the reverse of psd_unzip_without_prediction
152int compress(const char *input, int unpacked_len, char *dst, int maxout)
153{
154 z_stream stream{};
155 int state;
156
157 stream.data_type = Z_BINARY;
158 stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(input));
159 stream.avail_in = static_cast<uInt>(unpacked_len);
160 stream.next_out = reinterpret_cast<Bytef *>(dst);
161 stream.avail_out = static_cast<uInt>(maxout);
162
163 dbgFile << "Expected unpacked length:" << unpacked_len << ", maxout:" << maxout;
164
165 if (deflateInit(&stream, -1) != Z_OK) {
166 dbgFile << "Failed deflate initialization";
167 return 0;
168 }
169
170 int flush = Z_PARTIAL_FLUSH;
171
172 do {
173 state = deflate(&stream, flush);
174 if (state == Z_STREAM_END) {
175 dbgFile << "Finished deflating";
176 flush = Z_FINISH;
177 } else if (state != Z_OK) {
178 dbgFile << "Error deflating" << state << stream.msg;
179 break;
180 }
181 } while (stream.avail_in > 0);
182
183 if (state != Z_OK || stream.avail_in > 0) {
184 dbgFile << "Failed deflating" << state << stream.msg;
185 return 0;
186 }
187
188 dbgFile << "Success, deflated size:" << stream.total_out;
189
190 return static_cast<int>(stream.total_out);
191}
192
193QByteArray compress(const QByteArray &data)
194{
195 QByteArray output(data.length() * 4, '\0');
196 const int result = KisZip::compress(data.constData(), data.size(), output.data(), output.size());
197 output.resize(result);
198 return output;
199}
200
201/**********************************************************************/
202/* Two functions copied from the abandoned PSDParse library (GPL) */
203/* See: http://www.telegraphics.com.au/svn/psdparse/trunk/psd_zip.c */
204/* Created by Patrick in 2007.02.02, libpsd@graphest.com */
205/* Modifications by Toby Thain <toby@telegraphics.com.au> */
206/* Refactored by L. E. Segovia <amy@amyspark.me>, 2021.06.30 */
207/**********************************************************************/
208int psd_unzip_without_prediction(const char *src, int packed_len, char *dst, int unpacked_len)
209{
210 z_stream stream{};
211 int state;
212
213 stream.data_type = Z_BINARY;
214 stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(src));
215 stream.avail_in = static_cast<uInt>(packed_len);
216 stream.next_out = reinterpret_cast<Bytef *>(dst);
217 stream.avail_out = static_cast<uInt>(unpacked_len);
218
219 if (inflateInit(&stream) != Z_OK)
220 return 0;
221
222 int flush = Z_PARTIAL_FLUSH;
223
224 do {
225 state = inflate(&stream, flush);
226 if (state == Z_STREAM_END) {
227 dbgFile << "Finished inflating";
228 break;
229 } else if (state == Z_DATA_ERROR) {
230 dbgFile << "Error inflating" << state << stream.msg;
231 if (inflateSync(&stream) != Z_OK)
232 return 0;
233 continue;
234 }
235 } while (stream.avail_out > 0);
236
237 if ((state != Z_STREAM_END && state != Z_OK) || stream.avail_out > 0) {
238 dbgFile << "Failed inflating" << state << stream.msg;
239 return 0;
240 }
241
242 return static_cast<int>(stream.total_out);
243}
244
245template<typename T>
246inline void psd_unzip_with_prediction(QByteArray &dst_buf, int row_size);
247
248template<>
249inline void psd_unzip_with_prediction<uint8_t>(QByteArray &dst_buf, const int row_size)
250{
251 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data());
252 int len = 0;
253 int dst_len = dst_buf.size();
254
255 while (dst_len > 0) {
256 len = row_size;
257 while (--len) {
258 *(buf + 1) += *buf;
259 buf++;
260 }
261 buf++;
262 dst_len -= row_size;
263 }
264}
265
266template<>
267inline void psd_unzip_with_prediction<uint16_t>(QByteArray &dst_buf, const int row_size)
268{
269 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data());
270 int len = 0;
271 int dst_len = dst_buf.size();
272
273 while (dst_len > 0) {
274 len = row_size;
275 while (--len) {
276 buf[2] += buf[0] + ((buf[1] + buf[3]) >> 8);
277 buf[3] += buf[1];
278 buf += 2;
279 }
280 buf += 2;
281 dst_len -= row_size * 2;
282 }
283}
284
285QByteArray psd_unzip_with_prediction(const QByteArray &src, int dst_len, int row_size, int color_depth)
286{
287 QByteArray dst_buf = Compression::uncompress(dst_len, src, psd_compression_type::ZIP);
288
289 if (dst_buf.size() == 0)
290 return {};
291
292 if (color_depth == 32) {
293 // Placeholded for future implementation.
294 errKrita << "Unsupported bit depth for prediction";
295 return {};
296 } else if (color_depth == 16) {
297 psd_unzip_with_prediction<quint16>(dst_buf, row_size);
298 } else {
299 psd_unzip_with_prediction<quint8>(dst_buf, row_size);
300 }
301
302 return dst_buf;
303}
304
305/**********************************************************************/
306/* End of third party block */
307/**********************************************************************/
308
309template<typename T>
310inline void psd_zip_with_prediction(QByteArray &dst_buf, int row_size);
311
312template<>
313inline void psd_zip_with_prediction<uint8_t>(QByteArray &dst_buf, const int row_size)
314{
315 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data());
316 int len = 0;
317 int dst_len = dst_buf.size();
318
319 while (dst_len > 0) {
320 len = row_size;
321 while (--len) {
322 *(buf + 1) -= *buf;
323 buf++;
324 }
325 buf++;
326 dst_len -= row_size;
327 }
328}
329
330template<>
331inline void psd_zip_with_prediction<uint16_t>(QByteArray &dst_buf, const int row_size)
332{
333 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data());
334 int len = 0;
335 int dst_len = dst_buf.size();
336
337 while (dst_len > 0) {
338 len = row_size;
339 while (--len) {
340 buf[2] -= buf[0] + ((buf[1] + buf[3]) >> 8);
341 buf[3] -= buf[1];
342 buf += 2;
343 }
344 buf += 2;
345 dst_len -= row_size * 2;
346 }
347}
348
349QByteArray psd_zip_with_prediction(const QByteArray &src, int row_size, int color_depth)
350{
351 QByteArray dst_buf(src);
352 if (color_depth == 32) {
353 // Placeholded for future implementation.
354 errKrita << "Unsupported bit depth for prediction";
355 return {};
356 } else if (color_depth == 16) {
357 psd_zip_with_prediction<quint16>(dst_buf, row_size);
358 } else {
359 psd_zip_with_prediction<quint8>(dst_buf, row_size);
360 }
361
363}
364
365QByteArray decompress(const QByteArray &data, int expected_length)
366{
367 QByteArray output(expected_length, '\0');
368 const int result = psd_unzip_without_prediction(data.constData(), data.size(), output.data(), expected_length);
369 if (result == 0)
370 return QByteArray();
371 else
372 return output;
373}
374} // namespace KisZip
375
376QByteArray Compression::uncompress(int unpacked_len, QByteArray bytes, psd_compression_type compressionType, int row_size, int color_depth)
377{
378 if (bytes.size() < 1)
379 return QByteArray();
380
381 switch (compressionType) {
382 case Uncompressed:
383 return bytes;
384 case RLE:
385 return KisRLE::decompress(bytes, unpacked_len);
386 case ZIP:
387 return KisZip::decompress(bytes, unpacked_len);
389 return KisZip::psd_unzip_with_prediction(bytes, unpacked_len, row_size, color_depth);
390 default:
391 qFatal("Cannot uncompress layer data: invalid compression type");
392 }
393
394 return QByteArray();
395}
396
397QByteArray Compression::compress(QByteArray bytes, psd_compression_type compressionType, int row_size, int color_depth)
398{
399 if (bytes.size() < 1)
400 return QByteArray();
401
402 switch (compressionType) {
403 case Uncompressed:
404 return bytes;
405 case RLE:
406 return KisRLE::compress(bytes);
407 case ZIP:
408 return KisZip::compress(bytes);
410 return KisZip::psd_zip_with_prediction(bytes, row_size, color_depth);
411 default:
412 qFatal("Cannot compress layer data: invalid compression type");
413 }
414
415 return QByteArray();
416}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
static QByteArray uncompress(int unpacked_len, QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
static QByteArray compress(QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
#define errFile
Definition kis_debug.h:115
#define errKrita
Definition kis_debug.h:107
#define dbgFile
Definition kis_debug.h:53
QByteArray decompress(const QByteArray &input, int unpacked_len)
int compress(const QByteArray &src, QByteArray &dst)
int psd_unzip_without_prediction(const char *src, int packed_len, char *dst, int unpacked_len)
QByteArray decompress(const QByteArray &data, int expected_length)
void psd_zip_with_prediction< uint16_t >(QByteArray &dst_buf, const int row_size)
void psd_unzip_with_prediction< uint8_t >(QByteArray &dst_buf, const int row_size)
void psd_unzip_with_prediction< uint16_t >(QByteArray &dst_buf, const int row_size)
void psd_unzip_with_prediction(QByteArray &dst_buf, int row_size)
void psd_zip_with_prediction< uint8_t >(QByteArray &dst_buf, const int row_size)
void psd_zip_with_prediction(QByteArray &dst_buf, int row_size)
int compress(const char *input, int unpacked_len, char *dst, int maxout)
psd_compression_type
Definition psd.h:39
@ RLE
Definition psd.h:41
@ ZIPWithPrediction
Definition psd.h:43
@ Uncompressed
Definition psd.h:40
@ ZIP
Definition psd.h:42