File indexing completed on 2024-05-12 16:01:40
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 byteorder 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 pointer-per-inchs 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 if (device->colorSpace()->colorDepthId() == Float16BitsColorDepthID 0932 || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID 0933 || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID 0934 || options.saveAsHDR 0935 || (device->colorSpace()->colorDepthId() != Integer8BitsColorDepthID && options.downsample)) { 0936 0937 const KoColorSpace *dstCS = 0938 KoColorSpaceRegistry::instance()->colorSpace( 0939 device->colorSpace()->colorModelId().id(), 0940 options.downsample ? Integer8BitsColorDepthID.id() : Integer16BitsColorDepthID.id(), 0941 device->colorSpace()->profile()); 0942 0943 if (options.saveAsHDR) { 0944 dstCS = 0945 KoColorSpaceRegistry::instance()->colorSpace( 0946 RGBAColorModelID.id(), 0947 Integer16BitsColorDepthID.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 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) { 0959 options.forceSRGB = false; 0960 } 0961 0962 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.tryToSaveAsIndexed) { 0963 options.tryToSaveAsIndexed = false; 0964 } 0965 0966 QStringList colormodels = QStringList() << RGBAColorModelID.id() << GrayAColorModelID.id(); 0967 if (options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) { 0968 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), options.downsample ? Integer8BitsColorDepthID.id() : device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); 0969 device = new KisPaintDevice(*device); 0970 device->convertTo(cs); 0971 } 0972 0973 // Initialize structures 0974 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 0975 if (!png_ptr) { 0976 return (ImportExportCodes::Failure); 0977 } 0978 0979 #ifdef PNG_SET_USER_LIMITS_SUPPORTED 0980 /* Remove the user limits, if any */ 0981 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff); 0982 png_set_chunk_cache_max(png_ptr, 0); 0983 png_set_chunk_malloc_max(png_ptr, 0); 0984 #endif 0985 0986 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning); 0987 #ifdef PNG_BENIGN_ERRORS_SUPPORTED 0988 png_set_benign_errors(png_ptr, 1); 0989 #endif 0990 0991 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED) 0992 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); 0993 #endif 0994 0995 0996 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED 0997 png_set_check_for_invalid_index(png_ptr, 0); 0998 #endif 0999 1000 png_infop info_ptr = png_create_info_struct(png_ptr); 1001 if (!info_ptr) { 1002 png_destroy_write_struct(&png_ptr, (png_infopp)0); 1003 return (ImportExportCodes::Failure); 1004 } 1005 1006 // If an error occurs during writing, libpng will jump here 1007 if (setjmp(png_jmpbuf(png_ptr))) { 1008 png_destroy_write_struct(&png_ptr, &info_ptr); 1009 return (ImportExportCodes::Failure); 1010 } 1011 // Initialize the writing 1012 // png_init_io(png_ptr, fp); 1013 // Setup the progress function 1014 // XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);" 1015 // setProgressTotalSteps(100/*height*/); 1016 1017 /* set the zlib compression level */ 1018 png_set_compression_level(png_ptr, options.compression); 1019 1020 png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn); 1021 1022 /* set other zlib parameters */ 1023 png_set_compression_mem_level(png_ptr, 8); 1024 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); 1025 png_set_compression_window_bits(png_ptr, 15); 1026 png_set_compression_method(png_ptr, 8); 1027 png_set_compression_buffer_size(png_ptr, 8192); 1028 1029 int color_nb_bits = 8 * device->pixelSize() / device->channelCount(); 1030 int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha); 1031 1032 Q_ASSERT(color_type > -1); 1033 1034 // Try to compute a table of color if the colorspace is RGB8f 1035 QScopedArrayPointer<png_color> palette; 1036 int num_palette = 0; 1037 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 1038 palette.reset(new png_color[255]); 1039 1040 KisSequentialIterator it(device, imageRect); 1041 1042 bool toomuchcolor = false; 1043 while (it.nextPixel()) { 1044 const quint8* c = it.oldRawData(); 1045 bool findit = false; 1046 for (int i = 0; i < num_palette; i++) { 1047 if (palette[i].red == c[2] && 1048 palette[i].green == c[1] && 1049 palette[i].blue == c[0]) { 1050 findit = true; 1051 break; 1052 } 1053 } 1054 if (!findit) { 1055 if (num_palette == 255) { 1056 toomuchcolor = true; 1057 break; 1058 } 1059 palette[num_palette].red = c[2]; 1060 palette[num_palette].green = c[1]; 1061 palette[num_palette].blue = c[0]; 1062 num_palette++; 1063 } 1064 } 1065 1066 if (!toomuchcolor) { 1067 dbgFile << "Found a palette of " << num_palette << " colors"; 1068 color_type = PNG_COLOR_TYPE_PALETTE; 1069 if (num_palette <= 2) { 1070 color_nb_bits = 1; 1071 } else if (num_palette <= 4) { 1072 color_nb_bits = 2; 1073 } else if (num_palette <= 16) { 1074 color_nb_bits = 4; 1075 } else { 1076 color_nb_bits = 8; 1077 } 1078 } else { 1079 palette.reset(); 1080 } 1081 } 1082 1083 int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; 1084 1085 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(color_type >= 0, ImportExportCodes::Failure); 1086 1087 png_set_IHDR(png_ptr, info_ptr, 1088 imageRect.width(), 1089 imageRect.height(), 1090 color_nb_bits, 1091 color_type, interlacetype, 1092 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 1093 1094 // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present 1095 1096 const bool sRGB = *device->colorSpace()->profile() == *KoColorSpaceRegistry::instance()->p709SRGBProfile(); 1097 /* 1098 * This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's 1099 * color management is bugged, so once you give it any incentive to start color managing an sRGB image it 1100 * will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now. 1101 */ 1102 /*if (!options.saveSRGBProfile && sRGB) { 1103 png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); 1104 }*/ 1105 1106 1107 /** TODO: Firefox still opens the image incorrectly if there is gAMA+cHRM tags 1108 * present. According to the standard it should use iCCP tag with higher priority, 1109 * but it doesn't: 1110 * 1111 * "When the iCCP chunk is present, PNG decoders that recognize it and are capable 1112 * of color management [ICC] shall ignore the gAMA and cHRM chunks and use 1113 * the iCCP chunk instead and interpret it according to [ICC-1] and [ICC-1A]" 1114 */ 1115 1116 #if 0 1117 if (options.saveAsHDR) { 1118 // https://www.w3.org/TR/PNG/#11gAMA 1119 #if defined(PNG_GAMMA_SUPPORTED) 1120 // the values are set in accordance of HDR-PNG standard: 1121 // https://www.w3.org/TR/png-hdr-pq/ 1122 1123 png_set_gAMA_fixed(png_ptr, info_ptr, 15000); 1124 dbgFile << "gAMA" << "(Rec 2100)"; 1125 #endif 1126 1127 #if defined PNG_cHRM_SUPPORTED 1128 png_set_cHRM_fixed(png_ptr, info_ptr, 1129 31270, 32900, // white point 1130 70800, 29200, // red 1131 17000, 79700, // green 1132 13100, 4600 // blue 1133 ); 1134 dbgFile << "cHRM" << "(Rec 2100)"; 1135 #endif 1136 } 1137 #endif 1138 1139 1140 // we should ensure we don't access non-existing palette object 1141 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, ImportExportCodes::Failure); 1142 1143 // set the palette 1144 if (color_type == PNG_COLOR_TYPE_PALETTE) { 1145 png_set_PLTE(png_ptr, info_ptr, palette.data(), num_palette); 1146 } 1147 // Save annotation 1148 vKisAnnotationSP_it it = annotationsStart; 1149 while (it != annotationsEnd) { 1150 if (!(*it) || (*it)->type().isEmpty()) { 1151 dbgFile << "Warning: empty annotation"; 1152 it++; 1153 continue; 1154 } 1155 1156 dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size(); 1157 1158 if ((*it) -> type().startsWith(QString("krita_attribute:"))) { // 1159 // Attribute 1160 // XXX: it should be possible to save krita_attributes in the \"CHUNKs\"" 1161 dbgFile << "cannot save this annotation : " << (*it) -> type(); 1162 } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) { 1163 dbgFile << "Saving preset information " << (*it)->description(); 1164 png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text)); 1165 1166 QByteArray keyData = (*it)->description().toLatin1(); 1167 text[0].key = keyData.data(); 1168 text[0].text = (char*)(*it)->annotation().data(); 1169 text[0].text_length = (*it)->annotation().size(); 1170 text[0].compression = -1; 1171 1172 png_set_text(png_ptr, info_ptr, text, 1); 1173 png_free(png_ptr, text); 1174 } 1175 it++; 1176 } 1177 1178 // Save the color profile 1179 const KoColorProfile* colorProfile = device->colorSpace()->profile(); 1180 QByteArray colorProfileData = colorProfile->rawData(); 1181 if (!sRGB || options.saveSRGBProfile) { 1182 1183 #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 1184 const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL"; 1185 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); 1186 #else 1187 // older version of libpng has a problem with constness on the parameters 1188 char typeStringICC[] = "icc"; 1189 char typeStringHDR[] = "ITUR_2100_PQ_FULL"; 1190 char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR; 1191 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size()); 1192 #endif 1193 } 1194 1195 // save comments from the document information 1196 // warning: according to the official png spec, the keys need to be capitalized! 1197 if (m_doc) { 1198 png_text texts[4]; 1199 int nbtexts = 0; 1200 KoDocumentInfo * info = m_doc->documentInfo(); 1201 QString title = info->aboutInfo("title"); 1202 if (!title.isEmpty() && options.storeMetaData) { 1203 fillText(texts + nbtexts, "Title", title); 1204 nbtexts++; 1205 } 1206 QString abstract = info->aboutInfo("subject"); 1207 if (abstract.isEmpty()) { 1208 abstract = info->aboutInfo("abstract"); 1209 } 1210 if (!abstract.isEmpty() && options.storeMetaData) { 1211 QString keywords = info->aboutInfo("keyword"); 1212 if (!keywords.isEmpty()) { 1213 abstract = abstract + " keywords: " + keywords; 1214 } 1215 fillText(texts + nbtexts, "Description", abstract); 1216 nbtexts++; 1217 } 1218 1219 QString license = info->aboutInfo("license"); 1220 if (!license.isEmpty() && options.storeMetaData) { 1221 fillText(texts + nbtexts, "Copyright", license); 1222 nbtexts++; 1223 } 1224 1225 QString author = info->authorInfo("creator"); 1226 if (!author.isEmpty() && options.storeAuthor) { 1227 if (!info->authorContactInfo().isEmpty()) { 1228 QString contact = info->authorContactInfo().at(0); 1229 if (!contact.isEmpty()) { 1230 author = author+"("+contact+")"; 1231 } 1232 } 1233 fillText(texts + nbtexts, "Author", author); 1234 nbtexts++; 1235 } 1236 1237 png_set_text(png_ptr, info_ptr, texts, nbtexts); 1238 } 1239 1240 // Save metadata following imagemagick way 1241 1242 // Save exif 1243 if (metaData && !metaData->empty()) { 1244 if (options.exif) { 1245 dbgFile << "Trying to save exif information"; 1246 1247 KisMetaData::IOBackend *exifIO = KisMetadataBackendRegistry::instance()->value("exif"); 1248 Q_ASSERT(exifIO); 1249 1250 QBuffer buffer; 1251 exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); 1252 writeRawProfile(png_ptr, info_ptr, "exif", buffer.data()); 1253 } 1254 // Save IPTC 1255 if (options.iptc) { 1256 dbgFile << "Trying to save iptc information"; 1257 KisMetaData::IOBackend *iptcIO = KisMetadataBackendRegistry::instance()->value("iptc"); 1258 Q_ASSERT(iptcIO); 1259 1260 QBuffer buffer; 1261 iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader); 1262 1263 dbgFile << "IPTC information size is" << buffer.data().size(); 1264 writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data()); 1265 } 1266 // Save XMP 1267 if (options.xmp) { 1268 dbgFile << "Trying to save XMP information"; 1269 KisMetaData::IOBackend *xmpIO = KisMetadataBackendRegistry::instance()->value("xmp"); 1270 Q_ASSERT(xmpIO); 1271 1272 QBuffer buffer; 1273 xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader); 1274 1275 dbgFile << "XMP information size is" << buffer.data().size(); 1276 writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data()); 1277 } 1278 } 1279 #if 0 // Unimplemented? 1280 // Save resolution 1281 int unit_type; 1282 png_uint_32 x_resolution, y_resolution; 1283 #endif 1284 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 pointer-per-inchs to points 1285 1286 // Save the information to the file 1287 png_write_info(png_ptr, info_ptr); 1288 png_write_flush(png_ptr); 1289 1290 // swap byteorder on little endian machines. 1291 #ifndef WORDS_BIGENDIAN 1292 if (color_nb_bits > 8) 1293 png_set_swap(png_ptr); 1294 #endif 1295 1296 // Write the PNG 1297 // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0); 1298 1299 struct RowPointersStruct { 1300 RowPointersStruct(const QSize &size, int pixelSize) 1301 : numRows(size.height()) 1302 { 1303 rows = new png_byte*[numRows]; 1304 1305 for (int i = 0; i < numRows; i++) { 1306 rows[i] = new png_byte[size.width() * pixelSize]; 1307 } 1308 } 1309 1310 ~RowPointersStruct() { 1311 for (int i = 0; i < numRows; i++) { 1312 delete[] rows[i]; 1313 } 1314 delete[] rows; 1315 } 1316 1317 const int numRows = 0; 1318 png_byte** rows = 0; 1319 }; 1320 1321 1322 // Fill the data structure 1323 RowPointersStruct rowPointers(imageRect.size(), device->pixelSize()); 1324 1325 int row = 0; 1326 for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) { 1327 KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width()); 1328 1329 switch (color_type) { 1330 case PNG_COLOR_TYPE_GRAY: 1331 case PNG_COLOR_TYPE_GRAY_ALPHA: 1332 if (color_nb_bits == 16) { 1333 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]); 1334 do { 1335 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData()); 1336 *(dst++) = d[0]; 1337 if (options.alpha) *(dst++) = d[1]; 1338 } while (it->nextPixel()); 1339 } else { 1340 quint8 *dst = rowPointers.rows[row]; 1341 do { 1342 const quint8 *d = it->oldRawData(); 1343 *(dst++) = d[0]; 1344 if (options.alpha) *(dst++) = d[1]; 1345 } while (it->nextPixel()); 1346 } 1347 break; 1348 case PNG_COLOR_TYPE_RGB: 1349 case PNG_COLOR_TYPE_RGB_ALPHA: 1350 if (color_nb_bits == 16) { 1351 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]); 1352 do { 1353 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData()); 1354 *(dst++) = d[2]; 1355 *(dst++) = d[1]; 1356 *(dst++) = d[0]; 1357 if (options.alpha) *(dst++) = d[3]; 1358 } while (it->nextPixel()); 1359 } else { 1360 quint8 *dst = rowPointers.rows[row]; 1361 do { 1362 const quint8 *d = it->oldRawData(); 1363 *(dst++) = d[2]; 1364 *(dst++) = d[1]; 1365 *(dst++) = d[0]; 1366 if (options.alpha) *(dst++) = d[3]; 1367 } while (it->nextPixel()); 1368 } 1369 break; 1370 case PNG_COLOR_TYPE_PALETTE: { 1371 quint8 *dst = rowPointers.rows[row]; 1372 KisPNGWriteStream writestream(dst, color_nb_bits); 1373 do { 1374 const quint8 *d = it->oldRawData(); 1375 int i; 1376 for (i = 0; i < num_palette; i++) { 1377 if (palette[i].red == d[2] && 1378 palette[i].green == d[1] && 1379 palette[i].blue == d[0]) { 1380 break; 1381 } 1382 } 1383 writestream.setNextValue(i); 1384 } while (it->nextPixel()); 1385 } 1386 break; 1387 default: 1388 return ImportExportCodes::FormatColorSpaceUnsupported; 1389 } 1390 } 1391 1392 png_write_image(png_ptr, rowPointers.rows); 1393 1394 // Writing is over 1395 png_write_end(png_ptr, info_ptr); 1396 1397 // Free memory 1398 png_destroy_write_struct(&png_ptr, &info_ptr); 1399 return ImportExportCodes::OK; 1400 } 1401 1402 1403 void KisPNGConverter::cancel() 1404 { 1405 m_stop = true; 1406 } 1407 1408 void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) 1409 { 1410 if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return; 1411 // setProgress(row_number); 1412 } 1413 1414 bool KisPNGConverter::isColorSpaceSupported(const KoColorSpace *cs) 1415 { 1416 return colorSpaceIdSupported(cs->id()); 1417 } 1418 1419