Changeset View
Changeset View
Standalone View
Standalone View
libs/ui/kis_png_converter.cpp
Context not available. | |||||
70 | namespace | 70 | namespace | ||
---|---|---|---|---|---|
71 | { | 71 | { | ||
72 | 72 | | |||
73 | | ||||
74 | int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) | 73 | int getColorTypeforColorSpace(const KoColorSpace * cs , bool alpha) | ||
75 | { | 74 | { | ||
76 | 75 | | |||
Context not available. | |||||
409 | Q_UNUSED(png_ptr); | 408 | Q_UNUSED(png_ptr); | ||
410 | } | 409 | } | ||
411 | 410 | | |||
412 | | ||||
413 | KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod) | 411 | KisImageBuilder_Result KisPNGConverter::buildImage(QIODevice* iod) | ||
414 | { | 412 | { | ||
415 | dbgFile << "Start decoding PNG File"; | 413 | dbgFile << "Start decoding PNG File"; | ||
Context not available. | |||||
551 | } | 549 | } | ||
552 | } | 550 | } | ||
553 | 551 | | |||
552 | bool loadedImageIsHDR = false; | ||||
554 | const KoColorProfile* profile = 0; | 553 | const KoColorProfile* profile = 0; | ||
555 | if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { | 554 | if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) { | ||
556 | QByteArray profile_rawdata; | 555 | QByteArray profile_rawdata; | ||
Context not available. | |||||
565 | 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 | 564 | 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 | ||
566 | } | 565 | } | ||
567 | } | 566 | } | ||
567 | | ||||
568 | loadedImageIsHDR = strcmp(profile_name, "ITUR_2100_PQ_FULL") == 0; | ||||
568 | } | 569 | } | ||
569 | else { | 570 | else { | ||
570 | dbgFile << "no embedded profile, will use the default profile"; | 571 | dbgFile << "no embedded profile, will use the default profile"; | ||
Context not available. | |||||
595 | } | 596 | } | ||
596 | 597 | | |||
597 | // Retrieve a pointer to the colorspace | 598 | // Retrieve a pointer to the colorspace | ||
598 | const KoColorSpace* cs; | 599 | KoColorConversionTransformation* transform = 0; | ||
599 | if (profile && profile->isSuitableForOutput()) { | 600 | const KoColorSpace* cs = 0; | ||
601 | | ||||
602 | if (loadedImageIsHDR && | ||||
603 | csName.first == RGBAColorModelID.id() && | ||||
604 | csName.second == Integer16BitsColorDepthID.id()) { | ||||
605 | | ||||
606 | const KoColorSpace *p2020PQCS = | ||||
607 | KoColorSpaceRegistry::instance()->colorSpace( | ||||
608 | RGBAColorModelID.id(), | ||||
609 | Integer16BitsColorDepthID.id(), | ||||
610 | KoColorSpaceRegistry::instance()->p2020PQProfile()); | ||||
611 | | ||||
612 | cs = p2020PQCS; | ||||
613 | | ||||
614 | } else if (profile && profile->isSuitableForOutput()) { | ||||
600 | dbgFile << "image has embedded profile: " << profile->name() << "\n"; | 615 | dbgFile << "image has embedded profile: " << profile->name() << "\n"; | ||
601 | cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); | 616 | cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile); | ||
602 | } | 617 | } | ||
Context not available. | |||||
608 | } else { | 623 | } else { | ||
609 | cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); | 624 | cs = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, 0); | ||
610 | } | 625 | } | ||
626 | | ||||
627 | //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 | ||||
628 | // Create the cmsTransform if needed | ||||
629 | if (profile) { | ||||
630 | transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); | ||||
631 | } | ||||
611 | } | 632 | } | ||
612 | 633 | | |||
613 | if (cs == 0) { | 634 | if (cs == 0) { | ||
614 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | 635 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); | ||
615 | return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; | 636 | return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; | ||
616 | } | 637 | } | ||
617 | //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 | | |||
618 | // Create the cmsTransform if needed | | |||
619 | KoColorTransformation* transform = 0; | | |||
620 | if (profile && !profile->isSuitableForOutput()) { | | |||
621 | transform = KoColorSpaceRegistry::instance()->colorSpace(csName.first, csName.second, profile)->createColorConverter(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); | | |||
622 | } | | |||
623 | 638 | | |||
624 | // Creating the KisImageSP | 639 | // Creating the KisImageSP | ||
625 | if (m_image == 0) { | 640 | if (m_image == 0) { | ||
Context not available. | |||||
719 | do { | 734 | do { | ||
720 | quint16 *d = reinterpret_cast<quint16 *>(it->rawData()); | 735 | quint16 *d = reinterpret_cast<quint16 *>(it->rawData()); | ||
721 | d[0] = *(src++); | 736 | d[0] = *(src++); | ||
722 | if (transform) transform->transform(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1); | | |||
723 | if (hasalpha) { | 737 | if (hasalpha) { | ||
724 | d[1] = *(src++); | 738 | d[1] = *(src++); | ||
725 | } else { | 739 | } else { | ||
726 | d[1] = quint16_MAX; | 740 | d[1] = quint16_MAX; | ||
727 | } | 741 | } | ||
742 | if (transform) transform->transformInPlace(reinterpret_cast<quint8*>(d), reinterpret_cast<quint8*>(d), 1); | ||||
728 | } while (it->nextPixel()); | 743 | } while (it->nextPixel()); | ||
729 | } else { | 744 | } else { | ||
730 | KisPNGReadStream stream(row_pointer, color_nb_bits); | 745 | KisPNGReadStream stream(row_pointer, color_nb_bits); | ||
731 | do { | 746 | do { | ||
732 | quint8 *d = it->rawData(); | 747 | quint8 *d = it->rawData(); | ||
733 | d[0] = (quint8)(stream.nextValue() * coeff); | 748 | d[0] = (quint8)(stream.nextValue() * coeff); | ||
734 | if (transform) transform->transform(d, d, 1); | | |||
735 | if (hasalpha) { | 749 | if (hasalpha) { | ||
736 | d[1] = (quint8)(stream.nextValue() * coeff); | 750 | d[1] = (quint8)(stream.nextValue() * coeff); | ||
737 | } else { | 751 | } else { | ||
738 | d[1] = UCHAR_MAX; | 752 | d[1] = UCHAR_MAX; | ||
739 | } | 753 | } | ||
754 | if (transform) transform->transformInPlace(d, d, 1); | ||||
740 | } while (it->nextPixel()); | 755 | } while (it->nextPixel()); | ||
741 | } | 756 | } | ||
742 | // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" | 757 | // FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits" | ||
Context not available. | |||||
750 | d[2] = *(src++); | 765 | d[2] = *(src++); | ||
751 | d[1] = *(src++); | 766 | d[1] = *(src++); | ||
752 | d[0] = *(src++); | 767 | d[0] = *(src++); | ||
753 | if (transform) transform->transform(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1); | | |||
754 | if (hasalpha) d[3] = *(src++); | 768 | if (hasalpha) d[3] = *(src++); | ||
755 | else d[3] = quint16_MAX; | 769 | else d[3] = quint16_MAX; | ||
770 | if (transform) transform->transformInPlace(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8*>(d), 1); | ||||
756 | } while (it->nextPixel()); | 771 | } while (it->nextPixel()); | ||
757 | } else { | 772 | } else { | ||
758 | KisPNGReadStream stream(row_pointer, color_nb_bits); | 773 | KisPNGReadStream stream(row_pointer, color_nb_bits); | ||
Context not available. | |||||
761 | d[2] = (quint8)(stream.nextValue() * coeff); | 776 | d[2] = (quint8)(stream.nextValue() * coeff); | ||
762 | d[1] = (quint8)(stream.nextValue() * coeff); | 777 | d[1] = (quint8)(stream.nextValue() * coeff); | ||
763 | d[0] = (quint8)(stream.nextValue() * coeff); | 778 | d[0] = (quint8)(stream.nextValue() * coeff); | ||
764 | if (transform) transform->transform(d, d, 1); | | |||
765 | if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); | 779 | if (hasalpha) d[3] = (quint8)(stream.nextValue() * coeff); | ||
766 | else d[3] = UCHAR_MAX; | 780 | else d[3] = UCHAR_MAX; | ||
781 | if (transform) transform->transformInPlace(d, d, 1); | ||||
767 | } while (it->nextPixel()); | 782 | } while (it->nextPixel()); | ||
768 | } | 783 | } | ||
769 | break; | 784 | break; | ||
Context not available. | |||||
903 | if (device->colorSpace()->colorDepthId() == Float16BitsColorDepthID | 918 | if (device->colorSpace()->colorDepthId() == Float16BitsColorDepthID | ||
904 | || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID | 919 | || device->colorSpace()->colorDepthId() == Float32BitsColorDepthID | ||
905 | || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID) { | 920 | || device->colorSpace()->colorDepthId() == Float64BitsColorDepthID) { | ||
906 | const KoColorSpace *dstcs = KoColorSpaceRegistry::instance()->colorSpace(device->colorSpace()->colorModelId().id(), Integer16BitsColorDepthID.id(), device->colorSpace()->profile()); | 921 | | ||
907 | KisPaintDeviceSP tmp = new KisPaintDevice(dstcs); | 922 | const KoColorSpace *dstCS = | ||
908 | KisPainter gc(tmp); | 923 | KoColorSpaceRegistry::instance()->colorSpace( | ||
909 | gc.bitBlt(imageRect.topLeft(), device, imageRect); | 924 | device->colorSpace()->colorModelId().id(), | ||
910 | gc.end(); | 925 | Integer16BitsColorDepthID.id(), | ||
926 | device->colorSpace()->profile()); | ||||
927 | | ||||
928 | if (options.saveAsHDR) { | ||||
929 | dstCS = | ||||
930 | KoColorSpaceRegistry::instance()->colorSpace( | ||||
931 | RGBAColorModelID.id(), | ||||
932 | Integer16BitsColorDepthID.id(), | ||||
933 | KoColorSpaceRegistry::instance()->p2020PQProfile()); | ||||
934 | } | ||||
935 | | ||||
936 | KisPaintDeviceSP tmp = new KisPaintDevice(device->colorSpace()); | ||||
937 | tmp->makeCloneFromRough(device, imageRect); | ||||
938 | delete tmp->convertTo(dstCS); | ||||
939 | | ||||
911 | device = tmp; | 940 | device = tmp; | ||
941 | | ||||
942 | } else { | ||||
943 | KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || | ||||
944 | (device->colorSpace()->profile() && | ||||
945 | device->colorSpace()->profile()->uniqueId() == | ||||
946 | KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId())) { | ||||
947 | options.saveAsHDR = false; | ||||
948 | } | ||||
949 | } | ||||
950 | | ||||
951 | | ||||
952 | KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.forceSRGB) { | ||||
953 | options.forceSRGB = false; | ||||
954 | } | ||||
955 | | ||||
956 | KIS_SAFE_ASSERT_RECOVER(!options.saveAsHDR || !options.tryToSaveAsIndexed) { | ||||
957 | options.tryToSaveAsIndexed = false; | ||||
912 | } | 958 | } | ||
959 | | ||||
913 | QStringList colormodels = QStringList() << RGBAColorModelID.id() << GrayAColorModelID.id(); | 960 | QStringList colormodels = QStringList() << RGBAColorModelID.id() << GrayAColorModelID.id(); | ||
914 | if (options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) { | 961 | if (options.forceSRGB || !colormodels.contains(device->colorSpace()->colorModelId().id())) { | ||
915 | const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); | 962 | const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), device->colorSpace()->colorDepthId().id(), "sRGB built-in - (lcms internal)"); | ||
Context not available. | |||||
1018 | 1065 | | |||
1019 | int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; | 1066 | int interlacetype = options.interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; | ||
1020 | 1067 | | |||
1068 | KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(color_type >= 0, KisImageBuilder_RESULT_FAILURE); | ||||
1069 | | ||||
1021 | png_set_IHDR(png_ptr, info_ptr, | 1070 | png_set_IHDR(png_ptr, info_ptr, | ||
1022 | imageRect.width(), | 1071 | imageRect.width(), | ||
1023 | imageRect.height(), | 1072 | imageRect.height(), | ||
Context not available. | |||||
1038 | }*/ | 1087 | }*/ | ||
1039 | 1088 | | |||
1040 | 1089 | | |||
1090 | /** TODO: Firefox still opens the image incorrectly if there is gAMA+cHRM tags | ||||
1091 | * present. According to the standard it should use iCCP tag with higher priority, | ||||
1092 | * but it doesn't: | ||||
1093 | * | ||||
1094 | * "When the iCCP chunk is present, PNG decoders that recognize it and are capable | ||||
1095 | * of colour management [ICC] shall ignore the gAMA and cHRM chunks and use | ||||
1096 | * the iCCP chunk instead and interpret it according to [ICC-1] and [ICC-1A]" | ||||
1097 | */ | ||||
1098 | | ||||
1099 | #if 0 | ||||
1100 | if (options.saveAsHDR) { | ||||
1101 | // https://www.w3.org/TR/PNG/#11gAMA | ||||
1102 | #if defined(PNG_GAMMA_SUPPORTED) | ||||
1103 | // the values are set in accurdance of HDR-PNG standard: | ||||
1104 | // https://www.w3.org/TR/png-hdr-pq/ | ||||
1105 | | ||||
1106 | png_set_gAMA_fixed(png_ptr, info_ptr, 15000); | ||||
1107 | dbgFile << "gAMA" << "(Rec 2100)"; | ||||
1108 | #endif | ||||
1109 | | ||||
1110 | #if defined PNG_cHRM_SUPPORTED | ||||
1111 | png_set_cHRM_fixed(png_ptr, info_ptr, | ||||
1112 | 31270, 32900, // white point | ||||
1113 | 70800, 29200, // red | ||||
1114 | 17000, 79700, // green | ||||
1115 | 13100, 4600 // blue | ||||
1116 | ); | ||||
1117 | dbgFile << "cHRM" << "(Rec 2100)"; | ||||
1118 | #endif | ||||
1119 | } | ||||
1120 | #endif | ||||
1121 | | ||||
1122 | | ||||
1041 | // we should ensure we don't access non-existing palette object | 1123 | // we should ensure we don't access non-existing palette object | ||
1042 | KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, KisImageBuilder_RESULT_FAILURE); | 1124 | KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(palette || color_type != PNG_COLOR_TYPE_PALETTE, KisImageBuilder_RESULT_FAILURE); | ||
1043 | 1125 | | |||
Context not available. | |||||
1082 | if (!sRGB || options.saveSRGBProfile) { | 1164 | if (!sRGB || options.saveSRGBProfile) { | ||
1083 | 1165 | | |||
1084 | #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 | 1166 | #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 5 | ||
1085 | png_set_iCCP(png_ptr, info_ptr, (png_const_charp)"icc", PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); | 1167 | const char *typeString = !options.saveAsHDR ? "icc" : "ITUR_2100_PQ_FULL"; | ||
1168 | png_set_iCCP(png_ptr, info_ptr, (png_const_charp)typeString, PNG_COMPRESSION_TYPE_BASE, (png_const_bytep)colorProfileData.constData(), colorProfileData . size()); | ||||
1086 | #else | 1169 | #else | ||
1087 | // older version of libpng has a problem with constness on the parameters | 1170 | // older version of libpng has a problem with constness on the parameters | ||
1088 | char typeString[] = "icc"; | 1171 | char typeStringICC[] = "icc"; | ||
1172 | char typeStringHDR[] = "ITUR_2100_PQ_FULL"; | ||||
1173 | char *typeString = !options.saveAsHDR ? typeStringICC : typeStringHDR; | ||||
1089 | png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size()); | 1174 | png_set_iCCP(png_ptr, info_ptr, typeString, PNG_COMPRESSION_TYPE_BASE, colorProfileData.data(), colorProfileData . size()); | ||
1090 | #endif | 1175 | #endif | ||
1091 | } | 1176 | } | ||
Context not available. |