14#define PNG_MAX_UINT PNG_UINT_31_MAX
27#include <QApplication>
29#include <klocalizedstring.h>
66int getColorTypeforColorSpace(
const KoColorSpace * cs ,
bool alpha)
69 QString
id = cs->
id();
71 if (
id ==
"GRAYA" ||
id ==
"GRAYAU16" ||
id ==
"GRAYA16") {
72 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
74 if (
id ==
"RGBA" ||
id ==
"RGBA16" ||
id ==
"RGBAF16" ||
id ==
"RGBAF32") {
75 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
82bool colorSpaceIdSupported(
const QString &
id)
84 return id ==
"RGBA" ||
id ==
"RGBA16" ||
85 id ==
"GRAYA" ||
id ==
"GRAYAU16" ||
id ==
"GRAYA16";
90 QPair<QString, QString> r;
92 if (color_type == PNG_COLOR_TYPE_PALETTE) {
96 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
98 }
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
101 if (color_nb_bits == 16) {
103 }
else if (color_nb_bits <= 8) {
111void fillText(png_text* p_text,
const char* key, QString& text)
113 p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
114 p_text->key =
const_cast<char *
>(key);
115 char* textc =
new char[text.length()+1];
116 strcpy(textc, text.toLatin1());
117 p_text->text = textc;
118 p_text->text_length = text.length() + 1;
121long formatStringList(
char *
string,
const size_t length,
const char *format, va_list operands)
123 int n = vsnprintf(
string,
length, format, operands);
131long formatString(
char *
string,
const size_t length,
const char *format, ...)
137 va_start(operands, format);
138 n = (long) formatStringList(
string,
length, format, operands);
143void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data)
148 png_uint_32 allocated_length, description_length;
150 const uchar hex[16] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f'};
152 dbgFile <<
"Writing Raw profile: type=" << profile_type <<
", length=" << profile_data.length() << Qt::endl;
154 text = (png_textp) png_malloc(ping, (png_uint_32)
sizeof(png_text));
155 description_length = profile_type.length();
156 allocated_length = (png_uint_32)(profile_data.length() * 2 + (profile_data.length() >> 5) + 20 + description_length);
158 text[0].text = (png_charp) png_malloc(ping, allocated_length);
159 memset(text[0].text, 0, allocated_length);
161 QString key = QLatin1String(
"Raw profile type ") + profile_type.toLatin1();
162 QByteArray keyData = key.toLatin1();
163 text[0].key = keyData.data();
165 uchar* sp = (uchar*)profile_data.data();
166 png_charp dp = text[0].text;
169 memcpy(dp, profile_type.toLatin1().constData(), profile_type.length());
171 dp += description_length;
174 formatString(dp, allocated_length - strlen(text[0].text),
"%8lu ", (
unsigned long)profile_data.length());
178 for (
long i = 0; i < (long) profile_data.length(); i++) {
182 *(dp++) = (
char) hex[((*sp >> 4) & 0x0f)];
183 *(dp++) = (
char) hex[((*sp++) & 0x0f)];
188 text[0].text_length = (png_size_t)(dp - text[0].text);
189 text[0].compression = -1;
191 if (text[0].text_length <= allocated_length)
192 png_set_text(ping, ping_info, text, 1);
194 png_free(ping, text[0].text);
195 png_free(ping, text);
198QByteArray png_read_raw_profile(png_textp text)
202 static const unsigned char unhex[103] = {
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, 0, 0, 0,
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0,
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
211 png_charp sp = text[0].text + 1;
216 while (*sp ==
'\0' || *sp ==
' ' || *sp ==
'\n')
218 png_uint_32
length = (png_uint_32) atol(sp);
219 while (*sp !=
' ' && *sp !=
'\n')
226 unsigned char *dp = (
unsigned char*)profile.data();
227 png_uint_32 nibbles =
length * 2;
228 for (png_uint_32 i = 0; i < nibbles; i++) {
229 while (*sp <
'0' || (*sp >
'9' && *sp <
'a') || *sp >
'f') {
236 *dp = (
unsigned char)(16 * unhex[(
int) *sp++]);
238 (*dp++) += unhex[(int) *sp++];
243void decode_meta_data(png_textp text,
KisMetaData::Store* store, QString type,
int headerSize)
245 dbgFile <<
"Decoding " << type <<
" " << text[0].key;
249 QByteArray rawProfile = png_read_raw_profile(text);
250 if (headerSize > 0) {
251 rawProfile.remove(0, headerSize);
253 if (rawProfile.size() > 0) {
255 buffer.setData(rawProfile);
266 qWarning(
"libpng warning: %s", message);
343 std::size_t rowbytes = png_get_rowbytes(
png_ptr, info_ptr);
362 std::size_t rowbytes = png_get_rowbytes(
png_ptr, info_ptr);
363 for (
int i = 0; i <
height; i++) {
369 for (
int i = 0; i <
height; i++) {
386 QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
389 int nr = in->read((
char*)data,
length);
391 png_error(png_ptr,
"Read Error");
401 QIODevice* out = (QIODevice*)png_get_io_ptr(png_ptr);
405 png_error(png_ptr,
"Write Error");
418template <
typename src_channel_type,
419 typename dst_channel_type>
463 dbgFile <<
"Start decoding PNG File";
465 png_byte signature[8];
466 iod->peek((
char*)signature, 8);
468#if PNG_LIBPNG_VER < 10400
469 if (!png_check_sig(signature, 8)) {
471 if (png_sig_cmp(signature, 0, 8) != 0) {
478 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
484#ifdef PNG_SET_USER_LIMITS_SUPPORTED
486 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
487 png_set_chunk_cache_max(png_ptr, 0);
488 png_set_chunk_malloc_max(png_ptr, 0);
492 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
493 png_set_benign_errors(png_ptr, 1);
496 png_infop info_ptr = png_create_info_struct(png_ptr);
498 png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0);
503 png_infop end_info = png_create_info_struct(png_ptr);
505 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
511 if (setjmp(png_jmpbuf(png_ptr))) {
512 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
518 png_set_read_fn(png_ptr, iod,
_read_fn);
520#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
521 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
525 png_read_info(png_ptr, info_ptr);
528 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8) {
529 png_set_expand(png_ptr);
532 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE && png_get_bit_depth(png_ptr, info_ptr) < 8) {
533 png_set_packing(png_ptr);
537 if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE &&
538 (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) {
539 png_set_expand(png_ptr);
541 png_read_update_info(png_ptr, info_ptr);
544 png_uint_32 width, height;
545 int color_nb_bits, color_type, interlace_type;
546 png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, 0, 0);
547 dbgFile <<
"width = " << width <<
" height = " << height <<
" color_nb_bits = " << color_nb_bits <<
" color_type = " << color_type <<
" interlace_type = " << interlace_type << Qt::endl;
549#ifndef WORDS_BIGENDIAN
550 if (color_nb_bits > 8)
551 png_set_swap(png_ptr);
556 if (csName.first.isEmpty()) {
557 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
561 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
564 png_charp profile_name;
565#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
566 png_bytep profile_data;
568 png_charp profile_data;
570 int compression_type;
576#if defined(PNG_cHRM_SUPPORTED)
577 double whitePointX, whitePointY;
579 double greenX, greenY;
581 png_get_cHRM(png_ptr,info_ptr, &whitePointX, &whitePointY, &redX, &redY, &greenX, &greenY, &blueX, &blueY);
582 dbgFile <<
"cHRM:" << whitePointX << whitePointY << redX << redY << greenX << greenY << blueX << blueY;
586#if defined(PNG_GAMMA_SUPPORTED)
588 png_get_gAMA(png_ptr, info_ptr, &gamma);
593#if defined(PNG_sRGB_SUPPORTED)
595 png_get_sRGB(png_ptr, info_ptr, &sRGBIntent);
596 dbgFile <<
"sRGB" << sRGBIntent;
599 bool fromBlender =
false;
603 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
605 for (
int i = 0; i < num_comments; i++) {
606 QString key = QString(text_ptr[i].key).toLower();
608 QString relatedFile = text_ptr[i].text;
609 if (relatedFile.contains(
".blend", Qt::CaseInsensitive)){
615 bool loadedImageHasLegacyHDRDummyProfile =
false;
618 bool loadedCICP =
false;
621#if defined(PNG_cICP_SUPPORTED)
622 png_byte primaries = 0;
623 png_byte transfer = 0;
625 png_byte fullrange = 0;
626 if (png_get_cICP(png_ptr, info_ptr, &primaries, &transfer, &matrix, &fullrange)) {
628 dbgFile <<
"load cicp" << primaries << transfer << matrix << fullrange;
641 profile = cicpProfile;
648 if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) {
649 QByteArray profile_rawdata(
reinterpret_cast<char*
>(profile_data), proflen);
653 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";
657 loadedImageHasLegacyHDRDummyProfile = strcmp(profile_name,
"ITUR_2100_PQ_FULL") == 0;
659 else if (color_nb_bits == 16 && !fromBlender && !qAppName().toLower().contains(
"test") && !
m_batchMode) {
668 if (!dlg.
profile().isEmpty()) {
674 dbgFile <<
"no embedded profile, will use the default sRGB profile";
678 const QString colorSpaceId =
685 warnFile <<
"The profile " << profile->
name() <<
" is not compatible with the color space model " << csName.first <<
" " << csName.second;
693 if (loadedImageHasLegacyHDRDummyProfile &&
706 dbgFile <<
"image has embedded profile: " << profile->
name() <<
"\n";
720 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
732 png_uint_32 x_resolution = 0, y_resolution = 0;
734 png_get_pHYs(png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type);
735 if (x_resolution > 0 && y_resolution > 0 && unit_type == PNG_RESOLUTION_METER) {
737 }
else if (unit_type == PNG_RESOLUTION_UNKNOWN) {
741#if defined(PNG_cLLI_SUPPORTED)
744 const double refWhiteMultiplier = 1.0/profile->
hdrReferenceWhite().value_or(203.0);
751#if defined(PNG_mDCV_SUPPORTED)
753 if (png_get_mDCV(png_ptr, info_ptr,
763 double coeff =
quint8_MAX / (double)(pow((
double)2, color_nb_bits) - 1);
767 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
770 dbgFile <<
"There are " << num_comments <<
" comments in the text";
771 for (
int i = 0; i < num_comments; i++) {
772 QString key = QString(text_ptr[i].key).toLower();
773 dbgFile <<
"key: " << text_ptr[i].key
774 <<
", containing: " << text_ptr[i].text
775 <<
": " << (key ==
"raw profile type exif " ?
"isExif" :
"something else");
776 if (key ==
"title") {
778 }
else if (key ==
"description") {
780 }
else if (key ==
"author") {
782 }
else if (key.contains(
"raw profile type exif")) {
783 decode_meta_data(text_ptr + i, layer->
metaData(),
"exif", 6);
784 }
else if (key.contains(
"raw profile type iptc")) {
785 decode_meta_data(text_ptr + i, layer->
metaData(),
"iptc", 14);
786 }
else if (key.contains(
"raw profile type xmp")) {
787 decode_meta_data(text_ptr + i, layer->
metaData(),
"xmp", 0);
788 }
else if (key ==
"version") {
790 }
else if (key ==
"preset") {
796 QScopedPointer<KisPNGReaderAbstract> reader;
798 if (interlace_type == PNG_INTERLACE_ADAM7) {
803 }
catch (
const std::bad_alloc& e) {
806 dbgFile <<
"bad alloc: " << e.what();
808 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
815 if (color_type == PNG_COLOR_TYPE_PALETTE) {
816 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
820 quint8 palette_alpha[256];
821 memset(palette_alpha, 255, 256);
822 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
823 if (color_type == PNG_COLOR_TYPE_PALETTE) {
826 png_get_tRNS(png_ptr, info_ptr, &alpha_ptr, &num_alpha, 0);
827 for (
int i = 0; i < num_alpha; ++i) {
828 palette_alpha[i] = alpha_ptr[i];
833 for (png_uint_32 y = 0; y < height; y++) {
836 png_bytep row_pointer = reader->readLine();
838 switch (color_type) {
839 case PNG_COLOR_TYPE_GRAY:
840 case PNG_COLOR_TYPE_GRAY_ALPHA:
841 if (color_nb_bits == 16) {
842 quint16 *src =
reinterpret_cast<quint16 *
>(row_pointer);
844 quint16 *d =
reinterpret_cast<quint16 *
>(it->rawData());
851 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8*
>(d),
reinterpret_cast<quint8*
>(d), 1);
852 }
while (it->nextPixel());
856 quint8 *d = it->rawData();
857 d[0] = (quint8)(stream.
nextValue() * coeff);
859 d[1] = (quint8)(stream.
nextValue() * coeff);
864 }
while (it->nextPixel());
868 case PNG_COLOR_TYPE_RGB:
869 case PNG_COLOR_TYPE_RGB_ALPHA:
871 if (color_nb_bits == 16) {
873 quint16 *src =
reinterpret_cast<quint16 *
>(row_pointer);
875 float *d =
reinterpret_cast<float *
>(it->rawData());
876 d[0] = policy.remove(linearConversionPolicy, *(src++));
877 d[1] = policy.remove(linearConversionPolicy, *(src++));
878 d[2] = policy.remove(linearConversionPolicy, *(src++));
881 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8 *
>(d),
reinterpret_cast<quint8*
>(d), 1);
882 }
while (it->nextPixel());
885 quint8 *src =
reinterpret_cast<quint8 *
>(row_pointer);
887 float *d =
reinterpret_cast<float *
>(it->rawData());
888 d[0] = policy.remove(linearConversionPolicy, *(src++));
889 d[1] = policy.remove(linearConversionPolicy, *(src++));
890 d[2] = policy.remove(linearConversionPolicy, *(src++));
893 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8 *
>(d),
reinterpret_cast<quint8*
>(d), 1);
894 }
while (it->nextPixel());
897 if (color_nb_bits == 16) {
898 quint16 *src =
reinterpret_cast<quint16 *
>(row_pointer);
900 quint16 *d =
reinterpret_cast<quint16 *
>(it->rawData());
904 if (hasalpha) d[3] = *(src++);
906 if (transform) transform->
transformInPlace(
reinterpret_cast<quint8 *
>(d),
reinterpret_cast<quint8*
>(d), 1);
907 }
while (it->nextPixel());
911 quint8 *d = it->rawData();
912 d[2] = (quint8)(stream.
nextValue() * coeff);
913 d[1] = (quint8)(stream.
nextValue() * coeff);
914 d[0] = (quint8)(stream.
nextValue() * coeff);
915 if (hasalpha) d[3] = (quint8)(stream.
nextValue() * coeff);
916 else d[3] = UCHAR_MAX;
918 }
while (it->nextPixel());
922 case PNG_COLOR_TYPE_PALETTE: {
925 quint8 *d = it->rawData();
927 quint8 alpha = palette_alpha[ index ];
931 png_color c = palette[ index ];
937 }
while (it->nextPixel());
946 png_read_end(png_ptr, end_info);
950 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
961 if (!fp.open(QIODevice::ReadOnly)) {
962 dbgFile <<
"Failed to open PNG File";
980 if (store->
open(filename)) {
982 if (!io.
open(QIODevice::WriteOnly)) {
983 dbgFile <<
"Could not open for writing:" << filename;
996 options.
alpha =
true;
1006 if (!success.
isOk()) {
1007 dbgFile <<
"Saving PNG failed:" << filename;
1008 delete metaDataStore;
1011 delete metaDataStore;
1013 if (!store->
close()) {
1017 dbgFile <<
"Opening of data file failed :" << filename;
1027 dbgFile <<
"Start writing PNG File " << filename;
1029 QFile fp (filename);
1030 if (!fp.open(QIODevice::WriteOnly)) {
1031 dbgFile <<
"Failed to open PNG File for writing";
1044 if (!options.
alpha) {
1047 tmp->fill(imageRect, c);
1049 gc.
bitBlt(imageRect.topLeft(), device, imageRect);
1065 bool needColorTransform =
false;
1072 needColorTransform =
true;
1098 needColorTransform =
true;
1105 if (needColorTransform) {
1124 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
1129#ifdef PNG_SET_USER_LIMITS_SUPPORTED
1131 png_set_user_limits(png_ptr, 0x7fffffff, 0x7fffffff);
1132 png_set_chunk_cache_max(png_ptr, 0);
1133 png_set_chunk_malloc_max(png_ptr, 0);
1137 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
1138 png_set_benign_errors(png_ptr, 1);
1141#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
1142 png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
1146#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
1147 png_set_check_for_invalid_index(png_ptr, 0);
1150 png_infop info_ptr = png_create_info_struct(png_ptr);
1152 png_destroy_write_struct(&png_ptr, (png_infopp)0);
1157 if (setjmp(png_jmpbuf(png_ptr))) {
1158 png_destroy_write_struct(&png_ptr, &info_ptr);
1168 png_set_compression_level(png_ptr, options.
compression);
1173 png_set_compression_mem_level(png_ptr, 8);
1174 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
1175 png_set_compression_window_bits(png_ptr, 15);
1176 png_set_compression_method(png_ptr, 8);
1177 png_set_compression_buffer_size(png_ptr, 8192);
1180 int color_type = getColorTypeforColorSpace(device->
colorSpace(), options.
alpha);
1182 Q_ASSERT(color_type > -1);
1185 QScopedArrayPointer<png_color> palette;
1186 int num_palette = 0;
1188 palette.reset(
new png_color[255]);
1192 bool toomuchcolor =
false;
1195 bool findit =
false;
1196 for (
int i = 0; i < num_palette; i++) {
1197 if (palette[i].red == c[2] &&
1198 palette[i].green == c[1] &&
1199 palette[i].blue == c[0]) {
1205 if (num_palette == 255) {
1206 toomuchcolor =
true;
1209 palette[num_palette].red = c[2];
1210 palette[num_palette].green = c[1];
1211 palette[num_palette].blue = c[0];
1216 if (!toomuchcolor) {
1217 dbgFile <<
"Found a palette of " << num_palette <<
" colors";
1218 color_type = PNG_COLOR_TYPE_PALETTE;
1219 if (num_palette <= 2) {
1221 }
else if (num_palette <= 4) {
1223 }
else if (num_palette <= 16) {
1233 int interlace_type = options.
interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
1237 png_set_IHDR(png_ptr, info_ptr,
1241 color_type, interlace_type,
1242 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1255 png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
1270#if defined(PNG_GAMMA_SUPPORTED)
1273 if (colorProfilePQ) {
1276 png_set_gAMA_fixed(png_ptr, info_ptr, 15000);
1277 dbgFile <<
"gAMA" <<
"(Rec 2100)";
1280 png_set_gAMA(png_ptr, info_ptr, gamma);
1284#if defined PNG_cHRM_SUPPORTED
1285 if (colorProfilePQ) {
1286 png_set_cHRM_fixed(png_ptr, info_ptr,
1292 dbgFile <<
"cHRM" <<
"(Rec 2100)";
1296 png_set_cHRM(png_ptr, info_ptr,
1297 whitePoint.
x, whitePoint.
y,
1298 colorants[0].x, colorants[0].y,
1299 colorants[1].x, colorants[1].y,
1300 colorants[2].x, colorants[2].y);
1310 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1311 png_set_PLTE(png_ptr, info_ptr, palette.data(), num_palette);
1315 while (it != annotationsEnd) {
1316 if (!(*it) || (*it)->type().isEmpty()) {
1317 dbgFile <<
"Warning: empty annotation";
1322 dbgFile <<
"Trying to store annotation of type " << (*it) -> type() <<
" of size " << (*it) -> annotation() . size();
1324 if ((*it) -> type().startsWith(QString(
"krita_attribute:"))) {
1327 dbgFile <<
"cannot save this annotation : " << (*it) -> type();
1328 }
else if ((*it)->type() ==
"kpp_version" || (*it)->type() ==
"kpp_preset" ) {
1329 dbgFile <<
"Saving preset information " << (*it)->description();
1330 png_textp text = (png_textp) png_malloc(png_ptr, (png_uint_32)
sizeof(png_text));
1332 QByteArray keyData = (*it)->description().toLatin1();
1333 text[0].key = keyData.data();
1334 text[0].text = (
char*)(*it)->annotation().data();
1335 text[0].text_length = (*it)->annotation().size();
1336 text[0].compression = -1;
1338 png_set_text(png_ptr, info_ptr, text, 1);
1339 png_free(png_ptr, text);
1346 QByteArray colorProfileData = colorProfile->
rawData();
1354 bool wroteCICP =
false;
1358 if (colorProfilePQ) {
1369 png_byte matrixCoef = 0;
1370 png_byte fullRange = 1;
1371 dbgFile <<
"Writing cicp" << primaries << transfer << matrixCoef << fullRange;
1372 png_set_cICP(png_ptr, info_ptr, primaries, transfer, matrixCoef, fullRange);
1376#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
1377 const char *typeString =
"icc";
1378 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size());
1381 char typeStringICC[] =
"icc";
1382 char *typeString = typeStringICC;
1383 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size());
1387#if defined(PNG_cLLI_SUPPORTED)
1391 const double maxcll = clli->maxContentLightLevel * hdrRefWhite;
1392 const double maxfall = clli->maxFrameAverageLightLevel * hdrRefWhite;
1393 png_set_cLLI(png_ptr, info_ptr, maxcll, maxfall);
1396#if defined(PNG_mDCV_SUPPORTED)
1399 png_set_mDCV(png_ptr, info_ptr,
1400 cvi->white.x, cvi->white.y,
1401 cvi->red.x, cvi->red.y,
1402 cvi->green.x,cvi->green.y,
1403 cvi->blue.x, cvi->blue.y, cvi->maxLuminance, cvi->minLuminance);
1413 QString title = info->
aboutInfo(
"title");
1415 fillText(texts + nbtexts,
"Title", title);
1418 QString abstract = info->
aboutInfo(
"subject");
1419 if (abstract.isEmpty()) {
1423 QString keywords = info->
aboutInfo(
"keyword");
1424 if (!keywords.isEmpty()) {
1425 abstract = abstract +
" keywords: " + keywords;
1427 fillText(texts + nbtexts,
"Description", abstract);
1431 QString license = info->
aboutInfo(
"license");
1433 fillText(texts + nbtexts,
"Copyright", license);
1437 QString author = info->
authorInfo(
"creator");
1441 if (!contact.isEmpty()) {
1442 author = author+
"("+contact+
")";
1445 fillText(texts + nbtexts,
"Author", author);
1449 png_set_text(png_ptr, info_ptr, texts, nbtexts);
1455 if (metaData && !metaData->
empty()) {
1457 dbgFile <<
"Trying to save exif information";
1464 writeRawProfile(png_ptr, info_ptr,
"exif", buffer.data());
1468 dbgFile <<
"Trying to save iptc information";
1475 dbgFile <<
"IPTC information size is" << buffer.data().size();
1476 writeRawProfile(png_ptr, info_ptr,
"iptc", buffer.data());
1480 dbgFile <<
"Trying to save XMP information";
1487 dbgFile <<
"XMP information size is" << buffer.data().size();
1488 writeRawProfile(png_ptr, info_ptr,
"xmp", buffer.data());
1494 png_uint_32 x_resolution, y_resolution;
1496 png_set_pHYs(png_ptr, info_ptr,
CM_TO_POINT(xRes) * 100.0,
CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER);
1499 png_write_info(png_ptr, info_ptr);
1500 png_write_flush(png_ptr);
1503#ifndef WORDS_BIGENDIAN
1504 if (color_nb_bits > 8)
1505 png_set_swap(png_ptr);
1511 struct RowPointersStruct {
1512 RowPointersStruct(
const QSize &size,
int pixelSize)
1513 : numRows(size.height())
1515 rows =
new png_byte*[numRows];
1517 for (
int i = 0; i < numRows; i++) {
1518 rows[i] =
new png_byte[size.width() * pixelSize];
1522 ~RowPointersStruct() {
1523 for (
int i = 0; i < numRows; i++) {
1529 const int numRows = 0;
1530 png_byte** rows = 0;
1535 RowPointersStruct rowPointers(imageRect.size(), device->
pixelSize());
1538 for (
int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
1541 switch (color_type) {
1542 case PNG_COLOR_TYPE_GRAY:
1543 case PNG_COLOR_TYPE_GRAY_ALPHA:
1544 if (color_nb_bits == 16) {
1545 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1547 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1549 if (options.
alpha) *(dst++) = d[1];
1552 quint8 *dst = rowPointers.rows[row];
1556 if (options.
alpha) *(dst++) = d[1];
1560 case PNG_COLOR_TYPE_RGB:
1561 case PNG_COLOR_TYPE_RGB_ALPHA:
1562 if (isFloatingPoint) {
1563 if (color_nb_bits == 16) {
1566 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1568 const float *d =
reinterpret_cast<const float *
>(it->
oldRawData());
1578 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1580 const half *d =
reinterpret_cast<const half *
>(it->
oldRawData());
1592 quint8 *dst =
reinterpret_cast<quint8 *
>(rowPointers.rows[row]);
1594 const float *d =
reinterpret_cast<const float *
>(it->
oldRawData());
1604 quint8 *dst =
reinterpret_cast<quint8 *
>(rowPointers.rows[row]);
1606 const half *d =
reinterpret_cast<const half *
>(it->
oldRawData());
1616 if (color_nb_bits == 16) {
1617 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1619 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1623 if (options.
alpha) *(dst++) = d[3];
1626 quint8 *dst = rowPointers.rows[row];
1632 if (options.
alpha) *(dst++) = d[3];
1637 case PNG_COLOR_TYPE_PALETTE: {
1638 quint8 *dst = rowPointers.rows[row];
1643 for (i = 0; i < num_palette; i++) {
1644 if (palette[i].red == d[2] &&
1645 palette[i].green == d[1] &&
1646 palette[i].blue == d[0]) {
1659 png_write_image(png_ptr, rowPointers.rows);
1662 png_write_end(png_ptr, info_ptr);
1665 png_destroy_write_struct(&png_ptr, &info_ptr);
1677 if (png_ptr == 0 || row_number >
PNG_MAX_UINT || pass > 7)
return;
1683 return colorSpaceIdSupported(cs->
id());
qreal length(const QPointF &vec)
float value(const T *src, size_t ch)
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"))
ColorPrimaries
The colorPrimaries enum Enum of colorants, follows ITU H.273 for values 0 to 255, and has extra known...
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
@ TRC_ITU_R_BT_2100_0_HLG
ALWAYS_INLINE float removeHLGCurve(float x) noexcept
ALWAYS_INLINE float applySmpte2048Curve(float x, float refWhite=203.0) noexcept
ALWAYS_INLINE float removeSMPTE_ST_428Curve(float x) noexcept
ALWAYS_INLINE float applySMPTE_ST_428Curve(float x) noexcept
ALWAYS_INLINE float removeSmpte2048Curve(float x, float refWhite=203.0) noexcept
ALWAYS_INLINE float applyHLGCurve(float x) noexcept
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
std::optional< KisRelativeContentLightLevelInformation > relativeContentLightLevelInformation() const
relativeContentLightLevelInformation This returns (optionally) a KisRelativeContentLightLevelInformat...
void setColorVolumeInformation(const std::optional< KisColorVolumeInformation > cvi)
Set the color volume information.
void addAnnotation(KisAnnotationSP annotation)
KisGroupLayerSP rootLayer() const
std::optional< KisColorVolumeInformation > colorVolumeInformation() const
colorVolumeInformation
void setRelativeContentLightLevelInformation(const std::optional< KisRelativeContentLightLevelInformation > clli)
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
static _Tdst scaleToA(_T a)
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
static ALWAYS_INLINE dst_channel_type remove(ConversionPolicy linearizePolicy, src_channel_type value, float refWhite=203.0)
static ALWAYS_INLINE dst_channel_type apply(ConversionPolicy linearizePolicy, src_channel_type value, float refWhite=203.0)
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
bool storeExtraColorChunks
ConversionPolicy floatingPointConversion
bool convertFloatToRec2020
QColor transparencyFillColor
double maxFrameAverageLightLevel
maxFrameAverage MaxFrameAverageLightLevel or MaxFALL is the average of average pixel brightnesses in ...
double maxContentLightLevel
The KoColorProfileQuery struct.
virtual bool isSuitableForOutput() const =0
virtual std::optional< double > hdrReferenceWhite() const =0
hdrReferenceWhite HDR reference white is only available for Perceptual Quantizer profiles that save t...
virtual bool isSuitableForWorkspace() const =0
virtual QByteArray rawData() const
virtual KoColorimetryUtils::xyY getWhitePointxyY() const =0
virtual ColorPrimaries getColorPrimaries() const
getColorPrimaries
virtual QVector< qreal > getEstimatedTRC() const =0
virtual bool isSuitableForInput() const =0
virtual QVector< KoColorimetryUtils::xyY > getColorantsxyY() const =0
virtual TransferCharacteristics getTransferCharacteristics() const
getTransferCharacteristics This function should be subclassed at some point so we can get the value f...
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 * profileFor(const KoColorProfileQuery &query, const bool generate=true) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...
const KoColorProfile * p709SRGBProfile() const
const KoColorProfile * p2020PQProfile() const
const KoColorProfile * p2020G10Profile() const
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)