File indexing completed on 2024-05-19 04:29:05
0001 /* 0002 * SPDX-FileCopyrightText: 2005-2007 Cyrille Berger <cberger@cberger.net> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 * 0006 */ 0007 0008 #include "kis_png_converter.h" 0009 // A big thank to Glenn Randers-Pehrson for his wonderful 0010 // documentation of libpng available at 0011 // http://www.libpng.org/pub/png/libpng-1.2.5-manual.html 0012 0013 #ifndef PNG_MAX_UINT // Removed in libpng 1.4 0014 #define PNG_MAX_UINT PNG_UINT_31_MAX 0015 #endif 0016 0017 #include <KoConfig.h> // WORDS_BIGENDIAN 0018 #include <KoStore.h> 0019 #include <KoStoreDevice.h> 0020 0021 #include <limits.h> 0022 #include <stdio.h> 0023 #include <zlib.h> 0024 0025 #include <QBuffer> 0026 #include <QFile> 0027 #include <QApplication> 0028 0029 #include <klocalizedstring.h> 0030 #include <QUrl> 0031 0032 #include <KoColorSpace.h> 0033 #include <KoDocumentInfo.h> 0034 #include <KoID.h> 0035 #include <KoColorSpaceRegistry.h> 0036 #include <KoColorProfile.h> 0037 #include <KoColor.h> 0038 #include <KoUnit.h> 0039 0040 #include "dialogs/kis_dlg_png_import.h" 0041 #include "kis_clipboard.h" 0042 #include "kis_undo_stores.h" 0043 #include <KisDocument.h> 0044 #include <KoColorModelStandardIds.h> 0045 #include <kis_config.h> 0046 #include <kis_cursor_override_hijacker.h> 0047 #include <kis_group_layer.h> 0048 #include <kis_image.h> 0049 #include <kis_iterator_ng.h> 0050 #include <kis_layer.h> 0051 #include <kis_meta_data_backend_registry.h> 0052 #include <kis_meta_data_store.h> 0053 #include <kis_paint_device.h> 0054 #include <kis_paint_layer.h> 0055 #include <kis_painter.h> 0056 #include <kis_transaction.h> 0057 0058 #include <kis_assert.h> 0059 0060 namespace 0061 { 0062 0063 int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) 0064 { 0065 0066 QString id = cs->id(); 0067 0068 if (id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16") { 0069 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; 0070 } 0071 if (id == "RGBA" || id == "RGBA16") { 0072 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; 0073 } 0074 0075 return -1; 0076 0077 } 0078 0079 bool colorSpaceIdSupported(const QString &id) 0080 { 0081 return id == "RGBA" || id == "RGBA16" || 0082 id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16"; 0083 } 0084 0085 QPair<QString, QString> getColorSpaceForColorType(int color_type, int color_nb_bits) 0086 { 0087 QPair<QString, QString> r; 0088 0089 if (color_type == PNG_COLOR_TYPE_PALETTE) { 0090 r.first = RGBAColorModelID.id(); 0091 r.second = Integer8BitsColorDepthID.id(); 0092 } else { 0093 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { 0094 r.first = GrayAColorModelID.id(); 0095 } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) { 0096 r.first = RGBAColorModelID.id(); 0097 } 0098 if (color_nb_bits == 16) { 0099 r.second = Integer16BitsColorDepthID.id(); 0100 } else if (color_nb_bits <= 8) { 0101 r.second = Integer8BitsColorDepthID.id(); 0102 } 0103 } 0104 return r; 0105 } 0106 0107 0108 void fillText(png_text* p_text, const char* key, QString& text) 0109 { 0110 p_text->compression = PNG_TEXT_COMPRESSION_zTXt; 0111 p_text->key = const_cast<char *>(key); 0112 char* textc = new char[text.length()+1]; 0113 strcpy(textc, text.toLatin1()); 0114 p_text->text = textc; 0115 p_text->text_length = text.length() + 1; 0116 } 0117 0118 long formatStringList(char *string, const size_t length, const char *format, va_list operands) 0119 { 0120 int n = vsnprintf(string, length, format, operands); 0121 0122 if (n < 0) 0123 string[length-1] = '\0'; 0124 0125 return((long) n); 0126 } 0127 0128 long formatString(char *string, const size_t length, const char *format, ...) 0129 { 0130 long n; 0131 0132 va_list operands; 0133 0134 va_start(operands, format); 0135 n = (long) formatStringList(string, length, format, operands); 0136 va_end(operands); 0137 return(n); 0138 } 0139 0140 void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data) 0141 { 0142 0143 png_textp text; 0144 0145 png_uint_32 allocated_length, description_length; 0146 0147 const uchar hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 0148 0149 dbgFile << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << endl; 0150 0151 text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text)); 0152 description_length = profile_type.length(); 0153 allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length); 0154 0155 text[0].text = (png_charp) png_malloc(ping, allocated_length); 0156 memset(text[0].text, 0, allocated_length); 0157 0158 QString key = QLatin1Literal("Raw profile type ") + profile_type.toLatin1(); 0159 QByteArray keyData = key.toLatin1(); 0160 text[0].key = keyData.data(); 0161 0162 uchar* sp = (uchar*)profile_data.data(); 0163 png_charp dp = text[0].text; 0164 *dp++ = '\n'; 0165 0166 memcpy(dp, profile_type.toLatin1().constData(), profile_type.length()); 0167 0168 dp += description_length; 0169 *dp++ = '\n'; 0170 0171 formatString(dp, allocated_length - strlen(text[0].text), "%8lu ", (unsigned long)profile_data.length()); 0172 0173 dp += 8; 0174 0175 for (long i = 0; i < (long) profile_data.length(); i++) { 0176 if (i % 36 == 0) 0177 *dp++ = '\n'; 0178 0179 *(dp++) = (char) hex[((*sp >> 4) & 0x0f)]; 0180 *(dp++) = (char) hex[((*sp++) & 0x0f)]; 0181 } 0182 0183 *dp++ = '\n'; 0184 *dp = '\0'; 0185 text[0].text_length = (png_size_t)(dp - text[0].text); 0186 text[0].compression = -1; 0187 0188 if (text[0].text_length <= allocated_length) 0189 png_set_text(ping, ping_info, text, 1); 0190 0191 png_free(ping, text[0].text); 0192 png_free(ping, text); 0193 } 0194 0195 QByteArray png_read_raw_profile(png_textp text) 0196 { 0197 QByteArray profile; 0198 0199 static const unsigned char unhex[103] = { 0200 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0201 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0202 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 0205 13, 14, 15 0206 }; 0207 0208 png_charp sp = text[0].text + 1; 0209 /* look for newline */ 0210 while (*sp != '\n') 0211 sp++; 0212 /* look for length */ 0213 while (*sp == '\0' || *sp == ' ' || *sp == '\n') 0214 sp++; 0215 png_uint_32 length = (png_uint_32) atol(sp); 0216 while (*sp != ' ' && *sp != '\n') 0217 sp++; 0218 if (length == 0) { 0219 return profile; 0220 } 0221 profile.resize(length); 0222 /* copy profile, skipping white space and column 1 "=" signs */ 0223 unsigned char *dp = (unsigned char*)profile.data(); 0224 png_uint_32 nibbles = length * 2; 0225 for (png_uint_32 i = 0; i < nibbles; i++) { 0226 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') { 0227 if (*sp == '\0') { 0228 return QByteArray(); 0229 } 0230 sp++; 0231 } 0232 if (i % 2 == 0) 0233 *dp = (unsigned char)(16 * unhex[(int) *sp++]); 0234 else 0235 (*dp++) += unhex[(int) *sp++]; 0236 } 0237 return profile; 0238 } 0239 0240 void decode_meta_data(png_textp text, KisMetaData::Store* store, QString type, int headerSize) 0241 { 0242 dbgFile << "Decoding " << type << " " << text[0].key; 0243 KisMetaData::IOBackend *exifIO = KisMetadataBackendRegistry::instance()->value(type); 0244 Q_ASSERT(exifIO); 0245 0246 QByteArray rawProfile = png_read_raw_profile(text); 0247 if (headerSize > 0) { 0248 rawProfile.remove(0, headerSize); 0249 } 0250 if (rawProfile.size() > 0) { 0251 QBuffer buffer; 0252 buffer.setData(rawProfile); 0253 exifIO->loadFrom(store, &buffer); 0254 } else { 0255 dbgFile << "Decoding failed"; 0256 } 0257 } 0258 } 0259 0260 extern "C" { 0261 static void kis_png_warning(png_structp /*png_ptr*/, png_const_charp message) 0262 { 0263 qWarning("libpng warning: %s", message); 0264 } 0265 0266 } 0267 0268 0269 0270 0271 KisPNGConverter::KisPNGConverter(KisDocument *doc, bool batchMode) 0272 { 0273 // Q_ASSERT(doc); 0274 // Q_ASSERT(adapter); 0275 0276 m_doc = doc; 0277 m_stop = false; 0278 m_max_row = 0; 0279 m_image = 0; 0280 m_batchMode = batchMode; 0281 } 0282 0283 KisPNGConverter::~KisPNGConverter() 0284 { 0285 } 0286 0287 class KisPNGReadStream 0288 { 0289 public: 0290 KisPNGReadStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { 0291 } 0292 int nextValue() { 0293 if (m_posinc == 0) { 0294 m_posinc = 8; 0295 m_buf++; 0296 } 0297 m_posinc -= m_depth; 0298 return (((*m_buf) >> (m_posinc)) & ((1 << m_depth) - 1)); 0299 } 0300 private: 0301 quint32 m_posinc, m_depth; 0302 quint8* m_buf; 0303 }; 0304 0305 class KisPNGWriteStream 0306 { 0307 public: 0308 KisPNGWriteStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) { 0309 *m_buf = 0; 0310 } 0311 void setNextValue(int v) { 0312 if (m_posinc == 0) { 0313 m_posinc = 8; 0314 m_buf++; 0315 *m_buf = 0; 0316 } 0317 m_posinc -= m_depth; 0318 *m_buf = (v << m_posinc) | *m_buf; 0319 } 0320 private: 0321 quint32 m_posinc, m_depth; 0322 quint8* m_buf; 0323 }; 0324 0325 class KisPNGReaderAbstract 0326 { 0327 public: 0328 KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height) : png_ptr(_png_ptr), width(_width), height(_height) {} 0329 virtual ~KisPNGReaderAbstract() {} 0330 virtual png_bytep readLine() = 0; 0331 protected: 0332 png_structp png_ptr; 0333 int width, height; 0334 }; 0335 0336 class KisPNGReaderLineByLine : public KisPNGReaderAbstract 0337 { 0338 public: 0339 KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height) { 0340 std::size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); 0341 row_pointer = new png_byte[rowbytes]; 0342 } 0343 ~KisPNGReaderLineByLine() override { 0344 delete[] row_pointer; 0345 } 0346 png_bytep readLine() override { 0347 png_read_row(png_ptr, row_pointer, 0); 0348 return row_pointer; 0349 } 0350 private: 0351 png_bytep row_pointer; 0352 }; 0353 0354 class KisPNGReaderFullImage : public KisPNGReaderAbstract 0355 { 0356 public: 0357 KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height), y(0) { 0358 row_pointers = new png_bytep[height]; 0359 std::size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); 0360 for (int i = 0; i < height; i++) { 0361 row_pointers[i] = new png_byte[rowbytes]; 0362 } 0363 png_read_image(png_ptr, row_pointers); 0364 } 0365 ~KisPNGReaderFullImage() override { 0366 for (int i = 0; i < height; i++) { 0367 delete[] row_pointers[i]; 0368 } 0369 delete[] row_pointers; 0370 } 0371 png_bytep readLine() override { 0372 return row_pointers[y++]; 0373 } 0374 private: 0375 png_bytepp row_pointers; 0376 int y; 0377 }; 0378 0379 0380 static 0381 void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length) 0382 { 0383 QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr); 0384 0385 while (length) { 0386 int nr = in->read((char*)data, length); 0387 if (nr <= 0) { 0388 png_error(png_ptr, "Read Error"); 0389 return; 0390 } 0391 length -= nr; 0392 } 0393 } 0394 0395 static 0396 void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length) 0397 { 0398 QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr); 0399 0400 uint nr = out->write((char*)data, length); 0401 if (nr != length) { 0402 png_error(png_ptr, "Write Error"); 0403 return; 0404 } 0405 } 0406 0407 static 0408 void _flush_fn(png_structp png_ptr) 0409 { 0410 Q_UNUSED(png_ptr); 0411 } 0412 0413 KisImportExportErrorCode KisPNGConverter::buildImage(QIODevice* iod) 0414 { 0415 dbgFile << "Start decoding PNG File"; 0416 0417 png_byte signature[8]; 0418 iod->peek((char*)signature, 8); 0419 0420 #if PNG_LIBPNG_VER < 10400 0421 if (!png_check_sig(signature, 8)) { 0422 #else 0423 if (png_sig_cmp(signature, 0, 8) != 0) { 0424 #endif 0425 iod->close(); 0426 return (ImportExportCodes::FileFormatIncorrect); 0427 } 0428 0429 // Initialize the internal structures 0430 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 0431 0432 if (!png_ptr) { 0433 iod->close(); 0434 } 0435 0436 #ifdef PNG_SET_USER_LIMITS_SUPPORTED 0437 /* Remove the user limits, if any */ 0438 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff); 0439 png_set_chunk_cache_max(png_ptr, 0); 0440 png_set_chunk_malloc_max(png_ptr, 0); 0441 #endif 0442 0443 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning); 0444 #ifdef PNG_BENIGN_ERRORS_SUPPORTED 0445 png_set_benign_errors(png_ptr, 1); 0446 #endif 0447 0448 png_infop info_ptr = png_create_info_struct(png_ptr); 0449 if (!info_ptr) { 0450 png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); 0451 iod->close(); 0452 return (ImportExportCodes::Failure); 0453 } 0454 0455 png_infop end_info = png_create_info_struct(png_ptr); 0456 if (!end_info) { 0457 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); 0458 iod->close(); 0459 return (ImportExportCodes::Failure); 0460 } 0461 0462 // Catch errors 0463 if (setjmp(png_jmpbuf(png_ptr))) { 0464 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 0465 iod->close(); 0466 return (ImportExportCodes::Failure); 0467 } 0468 0469 // Initialize the special 0470 png_set_read_fn(png_ptr, iod, _read_fn); 0471 0472 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED) 0473 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); 0474 #endif 0475 0476 // read all PNG info up to image data 0477 png_read_info(png_ptr, info_ptr); 0478 0479 0480 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) { 0481 png_set_expand(png_ptr); 0482 } 0483 0484 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) { 0485 png_set_packing(png_ptr); 0486 } 0487 0488 0489 if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE && 0490 (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) { 0491 png_set_expand(png_ptr); 0492 } 0493 png_read_update_info(png_ptr, info_ptr); 0494 0495 // Read information about the png 0496 png_uint_32 width, height; 0497 int color_nb_bits, color_type, interlace_type; 0498 png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, 0, 0); 0499 dbgFile << "width = " << width << " height = " << height << " color_nb_bits = " << color_nb_bits << " color_type = " << color_type << " interlace_type = " << interlace_type << endl; 0500 // swap byte order on little endian machines. 0501 #ifndef WORDS_BIGENDIAN 0502 if (color_nb_bits > 8) 0503 png_set_swap(png_ptr); 0504 #endif 0505 0506 // Determine the colorspace 0507 QPair<QString, QString> csName = getColorSpaceForColorType(color_type, color_nb_bits); 0508 if (csName.first.isEmpty()) { 0509 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 0510 iod->close(); 0511 return ImportExportCodes::FormatColorSpaceUnsupported; 0512 } 0513 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA); 0514 0515 // Read image profile 0516 png_charp profile_name; 0517 #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 0518 png_bytep profile_data; 0519 #else 0520 png_charp profile_data; 0521 #endif 0522 int compression_type; 0523 png_uint_32 proflen; 0524 0525 // Get the various optional chunks 0526 0527 // https://www.w3.org/TR/PNG/#11cHRM 0528 #if defined(PNG_cHRM_SUPPORTED) 0529 double whitePointX, whitePointY; 0530 double redX, redY; 0531 double greenX, greenY; 0532 double blueX, blueY; 0533 png_get_cHRM(png_ptr,info_ptr, &whitePointX, &whitePointY, &redX, &redY, &greenX, &greenY, &blueX, &blueY); 0534 dbgFile << "cHRM:" << whitePointX << whitePointY << redX << redY << greenX << greenY << blueX << blueY; 0535 #endif 0536 0537 // https://www.w3.org/TR/PNG/#11gAMA 0538 #if defined(PNG_GAMMA_SUPPORTED) 0539 double gamma; 0540 png_get_gAMA(png_ptr, info_ptr, &gamma); 0541 dbgFile << "gAMA" << gamma; 0542 #endif 0543 0544 // https://www.w3.org/TR/PNG/#11sRGB 0545 #if defined(PNG_sRGB_SUPPORTED) 0546 int sRGBIntent; 0547 png_get_sRGB(png_ptr, info_ptr, &sRGBIntent); 0548 dbgFile << "sRGB" << sRGBIntent; 0549 #endif 0550 0551 bool fromBlender = false; 0552 0553 png_text* text_ptr; 0554 int num_comments; 0555 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); 0556 0557 for (int i = 0; i < num_comments; i++) { 0558 QString key = QString(text_ptr[i].key).toLower(); 0559 if (key == "file") { 0560 QString relatedFile = text_ptr[i].text; 0561 if (relatedFile.contains(".blend", Qt::CaseInsensitive)){ 0562 fromBlender=true; 0563 } 0564 } 0565 } 0566 0567 bool loadedImageIsHDR = false; 0568 const KoColorProfile* profile = 0; 0569 if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { 0570 QByteArray profile_rawdata; 0571 // XXX: Hardcoded for icc type -- is that correct for us? 0572 profile_rawdata.resize(proflen); 0573 memcpy(profile_rawdata.data(), profile_data, proflen); 0574 profile = KoColorSpaceRegistry::instance()->createColorProfile(csName.first, csName.second, profile_rawdata); 0575 Q_CHECK_PTR(profile); 0576 if (profile) { 0577 // dbgFile << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo(); 0578 if (!profile->isSuitableForOutput()) { 0579 dbgFile << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile"; // TODO: in ko2 popup a selection menu to inform the user 0580 } 0581 } 0582 0583 loadedImageIsHDR = strcmp(profile_name, "ITUR_2100_PQ_FULL") == 0; 0584 } 0585 else { 0586 dbgFile << "no embedded profile, will use the default profile"; 0587 if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains("test") && !m_batchMode) { 0588 KisConfig cfg(true); 0589 quint32 behaviour = cfg.pasteBehaviour(); 0590 if (behaviour == KisClipboard::PASTE_ASK) { 0591 KisDlgPngImport dlg(m_path, csName.first, csName.second); 0592 KisCursorOverrideHijacker hijacker; 0593 Q_UNUSED(hijacker); 0594 dlg.exec(); 0595 if (!dlg.profile().isEmpty()) { 0596 profile = KoColorSpaceRegistry::instance()->profileByName(dlg.profile()); 0597 } 0598 } 0599 } 0600 dbgFile << "no embedded profile, will use the default profile"; 0601 } 0602 0603 const QString colorSpaceId = 0604 KoColorSpaceRegistry::instance()->colorSpaceId(csName.first, csName.second); 0605 0606 // Check that the profile is used by the color space 0607 if (profile && !KoColorSpaceRegistry::instance()->profileIsCompatible(profile, colorSpaceId)) { 0608 warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << csName.first << " " << csName.second; 0609 profile = 0; 0610 } 0611 0612 // Retrieve a pointer to the colorspace 0613 KoColorConversionTransformation* transform = 0; 0614 const KoColorSpace* cs = 0; 0615 0616 if (loadedImageIsHDR && 0617 csName.first == RGBAColorModelID.id() && 0618 csName.second == Integer16BitsColorDepthID.id()) { 0619 0620 const KoColorSpace *p2020PQCS = 0621 KoColorSpaceRegistry::instance()->colorSpace( 0622 RGBAColorModelID.id(), 0623 Integer16BitsColorDepthID.id(), 0624 KoColorSpaceRegistry::instance()->p2020PQProfile()); 0625 0626 cs = p2020PQCS; 0627 0628 } else if (profile && profile->isSuitableForOutput()) { 0629 dbgFile << "image has embedded profile: " << profile->name() << "\n"; 0630 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); 0631 } 0632 else { 0633 if (csName.first == RGBAColorModelID.id()) { 0634 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, "sRGB-elle-V2-srgbtrc.icc"); 0635 } else if (csName.first == GrayAColorModelID.id()) { 0636 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, "Gray-D50-elle-V2-srgbtrc.icc"); 0637 } else { 0638 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); 0639 } 0640 0641 //TODO: two fixes : one tell the user about the problem and ask for a solution, and two once the kocolorspace include KoColorTransformation, use that instead of hacking a lcms transformation 0642 // Create the cmsTransform if needed 0643 if (profile) { 0644 transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); 0645 } 0646 } 0647 0648 if (cs == 0) { 0649 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 0650 return ImportExportCodes::FormatColorSpaceUnsupported; 0651 } 0652 0653 // Creating the KisImageSP 0654 if (m_image == 0) { 0655 KisUndoStore *store = m_doc ? m_doc->createUndoStore() : new KisSurrogateUndoStore(); 0656 m_image = new KisImage(store, width, height, cs, "built image"); 0657 } 0658 0659 // Read resolution 0660 int unit_type; 0661 png_uint_32 x_resolution, y_resolution; 0662 0663 png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type); 0664 if (x_resolution > 0 && y_resolution > 0 && unit_type == PNG_RESOLUTION_METER) { 0665 m_image->setResolution((double) POINT_TO_CM(x_resolution) / 100.0, (double) POINT_TO_CM(y_resolution) / 100.0); // It is the "invert" macro because we convert from point-per-inch to points 0666 } 0667 0668 double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1); 0669 KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX); 0670 0671 // Read comments/texts... 0672 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); 0673 if (m_doc) { 0674 KoDocumentInfo * info = m_doc->documentInfo(); 0675 dbgFile << "There are " << num_comments << " comments in the text"; 0676 for (int i = 0; i < num_comments; i++) { 0677 QString key = QString(text_ptr[i].key).toLower(); 0678 dbgFile << "key: " << text_ptr[i].key 0679 << ", containing: " << text_ptr[i].text 0680 << ": " << (key == "raw profile type exif " ? "isExif" : "something else"); 0681 if (key == "title") { 0682 info->setAboutInfo("title", text_ptr[i].text); 0683 } else if (key == "description") { 0684 info->setAboutInfo("comment", text_ptr[i].text); 0685 } else if (key == "author") { 0686 info->setAuthorInfo("creator", text_ptr[i].text); 0687 } else if (key.contains("raw profile type exif")) { 0688 decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6); 0689 } else if (key.contains("raw profile type iptc")) { 0690 decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14); 0691 } else if (key.contains("raw profile type xmp")) { 0692 decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0); 0693 } else if (key == "version") { 0694 m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text))); 0695 } else if (key == "preset") { 0696 m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text))); 0697 } 0698 } 0699 } 0700 // Read image data 0701 QScopedPointer<KisPNGReaderAbstract> reader; 0702 try { 0703 if (interlace_type == PNG_INTERLACE_ADAM7) { 0704 reader.reset(new KisPNGReaderFullImage(png_ptr, info_ptr, width, height)); 0705 } else { 0706 reader.reset(new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height)); 0707 } 0708 } catch (const std::bad_alloc& e) { 0709 // new png_byte[] may raise such an exception if the image 0710 // is invalid / to large. 0711 dbgFile << "bad alloc: " << e.what(); 0712 // Free only the already allocated png_byte instances. 0713 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 0714 return (ImportExportCodes::Failure); 0715 } 0716 0717 // Read the palette if the file is indexed 0718 png_colorp palette ; 0719 int num_palette; 0720 if (color_type == PNG_COLOR_TYPE_PALETTE) { 0721 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); 0722 } 0723 0724 // Read the transparency palette 0725 quint8 palette_alpha[256]; 0726 memset(palette_alpha, 255, 256); 0727 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 0728 if (color_type == PNG_COLOR_TYPE_PALETTE) { 0729 png_bytep alpha_ptr; 0730 int num_alpha; 0731 png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0); 0732 for (int i = 0; i < num_alpha; ++i) { 0733 palette_alpha[i] = alpha_ptr[i]; 0734 } 0735 } 0736 } 0737 0738 for (png_uint_32 y = 0; y < height; y++) { 0739 KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width); 0740 0741 png_bytep row_pointer = reader->readLine(); 0742 0743 switch (color_type) { 0744 case PNG_COLOR_TYPE_GRAY: 0745 case PNG_COLOR_TYPE_GRAY_ALPHA: 0746 if (color_nb_bits == 16) { 0747 quint16 *src = reinterpret_cast<quint16 *>(row_pointer); 0748 do { 0749 quint16 *d = reinterpret_cast<quint16 *>(it->rawData()); 0750 d[0] = *(src++); 0751 if (hasalpha) { 0752 d[1] = *(src++); 0753 } else { 0754 d[1] = quint16_MAX; 0755 } 0756 if (transform) transform->transformInPlace(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1); 0757 } while (it->nextPixel()); 0758 } else { 0759 KisPNGReadStream stream(row_pointer, color_nb_bits); 0760 do { 0761 quint8 *d = it->rawData(); 0762 d[0] = (quint8)(stream.nextValue() * coeff); 0763 if (hasalpha) { 0764 d[1] = (quint8)(stream.nextValue() * coeff); 0765 } else { 0766 d[1] = UCHAR_MAX; 0767 } 0768 if (transform) transform->transformInPlace(d, d, 1); 0769 } while (it->nextPixel()); 0770 } 0771 // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" 0772 break; 0773 case PNG_COLOR_TYPE_RGB: 0774 case PNG_COLOR_TYPE_RGB_ALPHA: 0775 if (color_nb_bits == 16) { 0776 quint16 *src = reinterpret_cast<quint16 *>(row_pointer); 0777 do { 0778 quint16 *d = reinterpret_cast<quint16 *>(it->rawData()); 0779 d[2] = *(src++); 0780 d[1] = *(src++); 0781 d[0] = *(src++); 0782 if (hasalpha) d[3] = *(src++); 0783 else d[3] = quint16_MAX; 0784 if (transform) transform->transformInPlace(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1); 0785 } while (it->nextPixel()); 0786 } else { 0787 KisPNGReadStream stream(row_pointer, color_nb_bits); 0788 do { 0789 quint8 *d = it->rawData(); 0790 d[2] = (quint8)(stream.nextValue() * coeff); 0791 d[1] = (quint8)(stream.nextValue() * coeff); 0792 d[0] = (quint8)(stream.nextValue() * coeff); 0793 if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); 0794 else d[3] = UCHAR_MAX; 0795 if (transform) transform->transformInPlace(d, d, 1); 0796 } while (it->nextPixel()); 0797 } 0798 break; 0799 case PNG_COLOR_TYPE_PALETTE: { 0800 KisPNGReadStream stream(row_pointer, color_nb_bits); 0801 do { 0802 quint8 *d = it->rawData(); 0803 quint8 index = stream.nextValue(); 0804 quint8 alpha = palette_alpha[ index ]; 0805 if (alpha == 0) { 0806 memset(d, 0, 4); 0807 } else { 0808 png_color c = palette[ index ]; 0809 d[2] = c.red; 0810 d[1] = c.green; 0811 d[0] = c.blue; 0812 d[3] = alpha; 0813 } 0814 } while (it->nextPixel()); 0815 } 0816 break; 0817 default: 0818 return ImportExportCodes::FormatFeaturesUnsupported; 0819 } 0820 } 0821 m_image->addNode(layer.data(), m_image->rootLayer().data()); 0822 0823 png_read_end(png_ptr, end_info); 0824 iod->close(); 0825 0826 // Freeing memory 0827 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 0828 return ImportExportCodes::OK; 0829 0830 } 0831 0832 KisImportExportErrorCode KisPNGConverter::buildImage(const QString &filename) 0833 { 0834 m_path = filename; 0835 0836 QFile fp(filename); 0837 if (fp.exists()) { 0838 if (!fp.open(QIODevice::ReadOnly)) { 0839 dbgFile << "Failed to open PNG File"; 0840 return (ImportExportCodes::FileFormatIncorrect); 0841 } 0842 0843 return buildImage(&fp); 0844 } 0845 return (ImportExportCodes::FileNotExist); 0846 0847 } 0848 0849 0850 KisImageSP KisPNGConverter::image() 0851 { 0852 return m_image; 0853 } 0854 0855 bool KisPNGConverter::saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData) 0856 { 0857 if (store->open(filename)) { 0858 KoStoreDevice io(store); 0859 if (!io.open(QIODevice::WriteOnly)) { 0860 dbgFile << "Could not open for writing:" << filename; 0861 return false; 0862 } 0863 KisPNGConverter pngconv(0); 0864 vKisAnnotationSP_it annotIt = 0; 0865 KisMetaData::Store* metaDataStore = 0; 0866 if (metaData) { 0867 metaDataStore = new KisMetaData::Store(*metaData); 0868 } 0869 KisPNGOptions options; 0870 options.compression = 3; 0871 options.interlace = false; 0872 options.tryToSaveAsIndexed = false; 0873 options.alpha = true; 0874 options.saveSRGBProfile = false; 0875 options.downsample = false; 0876 0877 if (dev->colorSpace()->id() != "RGBA") { 0878 dev = new KisPaintDevice(*dev.data()); 0879 dev->convertTo(KoColorSpaceRegistry::instance()->rgb8()); 0880 } 0881 0882 KisImportExportErrorCode success = pngconv.buildFile(&io, imageRect, xRes, yRes, dev, annotIt, annotIt, options, metaDataStore); 0883 if (!success.isOk()) { 0884 dbgFile << "Saving PNG failed:" << filename; 0885 delete metaDataStore; 0886 return false; 0887 } 0888 delete metaDataStore; 0889 io.close(); 0890 if (!store->close()) { 0891 return false; 0892 } 0893 } else { 0894 dbgFile << "Opening of data file failed :" << filename; 0895 return false; 0896 } 0897 return true; 0898 0899 } 0900 0901 0902 KisImportExportErrorCode KisPNGConverter::buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) 0903 { 0904 dbgFile << "Start writing PNG File " << filename; 0905 // Open a QIODevice for writing 0906 QFile fp (filename); 0907 if (!fp.open(QIODevice::WriteOnly)) { 0908 dbgFile << "Failed to open PNG File for writing"; 0909 return (KisImportExportErrorCannotWrite(fp.error())); 0910 } 0911 0912 KisImportExportErrorCode result = buildFile(&fp, imageRect, xRes, yRes, device, annotationsStart, annotationsEnd, options, metaData); 0913 0914 return result; 0915 } 0916 0917 KisImportExportErrorCode KisPNGConverter::buildFile(QIODevice* iodevice, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData) 0918 { 0919 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(device, ImportExportCodes::InternalError); 0920 0921 if (!options.alpha) { 0922 KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); 0923 KoColor c(options.transparencyFillColor, device->colorSpace()); 0924 tmp->fill(imageRect, c); 0925 KisPainter gc(tmp); 0926 gc.bitBlt(imageRect.topLeft(), device, imageRect); 0927 gc.end(); 0928 device = tmp; 0929 } 0930 0931 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) { 0932 options.forceSRGB = false; 0933 } 0934 0935 QStringList colormodels = QStringList() << RGBAColorModelID.id() << GrayAColorModelID.id(); 0936 if (options.saveAsHDR || options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) { 0937 const KoColorSpace *dstCS = 0938 KoColorSpaceRegistry::instance()->colorSpace( 0939 RGBAColorModelID.id(), 0940 device->colorSpace()->colorDepthId().id(), 0941 KoColorSpaceRegistry::instance()->p709SRGBProfile()); 0942 0943 if (options.saveAsHDR) { 0944 dstCS = 0945 KoColorSpaceRegistry::instance()->colorSpace( 0946 RGBAColorModelID.id(), 0947 device->colorSpace()->colorDepthId().id(), 0948 KoColorSpaceRegistry::instance()->p2020PQProfile()); 0949 } 0950 0951 KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); 0952 tmp->makeCloneFromRough(device, imageRect); 0953 tmp->convertTo(dstCS); 0954 0955 device = tmp; 0956 } 0957 0958 if (options.downsample 0959 || device->colorSpace()->colorDepthId() == Float16BitsColorDepthID 0960 || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID 0961 || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID) { 0962 const KoColorSpace* cs = 0963 KoColorSpaceRegistry::instance()->colorSpace( 0964 RGBAColorModelID.id(), 0965 Integer16BitsColorDepthID.id(), 0966 device->colorSpace()->profile()); 0967 0968 if (options.downsample) { 0969 cs = 0970 KoColorSpaceRegistry::instance()->colorSpace( 0971 RGBAColorModelID.id(), 0972 Integer8BitsColorDepthID.id(), 0973 device->colorSpace()->profile()); 0974 } 0975 0976 device = new KisPaintDevice(*device); 0977 device->convertTo(cs); 0978 } 0979 0980 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.tryToSaveAsIndexed) { 0981 options.tryToSaveAsIndexed = false; 0982 } 0983 0984 // Initialize structures 0985 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 0986 if (!png_ptr) { 0987 return (ImportExportCodes::Failure); 0988 } 0989 0990 #ifdef PNG_SET_USER_LIMITS_SUPPORTED 0991 /* Remove the user limits, if any */ 0992 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff); 0993 png_set_chunk_cache_max(png_ptr, 0); 0994 png_set_chunk_malloc_max(png_ptr, 0); 0995 #endif 0996 0997 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning); 0998 #ifdef PNG_BENIGN_ERRORS_SUPPORTED 0999 png_set_benign_errors(png_ptr, 1); 1000 #endif 1001 1002 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED) 1003 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); 1004 #endif 1005 1006 1007 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED 1008 png_set_check_for_invalid_index(png_ptr, 0); 1009 #endif 1010 1011 png_infop info_ptr = png_create_info_struct(png_ptr); 1012 if (!info_ptr) { 1013 png_destroy_write_struct(&png_ptr, (png_infopp)0); 1014 return (ImportExportCodes::Failure); 1015 } 1016 1017 // If an error occurs during writing, libpng will jump here 1018 if (setjmp(png_jmpbuf(png_ptr))) { 1019 png_destroy_write_struct(&png_ptr, &info_ptr); 1020 return (ImportExportCodes::Failure); 1021 } 1022 // Initialize the writing 1023 // png_init_io(png_ptr, fp); 1024 // Setup the progress function 1025 // XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);" 1026 // setProgressTotalSteps(100/*height*/); 1027 1028 /* set the zlib compression level */ 1029 png_set_compression_level(png_ptr, options.compression); 1030 1031 png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn); 1032 1033 /* set other zlib parameters */ 1034 png_set_compression_mem_level(png_ptr, 8); 1035 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); 1036 png_set_compression_window_bits(png_ptr, 15); 1037 png_set_compression_method(png_ptr, 8); 1038 png_set_compression_buffer_size(png_ptr, 8192); 1039 1040 int color_nb_bits = 8 * device->pixelSize() / device->channelCount(); 1041 int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha); 1042 1043 Q_ASSERT(color_type > -1); 1044 1045 // Try to compute a table of color if the colorspace is RGB8f 1046 QScopedArrayPointer<png_color> palette; 1047 int num_palette = 0; 1048 if (!options.alpha && options.tryToSaveAsIndexed && KoID(device->colorSpace()->id()) == KoID("RGBA")) { // png doesn't handle indexed images and alpha, and only have indexed for RGB8 1049 palette.reset(new png_color[255]); 1050 1051 KisSequentialIterator it(device, imageRect); 1052 1053 bool toomuchcolor = false; 1054 while (it.nextPixel()) { 1055 const quint8* c = it.oldRawData(); 1056 bool findit = false; 1057 for (int i = 0; i < num_palette; i++) { 1058 if (palette[i].red == c[2] && 1059 palette[i].green == c[1] && 1060 palette[i].blue == c[0]) { 1061 findit = true; 1062 break; 1063 } 1064 } 1065 if (!findit) { 1066 if (num_palette == 255) { 1067 toomuchcolor = true; 1068 break; 1069 } 1070 palette[num_palette].red = c[2]; 1071 palette[num_palette].green = c[1]; 1072 palette[num_palette].blue = c[0]; 1073 num_palette++; 1074 } 1075 } 1076 1077 if (!toomuchcolor) { 1078 dbgFile << "Found a palette of " << num_palette << " colors"; 1079 color_type = PNG_COLOR_TYPE_PALETTE; 1080 if (num_palette <= 2) { 1081 color_nb_bits = 1; 1082 } else if (num_palette <= 4) { 1083 color_nb_bits = 2; 1084 } else if (num_palette <= 16) { 1085 color_nb_bits = 4; 1086 } else { 1087 color_nb_bits = 8; 1088 } 1089 } else { 1090 palette.reset(); 1091 } 1092 } 1093 1094 int interlace_type = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; 1095 1096 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(color_type >= 0, ImportExportCodes::Failure); 1097 1098 png_set_IHDR(png_ptr, info_ptr, 1099 imageRect.width(), 1100 imageRect.height(), 1101 color_nb_bits, 1102 color_type, interlace_type, 1103 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 1104 1105 // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present 1106 1107 const bool sRGB = *device->colorSpace()->profile() == *KoColorSpaceRegistry::instance()->p709SRGBProfile(); 1108 /* 1109 * This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's 1110 * color management is bugged, so once you give it any incentive to start color managing an sRGB image it 1111 * will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now. 1112 */ 1113 /*if (!options.saveSRGBProfile && sRGB) { 1114 png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); 1115 }*/ 1116 1117 1118 /** TODO: Firefox still opens the image incorrectly if there is gAMA+cHRM tags 1119 * present. According to the standard it should use iCCP tag with higher priority, 1120 * but it doesn't: 1121 * 1122 * "When the iCCP chunk is present, PNG decoders that recognize it and are capable 1123 * of color management [ICC] shall ignore the gAMA and cHRM chunks and use 1124 * the iCCP chunk instead and interpret it according to [ICC-1] and [ICC-1A]" 1125 */ 1126 1127 #if 0 1128 if (options.saveAsHDR) { 1129 // https://www.w3.org/TR/PNG/#11gAMA 1130 #if defined(PNG_GAMMA_SUPPORTED) 1131 // the values are set in accordance of HDR-PNG standard: 1132 // https://www.w3.org/TR/png-hdr-pq/ 1133 1134 png_set_gAMA_fixed(png_ptr, info_ptr, 15000); 1135 dbgFile << "gAMA" << "(Rec 2100)"; 1136 #endif 1137 1138 #if defined PNG_cHRM_SUPPORTED 1139 png_set_cHRM_fixed(png_ptr, info_ptr, 1140 31270, 32900, // white point 1141 70800, 29200, // red 1142 17000, 79700, // green 1143 13100, 4600 // blue 1144 ); 1145 dbgFile << "cHRM" << "(Rec 2100)"; 1146 #endif 1147 } 1148 #endif 1149 1150 1151 // we should ensure we don't access non-existing palette object 1152 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, ImportExportCodes::Failure); 1153 1154 // set the palette 1155 if (color_type == PNG_COLOR_TYPE_PALETTE) { 1156 png_set_PLTE(png_ptr, info_ptr, palette.data(), num_palette); 1157 } 1158 // Save annotation 1159 vKisAnnotationSP_it it = annotationsStart; 1160 while (it != annotationsEnd) { 1161 if (!(*it) || (*it)->type().isEmpty()) { 1162 dbgFile << "Warning: empty annotation"; 1163 it++; 1164 continue; 1165 } 1166 1167 dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size(); 1168 1169 if ((*it) -> type().startsWith(QString("krita_attribute:"))) { // 1170 // Attribute 1171 // XXX: it should be possible to save krita_attributes in the \"CHUNKs\"" 1172 dbgFile << "cannot save this annotation : " << (*it) -> type(); 1173 } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) { 1174 dbgFile << "Saving preset information " << (*it)->description(); 1175 png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text)); 1176 1177 QByteArray keyData = (*it)->description().toLatin1(); 1178 text[0].key = keyData.data(); 1179 text[0].text = (char*)(*it)->annotation().data(); 1180 text[0].text_length = (*it)->annotation().size(); 1181 text[0].compression = -1; 1182 1183 png_set_text(png_ptr, info_ptr, text, 1); 1184 png_free(png_ptr, text); 1185 } 1186 it++; 1187 } 1188 1189 // Save the color profile 1190 const KoColorProfile* colorProfile = device->colorSpace()->profile(); 1191 QByteArray colorProfileData = colorProfile->rawData(); 1192 if (!sRGB || options.saveSRGBProfile) { 1193 1194 #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 1195 const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL"; 1196 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); 1197 #else 1198 // older version of libpng has a problem with constness on the parameters 1199 char typeStringICC[] = "icc"; 1200 char typeStringHDR[] = "ITUR_2100_PQ_FULL"; 1201 char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR; 1202 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size()); 1203 #endif 1204 } 1205 1206 // save comments from the document information 1207 // warning: according to the official png spec, the keys need to be capitalized! 1208 if (m_doc) { 1209 png_text texts[4]; 1210 int nbtexts = 0; 1211 KoDocumentInfo * info = m_doc->documentInfo(); 1212 QString title = info->aboutInfo("title"); 1213 if (!title.isEmpty() && options.storeMetaData) { 1214 fillText(texts + nbtexts, "Title", title); 1215 nbtexts++; 1216 } 1217 QString abstract = info->aboutInfo("subject"); 1218 if (abstract.isEmpty()) { 1219 abstract = info->aboutInfo("abstract"); 1220 } 1221 if (!abstract.isEmpty() && options.storeMetaData) { 1222 QString keywords = info->aboutInfo("keyword"); 1223 if (!keywords.isEmpty()) { 1224 abstract = abstract + " keywords: " + keywords; 1225 } 1226 fillText(texts + nbtexts, "Description", abstract); 1227 nbtexts++; 1228 } 1229 1230 QString license = info->aboutInfo("license"); 1231 if (!license.isEmpty() && options.storeMetaData) { 1232 fillText(texts + nbtexts, "Copyright", license); 1233 nbtexts++; 1234 } 1235 1236 QString author = info->authorInfo("creator"); 1237 if (!author.isEmpty() && options.storeAuthor) { 1238 if (!info->authorContactInfo().isEmpty()) { 1239 QString contact = info->authorContactInfo().at(0); 1240 if (!contact.isEmpty()) { 1241 author = author+"("+contact+")"; 1242 } 1243 } 1244 fillText(texts + nbtexts, "Author", author); 1245 nbtexts++; 1246 } 1247 1248 png_set_text(png_ptr, info_ptr, texts, nbtexts); 1249 } 1250 1251 // Save metadata following imagemagick way 1252 1253 // Save exif 1254 if (metaData && !metaData->empty()) { 1255 if (options.exif) { 1256 dbgFile << "Trying to save exif information"; 1257 1258 KisMetaData::IOBackend *exifIO = KisMetadataBackendRegistry::instance()->value("exif"); 1259 Q_ASSERT(exifIO); 1260 1261 QBuffer buffer; 1262 exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); 1263 writeRawProfile(png_ptr, info_ptr, "exif", buffer.data()); 1264 } 1265 // Save IPTC 1266 if (options.iptc) { 1267 dbgFile << "Trying to save iptc information"; 1268 KisMetaData::IOBackend *iptcIO = KisMetadataBackendRegistry::instance()->value("iptc"); 1269 Q_ASSERT(iptcIO); 1270 1271 QBuffer buffer; 1272 iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); 1273 1274 dbgFile << "IPTC information size is" << buffer.data().size(); 1275 writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data()); 1276 } 1277 // Save XMP 1278 if (options.xmp) { 1279 dbgFile << "Trying to save XMP information"; 1280 KisMetaData::IOBackend *xmpIO = KisMetadataBackendRegistry::instance()->value("xmp"); 1281 Q_ASSERT(xmpIO); 1282 1283 QBuffer buffer; 1284 xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader); 1285 1286 dbgFile << "XMP information size is" << buffer.data().size(); 1287 writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data()); 1288 } 1289 } 1290 #if 0 // Unimplemented? 1291 // Save resolution 1292 int unit_type; 1293 png_uint_32 x_resolution, y_resolution; 1294 #endif 1295 png_set_pHYs(png_ptr, info_ptr, CM_TO_POINT(xRes) * 100.0, CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER); // It is the "invert" macro because we convert from point-per-inch to points 1296 1297 // Save the information to the file 1298 png_write_info(png_ptr, info_ptr); 1299 png_write_flush(png_ptr); 1300 1301 // swap byteorder on little endian machines. 1302 #ifndef WORDS_BIGENDIAN 1303 if (color_nb_bits > 8) 1304 png_set_swap(png_ptr); 1305 #endif 1306 1307 // Write the PNG 1308 // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0); 1309 1310 struct RowPointersStruct { 1311 RowPointersStruct(const QSize &size, int pixelSize) 1312 : numRows(size.height()) 1313 { 1314 rows = new png_byte*[numRows]; 1315 1316 for (int i = 0; i < numRows; i++) { 1317 rows[i] = new png_byte[size.width() * pixelSize]; 1318 } 1319 } 1320 1321 ~RowPointersStruct() { 1322 for (int i = 0; i < numRows; i++) { 1323 delete[] rows[i]; 1324 } 1325 delete[] rows; 1326 } 1327 1328 const int numRows = 0; 1329 png_byte** rows = 0; 1330 }; 1331 1332 1333 // Fill the data structure 1334 RowPointersStruct rowPointers(imageRect.size(), device->pixelSize()); 1335 1336 int row = 0; 1337 for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) { 1338 KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width()); 1339 1340 switch (color_type) { 1341 case PNG_COLOR_TYPE_GRAY: 1342 case PNG_COLOR_TYPE_GRAY_ALPHA: 1343 if (color_nb_bits == 16) { 1344 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]); 1345 do { 1346 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData()); 1347 *(dst++) = d[0]; 1348 if (options.alpha) *(dst++) = d[1]; 1349 } while (it->nextPixel()); 1350 } else { 1351 quint8 *dst = rowPointers.rows[row]; 1352 do { 1353 const quint8 *d = it->oldRawData(); 1354 *(dst++) = d[0]; 1355 if (options.alpha) *(dst++) = d[1]; 1356 } while (it->nextPixel()); 1357 } 1358 break; 1359 case PNG_COLOR_TYPE_RGB: 1360 case PNG_COLOR_TYPE_RGB_ALPHA: 1361 if (color_nb_bits == 16) { 1362 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]); 1363 do { 1364 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData()); 1365 *(dst++) = d[2]; 1366 *(dst++) = d[1]; 1367 *(dst++) = d[0]; 1368 if (options.alpha) *(dst++) = d[3]; 1369 } while (it->nextPixel()); 1370 } else { 1371 quint8 *dst = rowPointers.rows[row]; 1372 do { 1373 const quint8 *d = it->oldRawData(); 1374 *(dst++) = d[2]; 1375 *(dst++) = d[1]; 1376 *(dst++) = d[0]; 1377 if (options.alpha) *(dst++) = d[3]; 1378 } while (it->nextPixel()); 1379 } 1380 break; 1381 case PNG_COLOR_TYPE_PALETTE: { 1382 quint8 *dst = rowPointers.rows[row]; 1383 KisPNGWriteStream writestream(dst, color_nb_bits); 1384 do { 1385 const quint8 *d = it->oldRawData(); 1386 int i; 1387 for (i = 0; i < num_palette; i++) { 1388 if (palette[i].red == d[2] && 1389 palette[i].green == d[1] && 1390 palette[i].blue == d[0]) { 1391 break; 1392 } 1393 } 1394 writestream.setNextValue(i); 1395 } while (it->nextPixel()); 1396 } 1397 break; 1398 default: 1399 return ImportExportCodes::FormatColorSpaceUnsupported; 1400 } 1401 } 1402 1403 png_write_image(png_ptr, rowPointers.rows); 1404 1405 // Writing is over 1406 png_write_end(png_ptr, info_ptr); 1407 1408 // Free memory 1409 png_destroy_write_struct(&png_ptr, &info_ptr); 1410 return ImportExportCodes::OK; 1411 } 1412 1413 1414 void KisPNGConverter::cancel() 1415 { 1416 m_stop = true; 1417 } 1418 1419 void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) 1420 { 1421 if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return; 1422 // setProgress(row_number); 1423 } 1424 1425 bool KisPNGConverter::isColorSpaceSupported(const KoColorSpace *cs) 1426 { 1427 return colorSpaceIdSupported(cs->id()); 1428 } 1429 1430