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