Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_png_converter.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005-2007 Cyrille Berger <cberger@cberger.net>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 */
7
8#include "kis_png_converter.h"
9// A big thank to Glenn Randers-Pehrson for his wonderful
10// documentation of libpng available at
11// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
12
13#ifndef PNG_MAX_UINT // Removed in libpng 1.4
14#define PNG_MAX_UINT PNG_UINT_31_MAX
15#endif
16
17#include <KoConfig.h> // WORDS_BIGENDIAN
18#include <KoStore.h>
19#include <KoStoreDevice.h>
20
21#include <limits.h>
22#include <stdio.h>
23#include <zlib.h>
24
25#include <QBuffer>
26#include <QFile>
27#include <QApplication>
28
29#include <klocalizedstring.h>
30#include <QUrl>
31
32#include <KoColorSpace.h>
33#include <KoDocumentInfo.h>
34#include <KoID.h>
36#include <KoColorProfile.h>
37#include <KoColor.h>
38#include <KoUnit.h>
39
41#include "kis_clipboard.h"
42#include "kis_undo_stores.h"
43#include <KisDocument.h>
45#include <kis_config.h>
47#include <kis_group_layer.h>
48#include <kis_image.h>
49#include <kis_iterator_ng.h>
50#include <kis_layer.h>
52#include <kis_meta_data_store.h>
53#include <kis_paint_device.h>
54#include <kis_paint_layer.h>
55#include <kis_painter.h>
56#include <kis_transaction.h>
57
58#include <kis_assert.h>
59
60namespace
61{
62
63int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha)
64{
65
66 QString id = cs->id();
67
68 if (id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16") {
69 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
70 }
71 if (id == "RGBA" || id == "RGBA16") {
72 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
73 }
74
75 return -1;
76
77}
78
79bool colorSpaceIdSupported(const QString &id)
80{
81 return id == "RGBA" || id == "RGBA16" ||
82 id == "GRAYA" || id == "GRAYAU16" || id == "GRAYA16";
83}
84
85QPair<QString, QString> getColorSpaceForColorType(int color_type, int color_nb_bits)
86{
87 QPair<QString, QString> r;
88
89 if (color_type == PNG_COLOR_TYPE_PALETTE) {
90 r.first = RGBAColorModelID.id();
91 r.second = Integer8BitsColorDepthID.id();
92 } else {
93 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
94 r.first = GrayAColorModelID.id();
95 } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
96 r.first = RGBAColorModelID.id();
97 }
98 if (color_nb_bits == 16) {
99 r.second = Integer16BitsColorDepthID.id();
100 } else if (color_nb_bits <= 8) {
101 r.second = Integer8BitsColorDepthID.id();
102 }
103 }
104 return r;
105}
106
107
108void fillText(png_text* p_text, const char* key, QString& text)
109{
110 p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
111 p_text->key = const_cast<char *>(key);
112 char* textc = new char[text.length()+1];
113 strcpy(textc, text.toLatin1());
114 p_text->text = textc;
115 p_text->text_length = text.length() + 1;
116}
117
118long formatStringList(char *string, const size_t length, const char *format, va_list operands)
119{
120 int n = vsnprintf(string, length, format, operands);
121
122 if (n < 0)
123 string[length-1] = '\0';
124
125 return((long) n);
126}
127
128long formatString(char *string, const size_t length, const char *format, ...)
129{
130 long n;
131
132 va_list operands;
133
134 va_start(operands, format);
135 n = (long) formatStringList(string, length, format, operands);
136 va_end(operands);
137 return(n);
138}
139
140void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data)
141{
142
143 png_textp text;
144
145 png_uint_32 allocated_length, description_length;
146
147 const uchar hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
148
149 dbgFile << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << Qt::endl;
150
151 text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
152 description_length = profile_type.length();
153 allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length);
154
155 text[0].text = (png_charp) png_malloc(ping, allocated_length);
156 memset(text[0].text, 0, allocated_length);
157
158 QString key = QLatin1String("Raw profile type ") + profile_type.toLatin1();
159 QByteArray keyData = key.toLatin1();
160 text[0].key = keyData.data();
161
162 uchar* sp = (uchar*)profile_data.data();
163 png_charp dp = text[0].text;
164 *dp++ = '\n';
165
166 memcpy(dp, profile_type.toLatin1().constData(), profile_type.length());
167
168 dp += description_length;
169 *dp++ = '\n';
170
171 formatString(dp, allocated_length - strlen(text[0].text), "%8lu ", (unsigned long)profile_data.length());
172
173 dp += 8;
174
175 for (long i = 0; i < (long) profile_data.length(); i++) {
176 if (i % 36 == 0)
177 *dp++ = '\n';
178
179 *(dp++) = (char) hex[((*sp >> 4) & 0x0f)];
180 *(dp++) = (char) hex[((*sp++) & 0x0f)];
181 }
182
183 *dp++ = '\n';
184 *dp = '\0';
185 text[0].text_length = (png_size_t)(dp - text[0].text);
186 text[0].compression = -1;
187
188 if (text[0].text_length <= allocated_length)
189 png_set_text(ping, ping_info, text, 1);
190
191 png_free(ping, text[0].text);
192 png_free(ping, text);
193}
194
195QByteArray png_read_raw_profile(png_textp text)
196{
197 QByteArray profile;
198
199 static const unsigned char unhex[103] = {
200 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
201 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
202 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
205 13, 14, 15
206 };
207
208 png_charp sp = text[0].text + 1;
209 /* look for newline */
210 while (*sp != '\n')
211 sp++;
212 /* look for length */
213 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
214 sp++;
215 png_uint_32 length = (png_uint_32) atol(sp);
216 while (*sp != ' ' && *sp != '\n')
217 sp++;
218 if (length == 0) {
219 return profile;
220 }
221 profile.resize(length);
222 /* copy profile, skipping white space and column 1 "=" signs */
223 unsigned char *dp = (unsigned char*)profile.data();
224 png_uint_32 nibbles = length * 2;
225 for (png_uint_32 i = 0; i < nibbles; i++) {
226 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f') {
227 if (*sp == '\0') {
228 return QByteArray();
229 }
230 sp++;
231 }
232 if (i % 2 == 0)
233 *dp = (unsigned char)(16 * unhex[(int) *sp++]);
234 else
235 (*dp++) += unhex[(int) *sp++];
236 }
237 return profile;
238}
239
240void decode_meta_data(png_textp text, KisMetaData::Store* store, QString type, int headerSize)
241{
242 dbgFile << "Decoding " << type << " " << text[0].key;
244 Q_ASSERT(exifIO);
245
246 QByteArray rawProfile = png_read_raw_profile(text);
247 if (headerSize > 0) {
248 rawProfile.remove(0, headerSize);
249 }
250 if (rawProfile.size() > 0) {
251 QBuffer buffer;
252 buffer.setData(rawProfile);
253 exifIO->loadFrom(store, &buffer);
254 } else {
255 dbgFile << "Decoding failed";
256 }
257}
258}
259
260extern "C" {
261static void kis_png_warning(png_structp /*png_ptr*/, png_const_charp message)
262{
263 qWarning("libpng warning: %s", message);
264}
265
266}
267
268
269
270
272{
273 // Q_ASSERT(doc);
274 // Q_ASSERT(adapter);
275
276 m_doc = doc;
277 m_stop = false;
278 m_max_row = 0;
279 m_image = 0;
280 m_batchMode = batchMode;
281}
282
286
288{
289public:
290 KisPNGReadStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) {
291 }
292 int nextValue() {
293 if (m_posinc == 0) {
294 m_posinc = 8;
295 m_buf++;
296 }
297 m_posinc -= m_depth;
298 return (((*m_buf) >> (m_posinc)) & ((1 << m_depth) - 1));
299 }
300private:
302 quint8* m_buf;
303};
304
306{
307public:
308 KisPNGWriteStream(quint8* buf, quint32 depth) : m_posinc(8), m_depth(depth), m_buf(buf) {
309 *m_buf = 0;
310 }
311 void setNextValue(int v) {
312 if (m_posinc == 0) {
313 m_posinc = 8;
314 m_buf++;
315 *m_buf = 0;
316 }
317 m_posinc -= m_depth;
318 *m_buf = (v << m_posinc) | *m_buf;
319 }
320private:
322 quint8* m_buf;
323};
324
326{
327public:
328 KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height) : png_ptr(_png_ptr), width(_width), height(_height) {}
330 virtual png_bytep readLine() = 0;
331protected:
332 png_structp png_ptr;
334};
335
337{
338public:
339 KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height) {
340 std::size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
341 row_pointer = new png_byte[rowbytes];
342 }
344 delete[] row_pointer;
345 }
346 png_bytep readLine() override {
347 png_read_row(png_ptr, row_pointer, 0);
348 return row_pointer;
349 }
350private:
351 png_bytep row_pointer;
352};
353
355{
356public:
357 KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height) : KisPNGReaderAbstract(_png_ptr, _width, _height), y(0) {
358 row_pointers = new png_bytep[height];
359 std::size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
360 for (int i = 0; i < height; i++) {
361 row_pointers[i] = new png_byte[rowbytes];
362 }
363 png_read_image(png_ptr, row_pointers);
364 }
366 for (int i = 0; i < height; i++) {
367 delete[] row_pointers[i];
368 }
369 delete[] row_pointers;
370 }
371 png_bytep readLine() override {
372 return row_pointers[y++];
373 }
374private:
375 png_bytepp row_pointers;
376 int y;
377};
378
379
380static
381void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
382{
383 QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
384
385 while (length) {
386 int nr = in->read((char*)data, length);
387 if (nr <= 0) {
388 png_error(png_ptr, "Read Error");
389 return;
390 }
391 length -= nr;
392 }
393}
394
395static
396void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
397{
398 QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr);
399
400 uint nr = out->write((char*)data, length);
401 if (nr != length) {
402 png_error(png_ptr, "Write Error");
403 return;
404 }
405}
406
407static
408void _flush_fn(png_structp png_ptr)
409{
410 Q_UNUSED(png_ptr);
411}
412
414{
415 dbgFile << "Start decoding PNG File";
416
417 png_byte signature[8];
418 iod->peek((char*)signature, 8);
419
420#if PNG_LIBPNG_VER < 10400
421 if (!png_check_sig(signature, 8)) {
422#else
423 if (png_sig_cmp(signature, 0, 8) != 0) {
424#endif
425 iod->close();
427 }
428
429 // Initialize the internal structures
430 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
431
432 if (!png_ptr) {
433 iod->close();
434 }
435
436#ifdef PNG_SET_USER_LIMITS_SUPPORTED
437 /* Remove the user limits, if any */
438 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
439 png_set_chunk_cache_max(png_ptr, 0);
440 png_set_chunk_malloc_max(png_ptr, 0);
441#endif
442
443 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning);
444 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
445 png_set_benign_errors(png_ptr, 1);
446 #endif
447
448 png_infop info_ptr = png_create_info_struct(png_ptr);
449 if (!info_ptr) {
450 png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0);
451 iod->close();
453 }
454
455 png_infop end_info = png_create_info_struct(png_ptr);
456 if (!end_info) {
457 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
458 iod->close();
460 }
461
462 // Catch errors
463 if (setjmp(png_jmpbuf(png_ptr))) {
464 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
465 iod->close();
467 }
468
469 // Initialize the special
470 png_set_read_fn(png_ptr, iod, _read_fn);
471
472#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
473 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
474#endif
475
476 // read all PNG info up to image data
477 png_read_info(png_ptr, info_ptr);
478
479
480 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) {
481 png_set_expand(png_ptr);
482 }
483
484 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) {
485 png_set_packing(png_ptr);
486 }
487
488
489 if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE &&
490 (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) {
491 png_set_expand(png_ptr);
492 }
493 png_read_update_info(png_ptr, info_ptr);
494
495 // Read information about the png
496 png_uint_32 width, height;
497 int color_nb_bits, color_type, interlace_type;
498 png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, 0, 0);
499 dbgFile << "width = " << width << " height = " << height << " color_nb_bits = " << color_nb_bits << " color_type = " << color_type << " interlace_type = " << interlace_type << Qt::endl;
500 // swap byte order on little endian machines.
501#ifndef WORDS_BIGENDIAN
502 if (color_nb_bits > 8)
503 png_set_swap(png_ptr);
504#endif
505
506 // Determine the colorspace
507 QPair<QString, QString> csName = getColorSpaceForColorType(color_type, color_nb_bits);
508 if (csName.first.isEmpty()) {
509 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
510 iod->close();
512 }
513 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
514
515 // Read image profile
516 png_charp profile_name;
517#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
518 png_bytep profile_data;
519#else
520 png_charp profile_data;
521#endif
522 int compression_type;
523 png_uint_32 proflen;
524
525 // Get the various optional chunks
526
527 // https://www.w3.org/TR/PNG/#11cHRM
528#if defined(PNG_cHRM_SUPPORTED)
529 double whitePointX, whitePointY;
530 double redX, redY;
531 double greenX, greenY;
532 double blueX, blueY;
533 png_get_cHRM(png_ptr,info_ptr, &whitePointX, &whitePointY, &redX, &redY, &greenX, &greenY, &blueX, &blueY);
534 dbgFile << "cHRM:" << whitePointX << whitePointY << redX << redY << greenX << greenY << blueX << blueY;
535#endif
536
537 // https://www.w3.org/TR/PNG/#11gAMA
538#if defined(PNG_GAMMA_SUPPORTED)
539 double gamma;
540 png_get_gAMA(png_ptr, info_ptr, &gamma);
541 dbgFile << "gAMA" << gamma;
542#endif
543
544 // https://www.w3.org/TR/PNG/#11sRGB
545#if defined(PNG_sRGB_SUPPORTED)
546 int sRGBIntent;
547 png_get_sRGB(png_ptr, info_ptr, &sRGBIntent);
548 dbgFile << "sRGB" << sRGBIntent;
549#endif
550
551 bool fromBlender = false;
552
553 png_text* text_ptr;
554 int num_comments;
555 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
556
557 for (int i = 0; i < num_comments; i++) {
558 QString key = QString(text_ptr[i].key).toLower();
559 if (key == "file") {
560 QString relatedFile = text_ptr[i].text;
561 if (relatedFile.contains(".blend", Qt::CaseInsensitive)){
562 fromBlender=true;
563 }
564 }
565 }
566
567 bool loadedImageIsHDR = false;
569
570 if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) {
571 QByteArray profile_rawdata(reinterpret_cast<char*>(profile_data), proflen);
572 profile = KoColorSpaceRegistry::instance()->createColorProfile(csName.first, csName.second, profile_rawdata);
573 if (profile) {
574 if (!profile->isSuitableForWorkspace()) {
575 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";
576 }
577 }
578
579 loadedImageIsHDR = strcmp(profile_name, "ITUR_2100_PQ_FULL") == 0;
580 }
581 else if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains("test") && !m_batchMode) {
582 // Ask the user which color profile to use
583 KisConfig cfg(true);
584 quint32 behaviour = cfg.pasteBehaviour();
585 if (behaviour == KisClipboard::PASTE_ASK) {
586 KisDlgPngImport dlg(m_path, csName.first, csName.second);
588 Q_UNUSED(hijacker);
589 dlg.exec();
590 if (!dlg.profile().isEmpty()) {
592 }
593 }
594 }
595 else {
596 dbgFile << "no embedded profile, will use the default sRGB profile";
597 }
598
599 const QString colorSpaceId =
600 KoColorSpaceRegistry::instance()->colorSpaceId(csName.first, csName.second);
601
602 // Check that the profile is used by the color space
603 if (profile
604 && (!KoColorSpaceRegistry::instance()->profileIsCompatible(profile, colorSpaceId)
605 || !(profile->isSuitableForOutput() || profile->isSuitableForInput()))) {
606 warnFile << "The profile " << profile->name() << " is not compatible with the color space model " << csName.first << " " << csName.second;
607 profile = 0;
608 }
609
610 // Retrieve a pointer to the colorspace
611 KoColorConversionTransformation* transform = 0;
612 const KoColorSpace* cs = 0;
613
614 if (loadedImageIsHDR &&
615 csName.first == RGBAColorModelID.id() &&
616 csName.second == Integer16BitsColorDepthID.id()) {
617
618 const KoColorSpace *p2020PQCS =
623
624 cs = p2020PQCS;
625
626 } else if (profile && profile->isSuitableForWorkspace()) {
627 dbgFile << "image has embedded profile: " << profile->name() << "\n";
628 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile);
629 }
630 else {
631 // Loading a backup colorspace
632 cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, "");
633 }
634
635 // Create the cmsTransform if needed
636 if (profile && !profile->isSuitableForWorkspace() && profile->isSuitableForInput()) {
638 }
639
640 if (cs == 0) {
641 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
643 }
644
645 // Creating the KisImageSP
646 if (m_image == 0) {
648 m_image = new KisImage(store, width, height, cs, "built image");
649 }
650
651 // Read resolution
652 int unit_type;
653 png_uint_32 x_resolution = 0, y_resolution = 0;
654
655 png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type);
656 if (x_resolution > 0 && y_resolution > 0 && unit_type == PNG_RESOLUTION_METER) {
657 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
658 } else if (unit_type == PNG_RESOLUTION_UNKNOWN) {
659 m_image->setResolution(100.0, 100.0);
660 }
661
662 double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1);
663 KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX);
664
665 // Read comments/texts...
666 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
667 if (m_doc) {
669 dbgFile << "There are " << num_comments << " comments in the text";
670 for (int i = 0; i < num_comments; i++) {
671 QString key = QString(text_ptr[i].key).toLower();
672 dbgFile << "key: " << text_ptr[i].key
673 << ", containing: " << text_ptr[i].text
674 << ": " << (key == "raw profile type exif " ? "isExif" : "something else");
675 if (key == "title") {
676 info->setAboutInfo("title", text_ptr[i].text);
677 } else if (key == "description") {
678 info->setAboutInfo("comment", text_ptr[i].text);
679 } else if (key == "author") {
680 info->setAuthorInfo("creator", text_ptr[i].text);
681 } else if (key.contains("raw profile type exif")) {
682 decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6);
683 } else if (key.contains("raw profile type iptc")) {
684 decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14);
685 } else if (key.contains("raw profile type xmp")) {
686 decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0);
687 } else if (key == "version") {
688 m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text)));
689 } else if (key == "preset") {
690 m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text)));
691 }
692 }
693 }
694 // Read image data
695 QScopedPointer<KisPNGReaderAbstract> reader;
696 try {
697 if (interlace_type == PNG_INTERLACE_ADAM7) {
698 reader.reset(new KisPNGReaderFullImage(png_ptr, info_ptr, width, height));
699 } else {
700 reader.reset(new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height));
701 }
702 } catch (const std::bad_alloc& e) {
703 // new png_byte[] may raise such an exception if the image
704 // is invalid / to large.
705 dbgFile << "bad alloc: " << e.what();
706 // Free only the already allocated png_byte instances.
707 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
709 }
710
711 // Read the palette if the file is indexed
712 png_colorp palette ;
713 int num_palette;
714 if (color_type == PNG_COLOR_TYPE_PALETTE) {
715 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
716 }
717
718 // Read the transparency palette
719 quint8 palette_alpha[256];
720 memset(palette_alpha, 255, 256);
721 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
722 if (color_type == PNG_COLOR_TYPE_PALETTE) {
723 png_bytep alpha_ptr;
724 int num_alpha;
725 png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0);
726 for (int i = 0; i < num_alpha; ++i) {
727 palette_alpha[i] = alpha_ptr[i];
728 }
729 }
730 }
731
732 for (png_uint_32 y = 0; y < height; y++) {
733 KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width);
734
735 png_bytep row_pointer = reader->readLine();
736
737 switch (color_type) {
738 case PNG_COLOR_TYPE_GRAY:
739 case PNG_COLOR_TYPE_GRAY_ALPHA:
740 if (color_nb_bits == 16) {
741 quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
742 do {
743 quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
744 d[0] = *(src++);
745 if (hasalpha) {
746 d[1] = *(src++);
747 } else {
748 d[1] = quint16_MAX;
749 }
750 if (transform) transform->transformInPlace(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1);
751 } while (it->nextPixel());
752 } else {
753 KisPNGReadStream stream(row_pointer, color_nb_bits);
754 do {
755 quint8 *d = it->rawData();
756 d[0] = (quint8)(stream.nextValue() * coeff);
757 if (hasalpha) {
758 d[1] = (quint8)(stream.nextValue() * coeff);
759 } else {
760 d[1] = UCHAR_MAX;
761 }
762 if (transform) transform->transformInPlace(d, d, 1);
763 } while (it->nextPixel());
764 }
765 // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits"
766 break;
767 case PNG_COLOR_TYPE_RGB:
768 case PNG_COLOR_TYPE_RGB_ALPHA:
769 if (color_nb_bits == 16) {
770 quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
771 do {
772 quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
773 d[2] = *(src++);
774 d[1] = *(src++);
775 d[0] = *(src++);
776 if (hasalpha) d[3] = *(src++);
777 else d[3] = quint16_MAX;
778 if (transform) transform->transformInPlace(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1);
779 } while (it->nextPixel());
780 } else {
781 KisPNGReadStream stream(row_pointer, color_nb_bits);
782 do {
783 quint8 *d = it->rawData();
784 d[2] = (quint8)(stream.nextValue() * coeff);
785 d[1] = (quint8)(stream.nextValue() * coeff);
786 d[0] = (quint8)(stream.nextValue() * coeff);
787 if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff);
788 else d[3] = UCHAR_MAX;
789 if (transform) transform->transformInPlace(d, d, 1);
790 } while (it->nextPixel());
791 }
792 break;
793 case PNG_COLOR_TYPE_PALETTE: {
794 KisPNGReadStream stream(row_pointer, color_nb_bits);
795 do {
796 quint8 *d = it->rawData();
797 quint8 index = stream.nextValue();
798 quint8 alpha = palette_alpha[ index ];
799 if (alpha == 0) {
800 memset(d, 0, 4);
801 } else {
802 png_color c = palette[ index ];
803 d[2] = c.red;
804 d[1] = c.green;
805 d[0] = c.blue;
806 d[3] = alpha;
807 }
808 } while (it->nextPixel());
809 }
810 break;
811 default:
813 }
814 }
815 m_image->addNode(layer.data(), m_image->rootLayer().data());
816
817 png_read_end(png_ptr, end_info);
818 iod->close();
819
820 // Freeing memory
821 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
823
824}
825
827{
828 m_path = filename;
829
830 QFile fp(filename);
831 if (fp.exists()) {
832 if (!fp.open(QIODevice::ReadOnly)) {
833 dbgFile << "Failed to open PNG File";
835 }
836
837 return buildImage(&fp);
838 }
840
841}
842
843
848
849bool KisPNGConverter::saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData)
850{
851 if (store->open(filename)) {
852 KoStoreDevice io(store);
853 if (!io.open(QIODevice::WriteOnly)) {
854 dbgFile << "Could not open for writing:" << filename;
855 return false;
856 }
857 KisPNGConverter pngconv(0);
858 vKisAnnotationSP_it annotIt;
859 KisMetaData::Store* metaDataStore = 0;
860 if (metaData) {
861 metaDataStore = new KisMetaData::Store(*metaData);
862 }
863 KisPNGOptions options;
864 options.compression = 3;
865 options.interlace = false;
866 options.tryToSaveAsIndexed = false;
867 options.alpha = true;
868 options.saveSRGBProfile = false;
869 options.downsample = false;
870
871 if (dev->colorSpace()->id() != "RGBA") {
872 dev = new KisPaintDevice(*dev.data());
874 }
875
876 KisImportExportErrorCode success = pngconv.buildFile(&io, imageRect, xRes, yRes, dev, annotIt, annotIt, options, metaDataStore);
877 if (!success.isOk()) {
878 dbgFile << "Saving PNG failed:" << filename;
879 delete metaDataStore;
880 return false;
881 }
882 delete metaDataStore;
883 io.close();
884 if (!store->close()) {
885 return false;
886 }
887 } else {
888 dbgFile << "Opening of data file failed :" << filename;
889 return false;
890 }
891 return true;
892
893}
894
895
896KisImportExportErrorCode 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)
897{
898 dbgFile << "Start writing PNG File " << filename;
899 // Open a QIODevice for writing
900 QFile fp (filename);
901 if (!fp.open(QIODevice::WriteOnly)) {
902 dbgFile << "Failed to open PNG File for writing";
903 return (KisImportExportErrorCannotWrite(fp.error()));
904 }
905
906 KisImportExportErrorCode result = buildFile(&fp, imageRect, xRes, yRes, device, annotationsStart, annotationsEnd, options, metaData);
907
908 return result;
909}
910
911KisImportExportErrorCode 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)
912{
914
915 if (!options.alpha) {
917 KoColor c(options.transparencyFillColor, device->colorSpace());
918 tmp->fill(imageRect, c);
919 KisPainter gc(tmp);
920 gc.bitBlt(imageRect.topLeft(), device, imageRect);
921 gc.end();
922 device = tmp;
923 }
924
925 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) {
926 options.forceSRGB = false;
927 }
928
930 QString dstModel = device->colorSpace()->colorModelId().id();
931 QString dstDepth = device->colorSpace()->colorDepthId().id();
932 const KoColorProfile *dstProfile = device->colorSpace()->profile();
933 bool needColorTransform = false;
934
935 if (options.saveAsHDR || options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) {
936 dstModel = RGBAColorModelID.id();
938
939 needColorTransform = true;
940
941 if (options.saveAsHDR) {
943 }
944 }
945
946 if (options.downsample
950 dstDepth = Integer16BitsColorDepthID.id();
951
952 needColorTransform = true;
953
954 if (options.downsample) {
955 dstDepth = Integer8BitsColorDepthID.id();
956 }
957 }
958
959 if (needColorTransform) {
960 const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(dstModel, dstDepth, dstProfile);
961
962 if (!dstCs) {
964 }
965
966 device = new KisPaintDevice(*device);
967 device->convertTo(dstCs);
968 }
969
971 options.tryToSaveAsIndexed = false;
972 }
973
974 // Initialize structures
975 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
976 if (!png_ptr) {
978 }
979
980#ifdef PNG_SET_USER_LIMITS_SUPPORTED
981 /* Remove the user limits, if any */
982 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
983 png_set_chunk_cache_max(png_ptr, 0);
984 png_set_chunk_malloc_max(png_ptr, 0);
985#endif
986
987 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning);
988 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
989 png_set_benign_errors(png_ptr, 1);
990 #endif
991
992#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
993 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
994#endif
995
996
997#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
998 png_set_check_for_invalid_index(png_ptr, 0);
999#endif
1000
1001 png_infop info_ptr = png_create_info_struct(png_ptr);
1002 if (!info_ptr) {
1003 png_destroy_write_struct(&png_ptr, (png_infopp)0);
1005 }
1006
1007 // If an error occurs during writing, libpng will jump here
1008 if (setjmp(png_jmpbuf(png_ptr))) {
1009 png_destroy_write_struct(&png_ptr, &info_ptr);
1011 }
1012 // Initialize the writing
1013 // png_init_io(png_ptr, fp);
1014 // Setup the progress function
1015 // XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);"
1016 // setProgressTotalSteps(100/*height*/);
1017
1018 /* set the zlib compression level */
1019 png_set_compression_level(png_ptr, options.compression);
1020
1021 png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn);
1022
1023 /* set other zlib parameters */
1024 png_set_compression_mem_level(png_ptr, 8);
1025 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
1026 png_set_compression_window_bits(png_ptr, 15);
1027 png_set_compression_method(png_ptr, 8);
1028 png_set_compression_buffer_size(png_ptr, 8192);
1029
1030 int color_nb_bits = 8 * device->pixelSize() / device->channelCount();
1031 int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha);
1032
1033 Q_ASSERT(color_type > -1);
1034
1035 // Try to compute a table of color if the colorspace is RGB8f
1036 QScopedArrayPointer<png_color> palette;
1037 int num_palette = 0;
1038 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
1039 palette.reset(new png_color[255]);
1040
1041 KisSequentialIterator it(device, imageRect);
1042
1043 bool toomuchcolor = false;
1044 while (it.nextPixel()) {
1045 const quint8* c = it.oldRawData();
1046 bool findit = false;
1047 for (int i = 0; i < num_palette; i++) {
1048 if (palette[i].red == c[2] &&
1049 palette[i].green == c[1] &&
1050 palette[i].blue == c[0]) {
1051 findit = true;
1052 break;
1053 }
1054 }
1055 if (!findit) {
1056 if (num_palette == 255) {
1057 toomuchcolor = true;
1058 break;
1059 }
1060 palette[num_palette].red = c[2];
1061 palette[num_palette].green = c[1];
1062 palette[num_palette].blue = c[0];
1063 num_palette++;
1064 }
1065 }
1066
1067 if (!toomuchcolor) {
1068 dbgFile << "Found a palette of " << num_palette << " colors";
1069 color_type = PNG_COLOR_TYPE_PALETTE;
1070 if (num_palette <= 2) {
1071 color_nb_bits = 1;
1072 } else if (num_palette <= 4) {
1073 color_nb_bits = 2;
1074 } else if (num_palette <= 16) {
1075 color_nb_bits = 4;
1076 } else {
1077 color_nb_bits = 8;
1078 }
1079 } else {
1080 palette.reset();
1081 }
1082 }
1083
1084 int interlace_type = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
1085
1087
1088 png_set_IHDR(png_ptr, info_ptr,
1089 imageRect.width(),
1090 imageRect.height(),
1091 color_nb_bits,
1092 color_type, interlace_type,
1093 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1094
1095 // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present
1096
1097 const bool sRGB = *device->colorSpace()->profile() == *KoColorSpaceRegistry::instance()->p709SRGBProfile();
1098 /*
1099 * This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's
1100 * color management is bugged, so once you give it any incentive to start color managing an sRGB image it
1101 * will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now.
1102 */
1103 /*if (!options.saveSRGBProfile && sRGB) {
1104 png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
1105 }*/
1106
1107
1117#if 0
1118 if (options.saveAsHDR) {
1119 // https://www.w3.org/TR/PNG/#11gAMA
1120#if defined(PNG_GAMMA_SUPPORTED)
1121 // the values are set in accordance of HDR-PNG standard:
1122 // https://www.w3.org/TR/png-hdr-pq/
1123
1124 png_set_gAMA_fixed(png_ptr, info_ptr, 15000);
1125 dbgFile << "gAMA" << "(Rec 2100)";
1126#endif
1127
1128#if defined PNG_cHRM_SUPPORTED
1129 png_set_cHRM_fixed(png_ptr, info_ptr,
1130 31270, 32900, // white point
1131 70800, 29200, // red
1132 17000, 79700, // green
1133 13100, 4600 // blue
1134 );
1135 dbgFile << "cHRM" << "(Rec 2100)";
1136#endif
1137 }
1138#endif
1139
1140
1141 // we should ensure we don't access non-existing palette object
1142 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, ImportExportCodes::Failure);
1143
1144 // set the palette
1145 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1146 png_set_PLTE(png_ptr, info_ptr, palette.data(), num_palette);
1147 }
1148 // Save annotation
1149 vKisAnnotationSP_it it = annotationsStart;
1150 while (it != annotationsEnd) {
1151 if (!(*it) || (*it)->type().isEmpty()) {
1152 dbgFile << "Warning: empty annotation";
1153 it++;
1154 continue;
1155 }
1156
1157 dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size();
1158
1159 if ((*it) -> type().startsWith(QString("krita_attribute:"))) { //
1160 // Attribute
1161 // XXX: it should be possible to save krita_attributes in the \"CHUNKs\""
1162 dbgFile << "cannot save this annotation : " << (*it) -> type();
1163 } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) {
1164 dbgFile << "Saving preset information " << (*it)->description();
1165 png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text));
1166
1167 QByteArray keyData = (*it)->description().toLatin1();
1168 text[0].key = keyData.data();
1169 text[0].text = (char*)(*it)->annotation().data();
1170 text[0].text_length = (*it)->annotation().size();
1171 text[0].compression = -1;
1172
1173 png_set_text(png_ptr, info_ptr, text, 1);
1174 png_free(png_ptr, text);
1175 }
1176 it++;
1177 }
1178
1179 // Save the color profile
1180 const KoColorProfile* colorProfile = device->colorSpace()->profile();
1181 QByteArray colorProfileData = colorProfile->rawData();
1182 if (!sRGB || options.saveSRGBProfile) {
1183
1184#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
1185 const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL";
1186 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size());
1187#else
1188 // older version of libpng has a problem with constness on the parameters
1189 char typeStringICC[] = "icc";
1190 char typeStringHDR[] = "ITUR_2100_PQ_FULL";
1191 char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR;
1192 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size());
1193#endif
1194 }
1195
1196 // save comments from the document information
1197 // warning: according to the official png spec, the keys need to be capitalized!
1198 if (m_doc) {
1199 png_text texts[4];
1200 int nbtexts = 0;
1201 KoDocumentInfo * info = m_doc->documentInfo();
1202 QString title = info->aboutInfo("title");
1203 if (!title.isEmpty() && options.storeMetaData) {
1204 fillText(texts + nbtexts, "Title", title);
1205 nbtexts++;
1206 }
1207 QString abstract = info->aboutInfo("subject");
1208 if (abstract.isEmpty()) {
1209 abstract = info->aboutInfo("abstract");
1210 }
1211 if (!abstract.isEmpty() && options.storeMetaData) {
1212 QString keywords = info->aboutInfo("keyword");
1213 if (!keywords.isEmpty()) {
1214 abstract = abstract + " keywords: " + keywords;
1215 }
1216 fillText(texts + nbtexts, "Description", abstract);
1217 nbtexts++;
1218 }
1219
1220 QString license = info->aboutInfo("license");
1221 if (!license.isEmpty() && options.storeMetaData) {
1222 fillText(texts + nbtexts, "Copyright", license);
1223 nbtexts++;
1224 }
1225
1226 QString author = info->authorInfo("creator");
1227 if (!author.isEmpty() && options.storeAuthor) {
1228 if (!info->authorContactInfo().isEmpty()) {
1229 QString contact = info->authorContactInfo().at(0);
1230 if (!contact.isEmpty()) {
1231 author = author+"("+contact+")";
1232 }
1233 }
1234 fillText(texts + nbtexts, "Author", author);
1235 nbtexts++;
1236 }
1237
1238 png_set_text(png_ptr, info_ptr, texts, nbtexts);
1239 }
1240
1241 // Save metadata following imagemagick way
1242
1243 // Save exif
1244 if (metaData && !metaData->empty()) {
1245 if (options.exif) {
1246 dbgFile << "Trying to save exif information";
1247
1249 Q_ASSERT(exifIO);
1250
1251 QBuffer buffer;
1252 exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
1253 writeRawProfile(png_ptr, info_ptr, "exif", buffer.data());
1254 }
1255 // Save IPTC
1256 if (options.iptc) {
1257 dbgFile << "Trying to save iptc information";
1259 Q_ASSERT(iptcIO);
1260
1261 QBuffer buffer;
1262 iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
1263
1264 dbgFile << "IPTC information size is" << buffer.data().size();
1265 writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data());
1266 }
1267 // Save XMP
1268 if (options.xmp) {
1269 dbgFile << "Trying to save XMP information";
1271 Q_ASSERT(xmpIO);
1272
1273 QBuffer buffer;
1274 xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader);
1275
1276 dbgFile << "XMP information size is" << buffer.data().size();
1277 writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data());
1278 }
1279 }
1280#if 0 // Unimplemented?
1281 // Save resolution
1282 int unit_type;
1283 png_uint_32 x_resolution, y_resolution;
1284#endif
1285 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
1286
1287 // Save the information to the file
1288 png_write_info(png_ptr, info_ptr);
1289 png_write_flush(png_ptr);
1290
1291 // swap byteorder on little endian machines.
1292#ifndef WORDS_BIGENDIAN
1293 if (color_nb_bits > 8)
1294 png_set_swap(png_ptr);
1295#endif
1296
1297 // Write the PNG
1298 // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
1299
1300 struct RowPointersStruct {
1301 RowPointersStruct(const QSize &size, int pixelSize)
1302 : numRows(size.height())
1303 {
1304 rows = new png_byte*[numRows];
1305
1306 for (int i = 0; i < numRows; i++) {
1307 rows[i] = new png_byte[size.width() * pixelSize];
1308 }
1309 }
1310
1311 ~RowPointersStruct() {
1312 for (int i = 0; i < numRows; i++) {
1313 delete[] rows[i];
1314 }
1315 delete[] rows;
1316 }
1317
1318 const int numRows = 0;
1319 png_byte** rows = 0;
1320 };
1321
1322
1323 // Fill the data structure
1324 RowPointersStruct rowPointers(imageRect.size(), device->pixelSize());
1325
1326 int row = 0;
1327 for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
1328 KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width());
1329
1330 switch (color_type) {
1331 case PNG_COLOR_TYPE_GRAY:
1332 case PNG_COLOR_TYPE_GRAY_ALPHA:
1333 if (color_nb_bits == 16) {
1334 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]);
1335 do {
1336 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
1337 *(dst++) = d[0];
1338 if (options.alpha) *(dst++) = d[1];
1339 } while (it->nextPixel());
1340 } else {
1341 quint8 *dst = rowPointers.rows[row];
1342 do {
1343 const quint8 *d = it->oldRawData();
1344 *(dst++) = d[0];
1345 if (options.alpha) *(dst++) = d[1];
1346 } while (it->nextPixel());
1347 }
1348 break;
1349 case PNG_COLOR_TYPE_RGB:
1350 case PNG_COLOR_TYPE_RGB_ALPHA:
1351 if (color_nb_bits == 16) {
1352 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]);
1353 do {
1354 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
1355 *(dst++) = d[2];
1356 *(dst++) = d[1];
1357 *(dst++) = d[0];
1358 if (options.alpha) *(dst++) = d[3];
1359 } while (it->nextPixel());
1360 } else {
1361 quint8 *dst = rowPointers.rows[row];
1362 do {
1363 const quint8 *d = it->oldRawData();
1364 *(dst++) = d[2];
1365 *(dst++) = d[1];
1366 *(dst++) = d[0];
1367 if (options.alpha) *(dst++) = d[3];
1368 } while (it->nextPixel());
1369 }
1370 break;
1371 case PNG_COLOR_TYPE_PALETTE: {
1372 quint8 *dst = rowPointers.rows[row];
1373 KisPNGWriteStream writestream(dst, color_nb_bits);
1374 do {
1375 const quint8 *d = it->oldRawData();
1376 int i;
1377 for (i = 0; i < num_palette; i++) {
1378 if (palette[i].red == d[2] &&
1379 palette[i].green == d[1] &&
1380 palette[i].blue == d[0]) {
1381 break;
1382 }
1383 }
1384 writestream.setNextValue(i);
1385 } while (it->nextPixel());
1386 }
1387 break;
1388 default:
1390 }
1391 }
1392
1393 png_write_image(png_ptr, rowPointers.rows);
1394
1395 // Writing is over
1396 png_write_end(png_ptr, info_ptr);
1397
1398 // Free memory
1399 png_destroy_write_struct(&png_ptr, &info_ptr);
1400 return ImportExportCodes::OK;
1401}
1402
1403
1405{
1406 m_stop = true;
1407}
1408
1409void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass)
1410{
1411 if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return;
1412 // setProgress(row_number);
1413}
1414
1416{
1417 return colorSpaceIdSupported(cs->id());
1418}
1419
1420
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
qreal v
QList< QString > QStringList
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID Float64BitsColorDepthID("F64", ki18n("64-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
unsigned int uint
constexpr qreal POINT_TO_CM(qreal px)
Definition KoUnit.h:33
constexpr qreal CM_TO_POINT(qreal cm)
Definition KoUnit.h:34
A data extension mechanism for Krita.
virtual const quint8 * oldRawData() const =0
virtual bool nextPixel()=0
qint32 pasteBehaviour(bool defaultValue=false) const
The KisCursorOverrideHijacker class stores all override cursors in a stack, and resets them back afte...
QString profile() const
KisUndoStore * createUndoStore()
KoDocumentInfo * documentInfo() const
void addAnnotation(KisAnnotationSP annotation)
KisGroupLayerSP rootLayer() const
void setResolution(double xres, double yres)
@ JpegHeader
Append Jpeg-style header.
@ NoHeader
Don't append any header.
virtual bool loadFrom(Store *store, QIODevice *ioDevice) const =0
virtual bool saveTo(const Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const =0
static KisMetadataBackendRegistry * instance()
void progress(png_structp png_ptr, png_uint_32 row_number, int pass)
static bool isColorSpaceSupported(const KoColorSpace *cs)
static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store *metaData=0)
saveDeviceToStore saves the given paint device to the KoStore. If the device is not 8 bits sRGB,...
KisPNGConverter(KisDocument *doc, bool batchMode=false)
KisImportExportErrorCode buildImage(const QString &filename)
KisDocument * m_doc
KisImportExportErrorCode 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)
KisPNGReadStream(quint8 *buf, quint32 depth)
KisPNGReaderAbstract(png_structp _png_ptr, int _width, int _height)
virtual png_bytep readLine()=0
png_bytep readLine() override
KisPNGReaderFullImage(png_structp _png_ptr, png_infop info_ptr, int _width, int _height)
KisPNGReaderLineByLine(png_structp _png_ptr, png_infop info_ptr, int _width, int _height)
png_bytep readLine() override
KisPNGWriteStream(quint8 *buf, quint32 depth)
quint32 pixelSize() const
quint32 channelCount() const
const KoColorSpace * colorSpace() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
ALWAYS_INLINE const quint8 * oldRawData() const
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
virtual KoColorConversionTransformation * createColorConverter(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
The class containing all meta information about a document.
void setAboutInfo(const QString &info, const QString &data)
QStringList authorContactInfo() const
authorContactInfo
QString authorInfo(const QString &info) const
void setAuthorInfo(const QString &info, const QString &data)
QString aboutInfo(const QString &info) const
const T value(const QString &id) const
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
void close() override
bool open(OpenMode m) override
bool close()
Definition KoStore.cpp:156
bool open(const QString &name)
Definition KoStore.cpp:109
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define warnFile
Definition kis_debug.h:95
#define dbgFile
Definition kis_debug.h:53
const quint16 quint16_MAX
Definition kis_global.h:25
const quint8 quint8_MAX
Definition kis_global.h:24
static void _flush_fn(png_structp png_ptr)
static void _read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
static void kis_png_warning(png_structp, png_const_charp message)
static void _write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
#define PNG_MAX_UINT
QString getColorSpaceForColorType(uint16_t sampletype, uint16_t color_type, uint16_t color_nb_bits, TIFF *image, uint16_t &nbchannels, uint16_t &extrasamplescount, uint8_t &destDepth)
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
rgba palette[MAX_PALETTE]
Definition palette.c:35
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
QColor transparencyFillColor
void transformInPlace(const quint8 *src, quint8 *dst, qint32 nPixels) const
virtual bool isSuitableForOutput() const =0
virtual bool isSuitableForWorkspace() const =0
virtual QByteArray rawData() const
virtual bool isSuitableForInput() const =0
const KoColorProfile * profileByName(const QString &name) const
QString colorSpaceId(const QString &colorModelId, const QString &colorDepthId) const
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorProfile * p709SRGBProfile() const
const KoColorProfile * p2020PQProfile() const
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)