File indexing completed on 2024-05-12 15:59:43
0001 /* 0002 * SPDX-FileCopyrightText: 2004-2007 Graphest Software <libpsd@graphest.com> 0003 * SPDX-FileCopyrightText: 2007 John Marshall 0004 * SPDX-FileCopyrightText: 2010 Boudewijn Rempt <boud@valdyas.org> 0005 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "compression.h" 0011 0012 #include <QBuffer> 0013 #include <QtEndian> 0014 #include <algorithm> 0015 #include <zlib.h> 0016 0017 #include <kis_debug.h> 0018 #include <psd_utils.h> 0019 0020 namespace KisRLE 0021 { 0022 // from gimp's psd-save.c 0023 int compress(const QByteArray &src, QByteArray &dst) 0024 { 0025 int length = src.size(); 0026 dst.resize(length * 2); 0027 dst.fill(0, length * 2); 0028 0029 int remaining = length; 0030 quint8 i, j; 0031 quint32 dest_ptr = 0; 0032 const char *start = src.constData(); 0033 0034 length = 0; 0035 while (remaining > 0) { 0036 /* Look for characters matching the first */ 0037 i = 0; 0038 while ((i < 128) && (remaining - i > 0) && (start[0] == start[i])) 0039 i++; 0040 0041 if (i > 1) /* Match found */ 0042 { 0043 dst[dest_ptr++] = static_cast<char>(-(i - 1)); 0044 dst[dest_ptr++] = *start; 0045 0046 start += i; 0047 remaining -= i; 0048 length += 2; 0049 } else { /* Look for characters different from the previous */ 0050 i = 0; 0051 while ((i < 128) && (remaining - (i + 1) > 0) && (start[i] != start[(i + 1)] || remaining - (i + 2) <= 0 || start[i] != start[(i + 2)])) 0052 i++; 0053 0054 /* If there's only 1 remaining, the previous WHILE stmt doesn't 0055 catch it */ 0056 0057 if (remaining == 1) { 0058 i = 1; 0059 } 0060 0061 if (i > 0) /* Some distinct ones found */ 0062 { 0063 dst[dest_ptr++] = static_cast<char>(i - 1U); 0064 for (j = 0; j < i; j++) { 0065 dst[dest_ptr++] = start[j]; 0066 } 0067 start += i; 0068 remaining -= i; 0069 length += i + 1; 0070 } 0071 } 0072 } 0073 dst.resize(length); 0074 return length; 0075 } 0076 0077 QByteArray compress(const QByteArray &data) 0078 { 0079 QByteArray output; 0080 const int result = KisRLE::compress(data, output); 0081 if (result <= 0) 0082 return QByteArray(); 0083 else 0084 return output; 0085 } 0086 0087 QByteArray decompress(const QByteArray &input, int unpacked_len) 0088 { 0089 QByteArray output; 0090 output.resize(unpacked_len); 0091 0092 const auto *src = input.cbegin(); 0093 auto *dst = output.begin(); 0094 0095 while (src < input.end() && dst < output.end()) { 0096 // NOLINTNEXTLINE(*-reinterpret-cast,readability-identifier-length) 0097 const int8_t n = *reinterpret_cast<const int8_t *>(src); 0098 src += 1; 0099 0100 if (n >= 0) { // copy next n+1 chars 0101 const int bytes = 1 + n; 0102 if (src + bytes > input.cend()) { 0103 errFile << "Input buffer exhausted in replicate of" << bytes << "chars, left" << (input.cend() - src); 0104 return {}; 0105 } 0106 if (dst + bytes > output.end()) { 0107 errFile << "Overrun in packbits replicate of" << bytes << "chars, left" << (output.end() - dst); 0108 return {}; 0109 } 0110 std::copy_n(src, bytes, dst); 0111 src += bytes; 0112 dst += bytes; 0113 } else if (n >= -127 && n <= -1) { // replicate next char -n+1 times 0114 const int bytes = 1 - n; 0115 if (src >= input.cend()) { 0116 errFile << "Input buffer exhausted in copy"; 0117 return {}; 0118 } 0119 if (dst + bytes > output.end()) { 0120 errFile << "Output buffer exhausted in copy of" << bytes << "chars, left" << (output.end() - dst); 0121 return {}; 0122 } 0123 const auto byte = *src; 0124 std::fill_n(dst, bytes, byte); 0125 src += 1; 0126 dst += bytes; 0127 } else if (n == -128) { 0128 continue; 0129 } 0130 } 0131 0132 if (dst < output.end()) { 0133 errFile << "Packbits decode - unpack left" << (output.end() - dst); 0134 std::fill(dst, output.end(), 0); 0135 } 0136 0137 // If the input line was odd width, there's a padding byte 0138 if (src + 1 < input.cend()) { 0139 QByteArray leftovers; 0140 leftovers.resize(static_cast<int>(input.cend() - src)); 0141 std::copy(src, input.cend(), leftovers.begin()); 0142 errFile << "Packbits decode - pack left" << leftovers.size() << leftovers.toHex(); 0143 } 0144 0145 return output; 0146 } 0147 } // namespace KisRLE 0148 0149 namespace KisZip 0150 { 0151 // Based on the reverse of psd_unzip_without_prediction 0152 int compress(const char *input, int unpacked_len, char *dst, int maxout) 0153 { 0154 z_stream stream{}; 0155 int state; 0156 0157 stream.data_type = Z_BINARY; 0158 stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(input)); 0159 stream.avail_in = static_cast<uInt>(unpacked_len); 0160 stream.next_out = reinterpret_cast<Bytef *>(dst); 0161 stream.avail_out = static_cast<uInt>(maxout); 0162 0163 dbgFile << "Expected unpacked length:" << unpacked_len << ", maxout:" << maxout; 0164 0165 if (deflateInit(&stream, -1) != Z_OK) { 0166 dbgFile << "Failed deflate initialization"; 0167 return 0; 0168 } 0169 0170 int flush = Z_PARTIAL_FLUSH; 0171 0172 do { 0173 state = deflate(&stream, flush); 0174 if (state == Z_STREAM_END) { 0175 dbgFile << "Finished deflating"; 0176 flush = Z_FINISH; 0177 } else if (state != Z_OK) { 0178 dbgFile << "Error deflating" << state << stream.msg; 0179 break; 0180 } 0181 } while (stream.avail_in > 0); 0182 0183 if (state != Z_OK || stream.avail_in > 0) { 0184 dbgFile << "Failed deflating" << state << stream.msg; 0185 return 0; 0186 } 0187 0188 dbgFile << "Success, deflated size:" << stream.total_out; 0189 0190 return static_cast<int>(stream.total_out); 0191 } 0192 0193 QByteArray compress(const QByteArray &data) 0194 { 0195 QByteArray output(data.length() * 4, '\0'); 0196 const int result = KisZip::compress(data.constData(), data.size(), output.data(), output.size()); 0197 output.resize(result); 0198 return output; 0199 } 0200 0201 /**********************************************************************/ 0202 /* Two functions copied from the abandoned PSDParse library (GPL) */ 0203 /* See: http://www.telegraphics.com.au/svn/psdparse/trunk/psd_zip.c */ 0204 /* Created by Patrick in 2007.02.02, libpsd@graphest.com */ 0205 /* Modifications by Toby Thain <toby@telegraphics.com.au> */ 0206 /* Refactored by L. E. Segovia <amy@amyspark.me>, 2021.06.30 */ 0207 /**********************************************************************/ 0208 int psd_unzip_without_prediction(const char *src, int packed_len, char *dst, int unpacked_len) 0209 { 0210 z_stream stream{}; 0211 int state; 0212 0213 stream.data_type = Z_BINARY; 0214 stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(src)); 0215 stream.avail_in = static_cast<uInt>(packed_len); 0216 stream.next_out = reinterpret_cast<Bytef *>(dst); 0217 stream.avail_out = static_cast<uInt>(unpacked_len); 0218 0219 if (inflateInit(&stream) != Z_OK) 0220 return 0; 0221 0222 int flush = Z_PARTIAL_FLUSH; 0223 0224 do { 0225 state = inflate(&stream, flush); 0226 if (state == Z_STREAM_END) { 0227 dbgFile << "Finished inflating"; 0228 break; 0229 } else if (state == Z_DATA_ERROR) { 0230 dbgFile << "Error inflating" << state << stream.msg; 0231 if (inflateSync(&stream) != Z_OK) 0232 return 0; 0233 continue; 0234 } 0235 } while (stream.avail_out > 0); 0236 0237 if ((state != Z_STREAM_END && state != Z_OK) || stream.avail_out > 0) { 0238 dbgFile << "Failed inflating" << state << stream.msg; 0239 return 0; 0240 } 0241 0242 return static_cast<int>(stream.total_out); 0243 } 0244 0245 template<typename T> 0246 inline void psd_unzip_with_prediction(QByteArray &dst_buf, int row_size); 0247 0248 template<> 0249 inline void psd_unzip_with_prediction<uint8_t>(QByteArray &dst_buf, const int row_size) 0250 { 0251 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data()); 0252 int len = 0; 0253 int dst_len = dst_buf.size(); 0254 0255 while (dst_len > 0) { 0256 len = row_size; 0257 while (--len) { 0258 *(buf + 1) += *buf; 0259 buf++; 0260 } 0261 buf++; 0262 dst_len -= row_size; 0263 } 0264 } 0265 0266 template<> 0267 inline void psd_unzip_with_prediction<uint16_t>(QByteArray &dst_buf, const int row_size) 0268 { 0269 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data()); 0270 int len = 0; 0271 int dst_len = dst_buf.size(); 0272 0273 while (dst_len > 0) { 0274 len = row_size; 0275 while (--len) { 0276 buf[2] += buf[0] + ((buf[1] + buf[3]) >> 8); 0277 buf[3] += buf[1]; 0278 buf += 2; 0279 } 0280 buf += 2; 0281 dst_len -= row_size * 2; 0282 } 0283 } 0284 0285 QByteArray psd_unzip_with_prediction(const QByteArray &src, int dst_len, int row_size, int color_depth) 0286 { 0287 QByteArray dst_buf = Compression::uncompress(dst_len, src, psd_compression_type::ZIP); 0288 0289 if (dst_buf.size() == 0) 0290 return {}; 0291 0292 if (color_depth == 32) { 0293 // Placeholded for future implementation. 0294 errKrita << "Unsupported bit depth for prediction"; 0295 return {}; 0296 } else if (color_depth == 16) { 0297 psd_unzip_with_prediction<quint16>(dst_buf, row_size); 0298 } else { 0299 psd_unzip_with_prediction<quint8>(dst_buf, row_size); 0300 } 0301 0302 return dst_buf; 0303 } 0304 0305 /**********************************************************************/ 0306 /* End of third party block */ 0307 /**********************************************************************/ 0308 0309 template<typename T> 0310 inline void psd_zip_with_prediction(QByteArray &dst_buf, int row_size); 0311 0312 template<> 0313 inline void psd_zip_with_prediction<uint8_t>(QByteArray &dst_buf, const int row_size) 0314 { 0315 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data()); 0316 int len = 0; 0317 int dst_len = dst_buf.size(); 0318 0319 while (dst_len > 0) { 0320 len = row_size; 0321 while (--len) { 0322 *(buf + 1) -= *buf; 0323 buf++; 0324 } 0325 buf++; 0326 dst_len -= row_size; 0327 } 0328 } 0329 0330 template<> 0331 inline void psd_zip_with_prediction<uint16_t>(QByteArray &dst_buf, const int row_size) 0332 { 0333 auto *buf = reinterpret_cast<uint8_t *>(dst_buf.data()); 0334 int len = 0; 0335 int dst_len = dst_buf.size(); 0336 0337 while (dst_len > 0) { 0338 len = row_size; 0339 while (--len) { 0340 buf[2] -= buf[0] + ((buf[1] + buf[3]) >> 8); 0341 buf[3] -= buf[1]; 0342 buf += 2; 0343 } 0344 buf += 2; 0345 dst_len -= row_size * 2; 0346 } 0347 } 0348 0349 QByteArray psd_zip_with_prediction(const QByteArray &src, int row_size, int color_depth) 0350 { 0351 QByteArray dst_buf(src); 0352 if (color_depth == 32) { 0353 // Placeholded for future implementation. 0354 errKrita << "Unsupported bit depth for prediction"; 0355 return {}; 0356 } else if (color_depth == 16) { 0357 psd_zip_with_prediction<quint16>(dst_buf, row_size); 0358 } else { 0359 psd_zip_with_prediction<quint8>(dst_buf, row_size); 0360 } 0361 0362 return Compression::compress(dst_buf, psd_compression_type::ZIP); 0363 } 0364 0365 QByteArray decompress(const QByteArray &data, int expected_length) 0366 { 0367 QByteArray output(expected_length, '\0'); 0368 const int result = psd_unzip_without_prediction(data.constData(), data.size(), output.data(), expected_length); 0369 if (result == 0) 0370 return QByteArray(); 0371 else 0372 return output; 0373 } 0374 } // namespace KisZip 0375 0376 QByteArray Compression::uncompress(int unpacked_len, QByteArray bytes, psd_compression_type compressionType, int row_size, int color_depth) 0377 { 0378 if (bytes.size() < 1) 0379 return QByteArray(); 0380 0381 switch (compressionType) { 0382 case Uncompressed: 0383 return bytes; 0384 case RLE: 0385 return KisRLE::decompress(bytes, unpacked_len); 0386 case ZIP: 0387 return KisZip::decompress(bytes, unpacked_len); 0388 case ZIPWithPrediction: 0389 return KisZip::psd_unzip_with_prediction(bytes, unpacked_len, row_size, color_depth); 0390 default: 0391 qFatal("Cannot uncompress layer data: invalid compression type"); 0392 } 0393 0394 return QByteArray(); 0395 } 0396 0397 QByteArray Compression::compress(QByteArray bytes, psd_compression_type compressionType, int row_size, int color_depth) 0398 { 0399 if (bytes.size() < 1) 0400 return QByteArray(); 0401 0402 switch (compressionType) { 0403 case Uncompressed: 0404 return bytes; 0405 case RLE: 0406 return KisRLE::compress(bytes); 0407 case ZIP: 0408 return KisZip::compress(bytes); 0409 case ZIPWithPrediction: 0410 return KisZip::psd_zip_with_prediction(bytes, row_size, color_depth); 0411 default: 0412 qFatal("Cannot compress layer data: invalid compression type"); 0413 } 0414 0415 return QByteArray(); 0416 }