14#define PNG_MAX_UINT PNG_UINT_31_MAX
27#include <QApplication>
29#include <klocalizedstring.h>
63int getColorTypeforColorSpace(
const KoColorSpace * cs ,
bool alpha)
66 QString
id = cs->
id();
68 if (
id ==
"GRAYA" ||
id ==
"GRAYAU16" ||
id ==
"GRAYA16") {
69 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
71 if (
id ==
"RGBA" ||
id ==
"RGBA16") {
72 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
79bool colorSpaceIdSupported(
const QString &
id)
81 return id ==
"RGBA" ||
id ==
"RGBA16" ||
82 id ==
"GRAYA" ||
id ==
"GRAYAU16" ||
id ==
"GRAYA16";
87 QPair<QString, QString> r;
89 if (color_type == PNG_COLOR_TYPE_PALETTE) {
93 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
95 }
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
98 if (color_nb_bits == 16) {
100 }
else if (color_nb_bits <= 8) {
108void fillText(png_text* p_text,
const char* key, QString& text)
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;
118long formatStringList(
char *
string,
const size_t length,
const char *format, va_list operands)
120 int n = vsnprintf(
string,
length, format, operands);
128long formatString(
char *
string,
const size_t length,
const char *format, ...)
134 va_start(operands, format);
135 n = (long) formatStringList(
string,
length, format, operands);
140void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data)
145 png_uint_32 allocated_length, description_length;
147 const uchar hex[16] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f'};
149 dbgFile <<
"Writing Raw profile: type=" << profile_type <<
", length=" << profile_data.length() << Qt::endl;
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);
155 text[0].text = (png_charp) png_malloc(ping, allocated_length);
156 memset(text[0].text, 0, allocated_length);
158 QString key = QLatin1String(
"Raw profile type ") + profile_type.toLatin1();
159 QByteArray keyData = key.toLatin1();
160 text[0].key = keyData.data();
162 uchar* sp = (uchar*)profile_data.data();
163 png_charp dp = text[0].text;
166 memcpy(dp, profile_type.toLatin1().constData(), profile_type.length());
168 dp += description_length;
171 formatString(dp, allocated_length - strlen(text[0].text),
"%8lu ", (
unsigned long)profile_data.length());
175 for (
long i = 0; i < (long) profile_data.length(); i++) {
179 *(dp++) = (
char) hex[((*sp >> 4) & 0x0f)];
180 *(dp++) = (
char) hex[((*sp++) & 0x0f)];
185 text[0].text_length = (png_size_t)(dp - text[0].text);
186 text[0].compression = -1;
188 if (text[0].text_length <= allocated_length)
189 png_set_text(ping, ping_info, text, 1);
191 png_free(ping, text[0].text);
192 png_free(ping, text);
195QByteArray png_read_raw_profile(png_textp text)
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,
208 png_charp sp = text[0].text + 1;
213 while (*sp ==
'\0' || *sp ==
' ' || *sp ==
'\n')
215 png_uint_32
length = (png_uint_32) atol(sp);
216 while (*sp !=
' ' && *sp !=
'\n')
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') {
233 *dp = (
unsigned char)(16 * unhex[(
int) *sp++]);
235 (*dp++) += unhex[(int) *sp++];
240void decode_meta_data(png_textp text,
KisMetaData::Store* store, QString type,
int headerSize)
242 dbgFile <<
"Decoding " << type <<
" " << text[0].key;
246 QByteArray rawProfile = png_read_raw_profile(text);
247 if (headerSize > 0) {
248 rawProfile.remove(0, headerSize);
250 if (rawProfile.size() > 0) {
252 buffer.setData(rawProfile);
263 qWarning(
"libpng warning: %s", message);
340 std::size_t rowbytes = png_get_rowbytes(
png_ptr, info_ptr);
359 std::size_t rowbytes = png_get_rowbytes(
png_ptr, info_ptr);
360 for (
int i = 0; i <
height; i++) {
366 for (
int i = 0; i <
height; i++) {
383 QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
386 int nr = in->read((
char*)data,
length);
388 png_error(png_ptr,
"Read Error");
398 QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr);
402 png_error(png_ptr,
"Write Error");
415 dbgFile <<
"Start decoding PNG File";
417 png_byte signature[8];
418 iod->peek((
char*)signature, 8);
420#if PNG_LIBPNG_VER < 10400
421 if (!png_check_sig(signature, 8)) {
423 if (png_sig_cmp(signature, 0, 8) != 0) {
430 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
436#ifdef PNG_SET_USER_LIMITS_SUPPORTED
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);
444 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
445 png_set_benign_errors(png_ptr, 1);
448 png_infop info_ptr = png_create_info_struct(png_ptr);
450 png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0);
455 png_infop end_info = png_create_info_struct(png_ptr);
457 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
463 if (setjmp(png_jmpbuf(png_ptr))) {
464 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
470 png_set_read_fn(png_ptr, iod,
_read_fn);
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);
477 png_read_info(png_ptr, info_ptr);
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);
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);
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);
493 png_read_update_info(png_ptr, info_ptr);
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;
501#ifndef WORDS_BIGENDIAN
502 if (color_nb_bits > 8)
503 png_set_swap(png_ptr);
508 if (csName.first.isEmpty()) {
509 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
513 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
516 png_charp profile_name;
517#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
518 png_bytep profile_data;
520 png_charp profile_data;
522 int compression_type;
528#if defined(PNG_cHRM_SUPPORTED)
529 double whitePointX, whitePointY;
531 double greenX, greenY;
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;
538#if defined(PNG_GAMMA_SUPPORTED)
540 png_get_gAMA(png_ptr, info_ptr, &gamma);
545#if defined(PNG_sRGB_SUPPORTED)
547 png_get_sRGB(png_ptr, info_ptr, &sRGBIntent);
548 dbgFile <<
"sRGB" << sRGBIntent;
551 bool fromBlender =
false;
555 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
557 for (
int i = 0; i < num_comments; i++) {
558 QString key = QString(text_ptr[i].key).toLower();
560 QString relatedFile = text_ptr[i].text;
561 if (relatedFile.contains(
".blend", Qt::CaseInsensitive)){
567 bool loadedImageIsHDR =
false;
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);
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";
579 loadedImageIsHDR = strcmp(profile_name,
"ITUR_2100_PQ_FULL") == 0;
581 else if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains(
"test") && !
m_batchMode) {
590 if (!dlg.
profile().isEmpty()) {
596 dbgFile <<
"no embedded profile, will use the default profile";
599 const QString colorSpaceId =
606 warnFile <<
"The profile " << profile->
name() <<
" is not compatible with the color space model " << csName.first <<
" " << csName.second;
614 if (loadedImageIsHDR &&
627 dbgFile <<
"image has embedded profile: " << profile->
name() <<
"\n";
641 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
653 png_uint_32 x_resolution, y_resolution;
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) {
660 double coeff =
quint8_MAX / (double)(pow((
double)2, color_nb_bits) - 1);
664 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
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") {
675 }
else if (key ==
"description") {
677 }
else if (key ==
"author") {
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") {
687 }
else if (key ==
"preset") {
693 QScopedPointer<KisPNGReaderAbstract> reader;
695 if (interlace_type == PNG_INTERLACE_ADAM7) {
700 }
catch (
const std::bad_alloc& e) {
703 dbgFile <<
"bad alloc: " << e.what();
705 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
712 if (color_type == PNG_COLOR_TYPE_PALETTE) {
713 png_get_PLTE(png_ptr, info_ptr, &
palette, &num_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) {
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];
730 for (png_uint_32 y = 0; y < height; y++) {
733 png_bytep row_pointer = reader->readLine();
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);
741 quint16 *d =
reinterpret_cast<quint16 *
>(it->rawData());
748 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8*
>(d),
reinterpret_cast<quint8*
>(d), 1);
749 }
while (it->nextPixel());
753 quint8 *d = it->rawData();
754 d[0] = (quint8)(stream.
nextValue() * coeff);
756 d[1] = (quint8)(stream.
nextValue() * coeff);
761 }
while (it->nextPixel());
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);
770 quint16 *d =
reinterpret_cast<quint16 *
>(it->rawData());
774 if (hasalpha) d[3] = *(src++);
776 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8 *
>(d),
reinterpret_cast<quint8*
>(d), 1);
777 }
while (it->nextPixel());
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;
788 }
while (it->nextPixel());
791 case PNG_COLOR_TYPE_PALETTE: {
794 quint8 *d = it->rawData();
796 quint8 alpha = palette_alpha[ index ];
800 png_color c =
palette[ index ];
806 }
while (it->nextPixel());
815 png_read_end(png_ptr, end_info);
819 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
830 if (!fp.open(QIODevice::ReadOnly)) {
831 dbgFile <<
"Failed to open PNG File";
849 if (store->
open(filename)) {
851 if (!io.
open(QIODevice::WriteOnly)) {
852 dbgFile <<
"Could not open for writing:" << filename;
865 options.
alpha =
true;
875 if (!success.
isOk()) {
876 dbgFile <<
"Saving PNG failed:" << filename;
877 delete metaDataStore;
880 delete metaDataStore;
882 if (!store->
close()) {
886 dbgFile <<
"Opening of data file failed :" << filename;
896 dbgFile <<
"Start writing PNG File " << filename;
899 if (!fp.open(QIODevice::WriteOnly)) {
900 dbgFile <<
"Failed to open PNG File for writing";
913 if (!options.
alpha) {
916 tmp->fill(imageRect, c);
918 gc.
bitBlt(imageRect.topLeft(), device, imageRect);
931 bool needColorTransform =
false;
937 needColorTransform =
true;
950 needColorTransform =
true;
957 if (needColorTransform) {
973 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
978#ifdef PNG_SET_USER_LIMITS_SUPPORTED
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);
986 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
987 png_set_benign_errors(png_ptr, 1);
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);
995#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
996 png_set_check_for_invalid_index(png_ptr, 0);
999 png_infop info_ptr = png_create_info_struct(png_ptr);
1001 png_destroy_write_struct(&png_ptr, (png_infopp)0);
1006 if (setjmp(png_jmpbuf(png_ptr))) {
1007 png_destroy_write_struct(&png_ptr, &info_ptr);
1017 png_set_compression_level(png_ptr, options.
compression);
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);
1029 int color_type = getColorTypeforColorSpace(device->
colorSpace(), options.
alpha);
1031 Q_ASSERT(color_type > -1);
1034 QScopedArrayPointer<png_color>
palette;
1035 int num_palette = 0;
1037 palette.reset(
new png_color[255]);
1041 bool toomuchcolor =
false;
1044 bool findit =
false;
1045 for (
int i = 0; i < num_palette; i++) {
1054 if (num_palette == 255) {
1055 toomuchcolor =
true;
1058 palette[num_palette].red = c[2];
1059 palette[num_palette].green = c[1];
1060 palette[num_palette].blue = c[0];
1065 if (!toomuchcolor) {
1066 dbgFile <<
"Found a palette of " << num_palette <<
" colors";
1067 color_type = PNG_COLOR_TYPE_PALETTE;
1068 if (num_palette <= 2) {
1070 }
else if (num_palette <= 4) {
1072 }
else if (num_palette <= 16) {
1082 int interlace_type = options.
interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
1086 png_set_IHDR(png_ptr, info_ptr,
1090 color_type, interlace_type,
1091 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1118#if defined(PNG_GAMMA_SUPPORTED)
1122 png_set_gAMA_fixed(png_ptr, info_ptr, 15000);
1123 dbgFile <<
"gAMA" <<
"(Rec 2100)";
1126#if defined PNG_cHRM_SUPPORTED
1127 png_set_cHRM_fixed(png_ptr, info_ptr,
1133 dbgFile <<
"cHRM" <<
"(Rec 2100)";
1143 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1144 png_set_PLTE(png_ptr, info_ptr,
palette.data(), num_palette);
1148 while (it != annotationsEnd) {
1149 if (!(*it) || (*it)->type().isEmpty()) {
1150 dbgFile <<
"Warning: empty annotation";
1155 dbgFile <<
"Trying to store annotation of type " << (*it) -> type() <<
" of size " << (*it) -> annotation() . size();
1157 if ((*it) -> type().startsWith(QString(
"krita_attribute:"))) {
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));
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;
1171 png_set_text(png_ptr, info_ptr, text, 1);
1172 png_free(png_ptr, text);
1179 QByteArray colorProfileData = colorProfile->
rawData();
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());
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());
1200 QString title = info->
aboutInfo(
"title");
1202 fillText(texts + nbtexts,
"Title", title);
1205 QString abstract = info->
aboutInfo(
"subject");
1206 if (abstract.isEmpty()) {
1210 QString keywords = info->
aboutInfo(
"keyword");
1211 if (!keywords.isEmpty()) {
1212 abstract = abstract +
" keywords: " + keywords;
1214 fillText(texts + nbtexts,
"Description", abstract);
1218 QString license = info->
aboutInfo(
"license");
1220 fillText(texts + nbtexts,
"Copyright", license);
1224 QString author = info->
authorInfo(
"creator");
1228 if (!contact.isEmpty()) {
1229 author = author+
"("+contact+
")";
1232 fillText(texts + nbtexts,
"Author", author);
1236 png_set_text(png_ptr, info_ptr, texts, nbtexts);
1242 if (metaData && !metaData->
empty()) {
1244 dbgFile <<
"Trying to save exif information";
1251 writeRawProfile(png_ptr, info_ptr,
"exif", buffer.data());
1255 dbgFile <<
"Trying to save iptc information";
1262 dbgFile <<
"IPTC information size is" << buffer.data().size();
1263 writeRawProfile(png_ptr, info_ptr,
"iptc", buffer.data());
1267 dbgFile <<
"Trying to save XMP information";
1274 dbgFile <<
"XMP information size is" << buffer.data().size();
1275 writeRawProfile(png_ptr, info_ptr,
"xmp", buffer.data());
1281 png_uint_32 x_resolution, y_resolution;
1283 png_set_pHYs(png_ptr, info_ptr,
CM_TO_POINT(xRes) * 100.0,
CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER);
1286 png_write_info(png_ptr, info_ptr);
1287 png_write_flush(png_ptr);
1290#ifndef WORDS_BIGENDIAN
1291 if (color_nb_bits > 8)
1292 png_set_swap(png_ptr);
1298 struct RowPointersStruct {
1299 RowPointersStruct(
const QSize &size,
int pixelSize)
1300 : numRows(size.height())
1302 rows =
new png_byte*[numRows];
1304 for (
int i = 0; i < numRows; i++) {
1305 rows[i] =
new png_byte[size.width() * pixelSize];
1309 ~RowPointersStruct() {
1310 for (
int i = 0; i < numRows; i++) {
1316 const int numRows = 0;
1317 png_byte** rows = 0;
1322 RowPointersStruct rowPointers(imageRect.size(), device->
pixelSize());
1325 for (
int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
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]);
1334 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1336 if (options.
alpha) *(dst++) = d[1];
1339 quint8 *dst = rowPointers.rows[row];
1343 if (options.
alpha) *(dst++) = d[1];
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]);
1352 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1356 if (options.
alpha) *(dst++) = d[3];
1359 quint8 *dst = rowPointers.rows[row];
1365 if (options.
alpha) *(dst++) = d[3];
1369 case PNG_COLOR_TYPE_PALETTE: {
1370 quint8 *dst = rowPointers.rows[row];
1375 for (i = 0; i < num_palette; i++) {
1391 png_write_image(png_ptr, rowPointers.rows);
1394 png_write_end(png_ptr, info_ptr);
1397 png_destroy_write_struct(&png_ptr, &info_ptr);
1409 if (png_ptr == 0 || row_number >
PNG_MAX_UINT || pass > 7)
return;
1415 return colorSpaceIdSupported(cs->
id());
qreal length(const QPointF &vec)
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"))
constexpr qreal POINT_TO_CM(qreal px)
constexpr qreal CM_TO_POINT(qreal cm)
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...
KisUndoStore * createUndoStore()
KoDocumentInfo * documentInfo() const
void addAnnotation(KisAnnotationSP annotation)
KisGroupLayerSP rootLayer() const
void setResolution(double xres, double yres)
~KisPNGConverter() override
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)
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
virtual ~KisPNGReaderAbstract()
~KisPNGReaderFullImage() override
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)
~KisPNGReaderLineByLine() override
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
bool open(OpenMode m) override
bool open(const QString &name)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
const quint16 quint16_MAX
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)
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
@ FormatFeaturesUnsupported
@ FormatColorSpaceUnsupported
rgba palette[MAX_PALETTE]
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
QColor transparencyFillColor
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)