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;
1355#if defined(PNG_cICP_SUPPORTED)
1359 if (colorProfilePQ) {
1370 png_byte matrixCoef = 0;
1371 png_byte fullRange = 1;
1372 dbgFile <<
"Writing cicp" << primaries << transfer << matrixCoef << fullRange;
1373 png_set_cICP(png_ptr, info_ptr, primaries, transfer, matrixCoef, fullRange);
1378#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5
1379 const char *typeString =
"icc";
1380 png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size());
1383 char typeStringICC[] =
"icc";
1384 char *typeString = typeStringICC;
1385 png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size());
1389#if defined(PNG_cLLI_SUPPORTED)
1393 const double maxcll = clli->maxContentLightLevel * hdrRefWhite;
1394 const double maxfall = clli->maxFrameAverageLightLevel * hdrRefWhite;
1395 png_set_cLLI(png_ptr, info_ptr, maxcll, maxfall);
1398#if defined(PNG_mDCV_SUPPORTED)
1401 png_set_mDCV(png_ptr, info_ptr,
1402 cvi->white.x, cvi->white.y,
1403 cvi->red.x, cvi->red.y,
1404 cvi->green.x,cvi->green.y,
1405 cvi->blue.x, cvi->blue.y, cvi->maxLuminance, cvi->minLuminance);
1415 QString title = info->
aboutInfo(
"title");
1417 fillText(texts + nbtexts,
"Title", title);
1420 QString abstract = info->
aboutInfo(
"subject");
1421 if (abstract.isEmpty()) {
1425 QString keywords = info->
aboutInfo(
"keyword");
1426 if (!keywords.isEmpty()) {
1427 abstract = abstract +
" keywords: " + keywords;
1429 fillText(texts + nbtexts,
"Description", abstract);
1433 QString license = info->
aboutInfo(
"license");
1435 fillText(texts + nbtexts,
"Copyright", license);
1439 QString author = info->
authorInfo(
"creator");
1443 if (!contact.isEmpty()) {
1444 author = author+
"("+contact+
")";
1447 fillText(texts + nbtexts,
"Author", author);
1451 png_set_text(png_ptr, info_ptr, texts, nbtexts);
1457 if (metaData && !metaData->
empty()) {
1459 dbgFile <<
"Trying to save exif information";
1466 writeRawProfile(png_ptr, info_ptr,
"exif", buffer.data());
1470 dbgFile <<
"Trying to save iptc information";
1477 dbgFile <<
"IPTC information size is" << buffer.data().size();
1478 writeRawProfile(png_ptr, info_ptr,
"iptc", buffer.data());
1482 dbgFile <<
"Trying to save XMP information";
1489 dbgFile <<
"XMP information size is" << buffer.data().size();
1490 writeRawProfile(png_ptr, info_ptr,
"xmp", buffer.data());
1496 png_uint_32 x_resolution, y_resolution;
1498 png_set_pHYs(png_ptr, info_ptr,
CM_TO_POINT(xRes) * 100.0,
CM_TO_POINT(yRes) * 100.0, PNG_RESOLUTION_METER);
1501 png_write_info(png_ptr, info_ptr);
1502 png_write_flush(png_ptr);
1505#ifndef WORDS_BIGENDIAN
1506 if (color_nb_bits > 8)
1507 png_set_swap(png_ptr);
1513 struct RowPointersStruct {
1514 RowPointersStruct(
const QSize &size,
int pixelSize)
1515 : numRows(size.height())
1517 rows =
new png_byte*[numRows];
1519 for (
int i = 0; i < numRows; i++) {
1520 rows[i] =
new png_byte[size.width() * pixelSize];
1524 ~RowPointersStruct() {
1525 for (
int i = 0; i < numRows; i++) {
1531 const int numRows = 0;
1532 png_byte** rows = 0;
1537 RowPointersStruct rowPointers(imageRect.size(), device->
pixelSize());
1540 for (
int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++, row++) {
1543 switch (color_type) {
1544 case PNG_COLOR_TYPE_GRAY:
1545 case PNG_COLOR_TYPE_GRAY_ALPHA:
1546 if (color_nb_bits == 16) {
1547 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1549 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1551 if (options.
alpha) *(dst++) = d[1];
1554 quint8 *dst = rowPointers.rows[row];
1558 if (options.
alpha) *(dst++) = d[1];
1562 case PNG_COLOR_TYPE_RGB:
1563 case PNG_COLOR_TYPE_RGB_ALPHA:
1564 if (isFloatingPoint) {
1565 if (color_nb_bits == 16) {
1568 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1570 const float *d =
reinterpret_cast<const float *
>(it->
oldRawData());
1580 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1582 const half *d =
reinterpret_cast<const half *
>(it->
oldRawData());
1594 quint8 *dst =
reinterpret_cast<quint8 *
>(rowPointers.rows[row]);
1596 const float *d =
reinterpret_cast<const float *
>(it->
oldRawData());
1606 quint8 *dst =
reinterpret_cast<quint8 *
>(rowPointers.rows[row]);
1608 const half *d =
reinterpret_cast<const half *
>(it->
oldRawData());
1618 if (color_nb_bits == 16) {
1619 quint16 *dst =
reinterpret_cast<quint16 *
>(rowPointers.rows[row]);
1621 const quint16 *d =
reinterpret_cast<const quint16 *
>(it->
oldRawData());
1625 if (options.
alpha) *(dst++) = d[3];
1628 quint8 *dst = rowPointers.rows[row];
1634 if (options.
alpha) *(dst++) = d[3];
1639 case PNG_COLOR_TYPE_PALETTE: {
1640 quint8 *dst = rowPointers.rows[row];
1645 for (i = 0; i < num_palette; i++) {
1646 if (palette[i].red == d[2] &&
1647 palette[i].green == d[1] &&
1648 palette[i].blue == d[0]) {
1661 png_write_image(png_ptr, rowPointers.rows);
1664 png_write_end(png_ptr, info_ptr);
1667 png_destroy_write_struct(&png_ptr, &info_ptr);
1679 if (png_ptr == 0 || row_number >
PNG_MAX_UINT || pass > 7)
return;
1685 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)