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;
568 const KoColorProfile* profile = 0;
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 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, y_resolution;
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 }
659
660 double coeff = quint8_MAX / (double)(pow((double)2, color_nb_bits) - 1);
661 KisPaintLayerSP layer = new KisPaintLayer(m_image.data(), m_image -> nextLayerName(), UCHAR_MAX);
662
663 // Read comments/texts...
664 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
665 if (m_doc) {
667 dbgFile << "There are " << num_comments << " comments in the text";
668 for (int i = 0; i < num_comments; i++) {
669 QString key = QString(text_ptr[i].key).toLower();
670 dbgFile << "key: " << text_ptr[i].key
671 << ", containing: " << text_ptr[i].text
672 << ": " << (key == "raw profile type exif " ? "isExif" : "something else");
673 if (key == "title") {
674 info->setAboutInfo("title", text_ptr[i].text);
675 } else if (key == "description") {
676 info->setAboutInfo("comment", text_ptr[i].text);
677 } else if (key == "author") {
678 info->setAuthorInfo("creator", text_ptr[i].text);
679 } else if (key.contains("raw profile type exif")) {
680 decode_meta_data(text_ptr + i, layer->metaData(), "exif", 6);
681 } else if (key.contains("raw profile type iptc")) {
682 decode_meta_data(text_ptr + i, layer->metaData(), "iptc", 14);
683 } else if (key.contains("raw profile type xmp")) {
684 decode_meta_data(text_ptr + i, layer->metaData(), "xmp", 0);
685 } else if (key == "version") {
686 m_image->addAnnotation(new KisAnnotation("kpp_version", "version", QByteArray(text_ptr[i].text)));
687 } else if (key == "preset") {
688 m_image->addAnnotation(new KisAnnotation("kpp_preset", "preset", QByteArray(text_ptr[i].text)));
689 }
690 }
691 }
692 // Read image data
693 QScopedPointer<KisPNGReaderAbstract> reader;
694 try {
695 if (interlace_type == PNG_INTERLACE_ADAM7) {
696 reader.reset(new KisPNGReaderFullImage(png_ptr, info_ptr, width, height));
697 } else {
698 reader.reset(new KisPNGReaderLineByLine(png_ptr, info_ptr, width, height));
699 }
700 } catch (const std::bad_alloc& e) {
701 // new png_byte[] may raise such an exception if the image
702 // is invalid / to large.
703 dbgFile << "bad alloc: " << e.what();
704 // Free only the already allocated png_byte instances.
705 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
707 }
708
709 // Read the palette if the file is indexed
710 png_colorp palette ;
711 int num_palette;
712 if (color_type == PNG_COLOR_TYPE_PALETTE) {
713 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
714 }
715
716 // Read the transparency palette
717 quint8 palette_alpha[256];
718 memset(palette_alpha, 255, 256);
719 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
720 if (color_type == PNG_COLOR_TYPE_PALETTE) {
721 png_bytep alpha_ptr;
722 int num_alpha;
723 png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0);
724 for (int i = 0; i < num_alpha; ++i) {
725 palette_alpha[i] = alpha_ptr[i];
726 }
727 }
728 }
729
730 for (png_uint_32 y = 0; y < height; y++) {
731 KisHLineIteratorSP it = layer -> paintDevice() -> createHLineIteratorNG(0, y, width);
732
733 png_bytep row_pointer = reader->readLine();
734
735 switch (color_type) {
736 case PNG_COLOR_TYPE_GRAY:
737 case PNG_COLOR_TYPE_GRAY_ALPHA:
738 if (color_nb_bits == 16) {
739 quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
740 do {
741 quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
742 d[0] = *(src++);
743 if (hasalpha) {
744 d[1] = *(src++);
745 } else {
746 d[1] = quint16_MAX;
747 }
748 if (transform) transform->transformInPlace(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1);
749 } while (it->nextPixel());
750 } else {
751 KisPNGReadStream stream(row_pointer, color_nb_bits);
752 do {
753 quint8 *d = it->rawData();
754 d[0] = (quint8)(stream.nextValue() * coeff);
755 if (hasalpha) {
756 d[1] = (quint8)(stream.nextValue() * coeff);
757 } else {
758 d[1] = UCHAR_MAX;
759 }
760 if (transform) transform->transformInPlace(d, d, 1);
761 } while (it->nextPixel());
762 }
763 // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits"
764 break;
765 case PNG_COLOR_TYPE_RGB:
766 case PNG_COLOR_TYPE_RGB_ALPHA:
767 if (color_nb_bits == 16) {
768 quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
769 do {
770 quint16 *d = reinterpret_cast<quint16 *>(it->rawData());
771 d[2] = *(src++);
772 d[1] = *(src++);
773 d[0] = *(src++);
774 if (hasalpha) d[3] = *(src++);
775 else d[3] = quint16_MAX;
776 if (transform) transform->transformInPlace(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1);
777 } while (it->nextPixel());
778 } else {
779 KisPNGReadStream stream(row_pointer, color_nb_bits);
780 do {
781 quint8 *d = it->rawData();
782 d[2] = (quint8)(stream.nextValue() * coeff);
783 d[1] = (quint8)(stream.nextValue() * coeff);
784 d[0] = (quint8)(stream.nextValue() * coeff);
785 if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff);
786 else d[3] = UCHAR_MAX;
787 if (transform) transform->transformInPlace(d, d, 1);
788 } while (it->nextPixel());
789 }
790 break;
791 case PNG_COLOR_TYPE_PALETTE: {
792 KisPNGReadStream stream(row_pointer, color_nb_bits);
793 do {
794 quint8 *d = it->rawData();
795 quint8 index = stream.nextValue();
796 quint8 alpha = palette_alpha[ index ];
797 if (alpha == 0) {
798 memset(d, 0, 4);
799 } else {
800 png_color c = palette[ index ];
801 d[2] = c.red;
802 d[1] = c.green;
803 d[0] = c.blue;
804 d[3] = alpha;
805 }
806 } while (it->nextPixel());
807 }
808 break;
809 default:
811 }
812 }
813 m_image->addNode(layer.data(), m_image->rootLayer().data());
814
815 png_read_end(png_ptr, end_info);
816 iod->close();
817
818 // Freeing memory
819 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
821
822}
823
825{
826 m_path = filename;
827
828 QFile fp(filename);
829 if (fp.exists()) {
830 if (!fp.open(QIODevice::ReadOnly)) {
831 dbgFile << "Failed to open PNG File";
833 }
834
835 return buildImage(&fp);
836 }
838
839}
840
841
846
847bool KisPNGConverter::saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData)
848{
849 if (store->open(filename)) {
850 KoStoreDevice io(store);
851 if (!io.open(QIODevice::WriteOnly)) {
852 dbgFile << "Could not open for writing:" << filename;
853 return false;
854 }
855 KisPNGConverter pngconv(0);
856 vKisAnnotationSP_it annotIt;
857 KisMetaData::Store* metaDataStore = 0;
858 if (metaData) {
859 metaDataStore = new KisMetaData::Store(*metaData);
860 }
861 KisPNGOptions options;
862 options.compression = 3;
863 options.interlace = false;
864 options.tryToSaveAsIndexed = false;
865 options.alpha = true;
866 options.saveSRGBProfile = false;
867 options.downsample = false;
868
869 if (dev->colorSpace()->id() != "RGBA") {
870 dev = new KisPaintDevice(*dev.data());
872 }
873
874 KisImportExportErrorCode success = pngconv.buildFile(&io, imageRect, xRes, yRes, dev, annotIt, annotIt, options, metaDataStore);
875 if (!success.isOk()) {
876 dbgFile << "Saving PNG failed:" << filename;
877 delete metaDataStore;
878 return false;
879 }
880 delete metaDataStore;
881 io.close();
882 if (!store->close()) {
883 return false;
884 }
885 } else {
886 dbgFile << "Opening of data file failed :" << filename;
887 return false;
888 }
889 return true;
890
891}
892
893
894KisImportExportErrorCode 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)
895{
896 dbgFile << "Start writing PNG File " << filename;
897 // Open a QIODevice for writing
898 QFile fp (filename);
899 if (!fp.open(QIODevice::WriteOnly)) {
900 dbgFile << "Failed to open PNG File for writing";
901 return (KisImportExportErrorCannotWrite(fp.error()));
902 }
903
904 KisImportExportErrorCode result = buildFile(&fp, imageRect, xRes, yRes, device, annotationsStart, annotationsEnd, options, metaData);
905
906 return result;
907}
908
909KisImportExportErrorCode 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)
910{
912
913 if (!options.alpha) {
915 KoColor c(options.transparencyFillColor, device->colorSpace());
916 tmp->fill(imageRect, c);
917 KisPainter gc(tmp);
918 gc.bitBlt(imageRect.topLeft(), device, imageRect);
919 gc.end();
920 device = tmp;
921 }
922
923 KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) {
924 options.forceSRGB = false;
925 }
926
928 QString dstModel = device->colorSpace()->colorModelId().id();
929 QString dstDepth = device->colorSpace()->colorDepthId().id();
930 const KoColorProfile *dstProfile = device->colorSpace()->profile();
931 bool needColorTransform = false;
932
933 if (options.saveAsHDR || options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) {
934 dstModel = RGBAColorModelID.id();
936
937 needColorTransform = true;
938
939 if (options.saveAsHDR) {
941 }
942 }
943
944 if (options.downsample
948 dstDepth = Integer16BitsColorDepthID.id();
949
950 needColorTransform = true;
951
952 if (options.downsample) {
953 dstDepth = Integer8BitsColorDepthID.id();
954 }
955 }
956
957 if (needColorTransform) {
958 const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(dstModel, dstDepth, dstProfile);
959
960 if (!dstCs) {
962 }
963
964 device = new KisPaintDevice(*device);
965 device->convertTo(dstCs);
966 }
967
969 options.tryToSaveAsIndexed = false;
970 }
971
972 // Initialize structures
973 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
974 if (!png_ptr) {
976 }
977
978#ifdef PNG_SET_USER_LIMITS_SUPPORTED
979 /* Remove the user limits, if any */
980 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
981 png_set_chunk_cache_max(png_ptr, 0);
982 png_set_chunk_malloc_max(png_ptr, 0);
983#endif
984
985 png_set_error_fn(png_ptr, nullptr, nullptr, kis_png_warning);
986 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
987 png_set_benign_errors(png_ptr, 1);
988 #endif
989
990#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
991 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
992#endif
993
994
995#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
996 png_set_check_for_invalid_index(png_ptr, 0);
997#endif
998
999 png_infop info_ptr = png_create_info_struct(png_ptr);
1000 if (!info_ptr) {
1001 png_destroy_write_struct(&png_ptr, (png_infopp)0);
1003 }
1004
1005 // If an error occurs during writing, libpng will jump here
1006 if (setjmp(png_jmpbuf(png_ptr))) {
1007 png_destroy_write_struct(&png_ptr, &info_ptr);
1009 }
1010 // Initialize the writing
1011 // png_init_io(png_ptr, fp);
1012 // Setup the progress function
1013 // XXX: Implement progress updating -- png_set_write_status_fn(png_ptr, progress);"
1014 // setProgressTotalSteps(100/*height*/);
1015
1016 /* set the zlib compression level */
1017 png_set_compression_level(png_ptr, options.compression);
1018
1019 png_set_write_fn(png_ptr, (void*)iodevice, _write_fn, _flush_fn);
1020
1021 /* set other zlib parameters */
1022 png_set_compression_mem_level(png_ptr, 8);
1023 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
1024 png_set_compression_window_bits(png_ptr, 15);
1025 png_set_compression_method(png_ptr, 8);
1026 png_set_compression_buffer_size(png_ptr, 8192);
1027
1028 int color_nb_bits = 8 * device->pixelSize() / device->channelCount();
1029 int color_type = getColorTypeforColorSpace(device->colorSpace(), options.alpha);
1030
1031 Q_ASSERT(color_type > -1);
1032
1033 // Try to compute a table of color if the colorspace is RGB8f
1034 QScopedArrayPointer<png_color> palette;
1035 int num_palette = 0;
1036 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
1037 palette.reset(new png_color[255]);
1038
1039 KisSequentialIterator it(device, imageRect);
1040
1041 bool toomuchcolor = false;
1042 while (it.nextPixel()) {
1043 const quint8* c = it.oldRawData();
1044 bool findit = false;
1045 for (int i = 0; i < num_palette; i++) {
1046 if (palette[i].red == c[2] &&
1047 palette[i].green == c[1] &&
1048 palette[i].blue == c[0]) {
1049 findit = true;
1050 break;
1051 }
1052 }
1053 if (!findit) {
1054 if (num_palette == 255) {
1055 toomuchcolor = true;
1056 break;
1057 }
1058 palette[num_palette].red = c[2];
1059 palette[num_palette].green = c[1];
1060 palette[num_palette].blue = c[0];
1061 num_palette++;
1062 }
1063 }
1064
1065 if (!toomuchcolor) {
1066 dbgFile << "Found a palette of " << num_palette << " colors";
1067 color_type = PNG_COLOR_TYPE_PALETTE;
1068 if (num_palette <= 2) {
1069 color_nb_bits = 1;
1070 } else if (num_palette <= 4) {
1071 color_nb_bits = 2;
1072 } else if (num_palette <= 16) {
1073 color_nb_bits = 4;
1074 } else {
1075 color_nb_bits = 8;
1076 }
1077 } else {
1078 palette.reset();
1079 }
1080 }
1081
1082 int interlace_type = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
1083
1085
1086 png_set_IHDR(png_ptr, info_ptr,
1087 imageRect.width(),
1088 imageRect.height(),
1089 color_nb_bits,
1090 color_type, interlace_type,
1091 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1092
1093 // set sRGB only if the profile is sRGB -- http://www.w3.org/TR/PNG/#11sRGB says sRGB and iCCP should not both be present
1094
1095 const bool sRGB = *device->colorSpace()->profile() == *KoColorSpaceRegistry::instance()->p709SRGBProfile();
1096 /*
1097 * This automatically writes the correct gamma and chroma chunks along with the sRGB chunk, but firefox's
1098 * color management is bugged, so once you give it any incentive to start color managing an sRGB image it
1099 * will turn, for example, a nice desaturated rusty red into bright poppy red. So this is disabled for now.
1100 */
1101 /*if (!options.saveSRGBProfile && sRGB) {
1102 png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
1103 }*/
1104
1105
1115#if 0
1116 if (options.saveAsHDR) {
1117 // https://www.w3.org/TR/PNG/#11gAMA
1118#if defined(PNG_GAMMA_SUPPORTED)
1119 // the values are set in accordance of HDR-PNG standard:
1120 // https://www.w3.org/TR/png-hdr-pq/
1121
1122 png_set_gAMA_fixed(png_ptr, info_ptr, 15000);
1123 dbgFile << "gAMA" << "(Rec 2100)";
1124#endif
1125
1126#if defined PNG_cHRM_SUPPORTED
1127 png_set_cHRM_fixed(png_ptr, info_ptr,
1128 31270, 32900, // white point
1129 70800, 29200, // red
1130 17000, 79700, // green
1131 13100, 4600 // blue
1132 );
1133 dbgFile << "cHRM" << "(Rec 2100)";
1134#endif
1135 }
1136#endif
1137
1138
1139 // we should ensure we don't access non-existing palette object
1140 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, ImportExportCodes::Failure);
1141
1142 // set the palette
1143 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1144 png_set_PLTE(png_ptr, info_ptr, palette.data(), num_palette);
1145 }
1146 // Save annotation
1147 vKisAnnotationSP_it it = annotationsStart;
1148 while (it != annotationsEnd) {
1149 if (!(*it) || (*it)->type().isEmpty()) {
1150 dbgFile << "Warning: empty annotation";
1151 it++;
1152 continue;
1153 }
1154
1155 dbgFile << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size();
1156
1157 if ((*it) -> type().startsWith(QString("krita_attribute:"))) { //
1158 // Attribute
1159 // XXX: it should be possible to save krita_attributes in the \"CHUNKs\""
1160 dbgFile << "cannot save this annotation : " << (*it) -> type();
1161 } else if ((*it)->type() == "kpp_version" || (*it)->type() == "kpp_preset" ) {
1162 dbgFile << "Saving preset information " << (*it)->description();
1163 png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32) sizeof(png_text));
1164
1165 QByteArray keyData = (*it)->description().toLatin1();
1166 text[0].key = keyData.data();
1167 text[0].text = (char*)(*it)->annotation().data();
1168 text[0].text_length = (*it)->annotation().size();
1169 text[0].compression = -1;
1170
1171 png_set_text(png_ptr, info_ptr, text, 1);
1172 png_free(png_ptr, text);
1173 }
1174 it++;
1175 }
1176
1177 // Save the color profile
1178 const KoColorProfile* colorProfile = device->colorSpace()->profile();
1179 QByteArray colorProfileData = colorProfile->rawData();
1180 if (!sRGB || options.saveSRGBProfile) {
1181
1182#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
1183 const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL";
1184 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size());
1185#else
1186 // older version of libpng has a problem with constness on the parameters
1187 char typeStringICC[] = "icc";
1188 char typeStringHDR[] = "ITUR_2100_PQ_FULL";
1189 char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR;
1190 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size());
1191#endif
1192 }
1193
1194 // save comments from the document information
1195 // warning: according to the official png spec, the keys need to be capitalized!
1196 if (m_doc) {
1197 png_text texts[4];
1198 int nbtexts = 0;
1199 KoDocumentInfo * info = m_doc->documentInfo();
1200 QString title = info->aboutInfo("title");
1201 if (!title.isEmpty() && options.storeMetaData) {
1202 fillText(texts + nbtexts, "Title", title);
1203 nbtexts++;
1204 }
1205 QString abstract = info->aboutInfo("subject");
1206 if (abstract.isEmpty()) {
1207 abstract = info->aboutInfo("abstract");
1208 }
1209 if (!abstract.isEmpty() && options.storeMetaData) {
1210 QString keywords = info->aboutInfo("keyword");
1211 if (!keywords.isEmpty()) {
1212 abstract = abstract + " keywords: " + keywords;
1213 }
1214 fillText(texts + nbtexts, "Description", abstract);
1215 nbtexts++;
1216 }
1217
1218 QString license = info->aboutInfo("license");
1219 if (!license.isEmpty() && options.storeMetaData) {
1220 fillText(texts + nbtexts, "Copyright", license);
1221 nbtexts++;
1222 }
1223
1224 QString author = info->authorInfo("creator");
1225 if (!author.isEmpty() && options.storeAuthor) {
1226 if (!info->authorContactInfo().isEmpty()) {
1227 QString contact = info->authorContactInfo().at(0);
1228 if (!contact.isEmpty()) {
1229 author = author+"("+contact+")";
1230 }
1231 }
1232 fillText(texts + nbtexts, "Author", author);
1233 nbtexts++;
1234 }
1235
1236 png_set_text(png_ptr, info_ptr, texts, nbtexts);
1237 }
1238
1239 // Save metadata following imagemagick way
1240
1241 // Save exif
1242 if (metaData && !metaData->empty()) {
1243 if (options.exif) {
1244 dbgFile << "Trying to save exif information";
1245
1247 Q_ASSERT(exifIO);
1248
1249 QBuffer buffer;
1250 exifIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
1251 writeRawProfile(png_ptr, info_ptr, "exif", buffer.data());
1252 }
1253 // Save IPTC
1254 if (options.iptc) {
1255 dbgFile << "Trying to save iptc information";
1257 Q_ASSERT(iptcIO);
1258
1259 QBuffer buffer;
1260 iptcIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::JpegHeader);
1261
1262 dbgFile << "IPTC information size is" << buffer.data().size();
1263 writeRawProfile(png_ptr, info_ptr, "iptc", buffer.data());
1264 }
1265 // Save XMP
1266 if (options.xmp) {
1267 dbgFile << "Trying to save XMP information";
1269 Q_ASSERT(xmpIO);
1270
1271 QBuffer buffer;
1272 xmpIO->saveTo(metaData, &buffer, KisMetaData::IOBackend::NoHeader);
1273
1274 dbgFile << "XMP information size is" << buffer.data().size();
1275 writeRawProfile(png_ptr, info_ptr, "xmp", buffer.data());
1276 }
1277 }
1278#if 0 // Unimplemented?
1279 // Save resolution
1280 int unit_type;
1281 png_uint_32 x_resolution, y_resolution;
1282#endif
1283 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
1284
1285 // Save the information to the file
1286 png_write_info(png_ptr, info_ptr);
1287 png_write_flush(png_ptr);
1288
1289 // swap byteorder on little endian machines.
1290#ifndef WORDS_BIGENDIAN
1291 if (color_nb_bits > 8)
1292 png_set_swap(png_ptr);
1293#endif
1294
1295 // Write the PNG
1296 // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
1297
1298 struct RowPointersStruct {
1299 RowPointersStruct(const QSize &size, int pixelSize)
1300 : numRows(size.height())
1301 {
1302 rows = new png_byte*[numRows];
1303
1304 for (int i = 0; i < numRows; i++) {
1305 rows[i] = new png_byte[size.width() * pixelSize];
1306 }
1307 }
1308
1309 ~RowPointersStruct() {
1310 for (int i = 0; i < numRows; i++) {
1311 delete[] rows[i];
1312 }
1313 delete[] rows;
1314 }
1315
1316 const int numRows = 0;
1317 png_byte** rows = 0;
1318 };
1319
1320
1321 // Fill the data structure
1322 RowPointersStruct rowPointers(imageRect.size(), device->pixelSize());
1323
1324 int row = 0;
1325 for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
1326 KisHLineConstIteratorSP it = device->createHLineConstIteratorNG(imageRect.x(), y, imageRect.width());
1327
1328 switch (color_type) {
1329 case PNG_COLOR_TYPE_GRAY:
1330 case PNG_COLOR_TYPE_GRAY_ALPHA:
1331 if (color_nb_bits == 16) {
1332 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]);
1333 do {
1334 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
1335 *(dst++) = d[0];
1336 if (options.alpha) *(dst++) = d[1];
1337 } while (it->nextPixel());
1338 } else {
1339 quint8 *dst = rowPointers.rows[row];
1340 do {
1341 const quint8 *d = it->oldRawData();
1342 *(dst++) = d[0];
1343 if (options.alpha) *(dst++) = d[1];
1344 } while (it->nextPixel());
1345 }
1346 break;
1347 case PNG_COLOR_TYPE_RGB:
1348 case PNG_COLOR_TYPE_RGB_ALPHA:
1349 if (color_nb_bits == 16) {
1350 quint16 *dst = reinterpret_cast<quint16 *>(rowPointers.rows[row]);
1351 do {
1352 const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
1353 *(dst++) = d[2];
1354 *(dst++) = d[1];
1355 *(dst++) = d[0];
1356 if (options.alpha) *(dst++) = d[3];
1357 } while (it->nextPixel());
1358 } else {
1359 quint8 *dst = rowPointers.rows[row];
1360 do {
1361 const quint8 *d = it->oldRawData();
1362 *(dst++) = d[2];
1363 *(dst++) = d[1];
1364 *(dst++) = d[0];
1365 if (options.alpha) *(dst++) = d[3];
1366 } while (it->nextPixel());
1367 }
1368 break;
1369 case PNG_COLOR_TYPE_PALETTE: {
1370 quint8 *dst = rowPointers.rows[row];
1371 KisPNGWriteStream writestream(dst, color_nb_bits);
1372 do {
1373 const quint8 *d = it->oldRawData();
1374 int i;
1375 for (i = 0; i < num_palette; i++) {
1376 if (palette[i].red == d[2] &&
1377 palette[i].green == d[1] &&
1378 palette[i].blue == d[0]) {
1379 break;
1380 }
1381 }
1382 writestream.setNextValue(i);
1383 } while (it->nextPixel());
1384 }
1385 break;
1386 default:
1388 }
1389 }
1390
1391 png_write_image(png_ptr, rowPointers.rows);
1392
1393 // Writing is over
1394 png_write_end(png_ptr, info_ptr);
1395
1396 // Free memory
1397 png_destroy_write_struct(&png_ptr, &info_ptr);
1398 return ImportExportCodes::OK;
1399}
1400
1401
1403{
1404 m_stop = true;
1405}
1406
1407void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass)
1408{
1409 if (png_ptr == 0 || row_number > PNG_MAX_UINT || pass > 7) return;
1410 // setProgress(row_number);
1411}
1412
1414{
1415 return colorSpaceIdSupported(cs->id());
1416}
1417
1418
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)