EXR semi-transparent images are expected to be rendered on black to ensure correctness of the light model
NOTE: We cannot do that automatically, because the EXR may be imported into the image as a layer, in which case the default color will create major issues. See https://bugs.kde.org/show_bug.cgi?id=427720
568{
569 try {
570 Imf::InputFile
file(filename.toUtf8());
571
572 Imath::Box2i dw =
file.header().dataWindow();
573 Imath::Box2i displayWindow =
file.header().displayWindow();
574
575 int width = dw.max.x - dw.min.x + 1;
576 int height = dw.max.y - dw.min.y + 1;
577 int dx = dw.min.x;
578 int dy = dw.min.y;
579
580
581 for (Imf::Header::ConstIterator it =
file.header().begin();
582 it !=
file.header().end(); ++it) {
583 dbgFile <<
"Attribute: " << it.name() <<
" type: " << it.attribute().typeName();
584 }
585
586
587 QDomDocument extraLayersInfo =
d->loadExtraLayersInfo(
file.header());
588
589
590
593
595
596 const Imf::ChannelList &channels =
file.header().channels();
597 std::set<std::string> layerNames;
598 channels.layers(layerNames);
599
600 if (!extraLayersInfo.isNull() &&
601 !
d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
602
603
604 extraLayersInfo = QDomDocument();
605 }
606
607
608
609 dbgFile <<
"Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:";
611 bool topLevelRGBFound = false;
613
615 << "R"
616 << "G"
617 << "B"
618 << ".A"
619 << ".R"
620 << ".G"
621 << ".B"
622 << "A."
623 << "R."
624 << "G."
625 << "B."
626 << "A."
627 << "R."
628 << "G."
629 << "B."
630 << ".alpha"
631 << ".red"
632 << ".green"
633 << ".blue"
634 << "X"
635 << "Y"
636 << "Z"
637 << ".X"
638 << ".Y"
639 << ".Z"
640 << "X."
641 << "Y."
642 << "Z.";
643
644 for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
645 const Imf::Channel &channel = i.channel();
646 dbgFile <<
"Channel name = " << i.name() <<
" type = " << channel.type;
647
648 QString qname = i.name();
649 if (topLevelChannelNames.contains(qname)) {
650 topLevelRGBFound = true;
651 dbgFile <<
"Found top-level channel" << qname;
654 }
655
656
657
658 else if (!qname.contains('.')
659 || !qname.mid(1).contains('.')
660 || !qname.left(qname.size() - 1).contains('.')) {
661 warnFile <<
"Found a top-level channel that is not part of the rendered image" << qname <<
". Krita will not load this channel.";
662 }
663 }
664 if (topLevelRGBFound) {
665 dbgFile <<
"Toplevel layer" << info.
name <<
":Image type:" << imageType <<
"Layer type" << info.
imageType;
666 informationObjects.push_back(info);
668 }
669
670 dbgFile <<
"Extra layers:" << layerNames.size();
671
672 for (std::set<std::string>::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) {
673
675
676 dbgFile <<
"layer name = " << i->c_str();
677 info.
name = i->c_str();
678 Imf::ChannelList::ConstIterator layerBegin, layerEnd;
679 channels.channelsInLayer(*i, layerBegin, layerEnd);
680 for (Imf::ChannelList::ConstIterator j = layerBegin;
681 j != layerEnd; ++j) {
682 const Imf::Channel &channel = j.channel();
683
685
686 QString qname = j.name();
688 QString layersuffix = list.last();
689
690 dbgFile <<
"\tchannel " << j.name() <<
"suffix" << layersuffix <<
" type = " << channel.type;
691
692
693
694 if (topLevelChannelNames.contains("." + layersuffix)) {
695 layersuffix = layersuffix.at(0).toUpper();
696 }
697 dbgFile <<
"\t\tsuffix" << layersuffix;
698
699
700 if (list.size() > 1) {
701 info.
name = list[list.size()-2];
703 }
704
706 }
707
709 informationObjects.push_back(info);
712 }
713 }
714 }
715
716 dbgFile <<
"File has" << informationObjects.size() <<
"layer(s)";
717
718
719 for (int i = 0; i < informationObjects.size(); ++i) {
721 QString modelId;
722
726 if (key != "Y") {
728 QString channel = info.
channelMap.begin().value();
731 }
732 }
735
736 QMap<QString,QString>::const_iterator it = info.
channelMap.constBegin();
737 QMap<QString,QString>::const_iterator end = info.
channelMap.constEnd();
738
739 QString failingChannelKey;
740
741 for (; it != end; ++it) {
742
743 if (it.key() != "A") {
744 failingChannelKey = it.key();
745 break;
746 }
747 }
748
750
751 QString failingChannelValue = info.
channelMap[failingChannelKey];
754 }
756
759 }
762 QMap<QString, QString> newChannelMap;
771 }
772
777 }
778 else {
780 QMap<QString, QString> newChannelMap;
781 QMap<QString, QString>::iterator it = info.
channelMap.begin();
782 newChannelMap["R"] = it.value();
784 ++it;
785 newChannelMap["G"] = it.value();
787 ++it;
788 newChannelMap["B"] = it.value();
791 ++it;
792 newChannelMap["A"] = it.value();
794 }
795
797 }
798 }
799 else {
800 dbgFile << info.
name <<
"has" << info.
channelMap.size() <<
"channels, and we don't know what to do.";
801 }
802 if (!modelId.isEmpty()) {
804 }
805 }
806
807
808 dbgFile <<
"Image type = " << imageType;
810
813
814
815 for (int i = 0; i < groups.size(); ++i) {
818 }
819
820
821
822
823 int displayWidth = displayWindow.max.x - displayWindow.min.x + 1;
824 int displayHeight = displayWindow.max.y - displayWindow.min.y + 1;
825 d->image =
new KisImage(
d->doc->createUndoStore(), displayWidth, displayHeight, colorSpace,
"");
826
829 }
830
839
840
841
842 for (int i = 0; i < groups.size(); ++i) {
847 d->image->addNode(info.
groupLayer, groupLayerParent);
848 }
849
850
851 for (int i = informationObjects.size() - 1; i >= 0; --i) {
856
857 if (!layer) {
859 }
860
862
864 case 1:
865 case 2:
866
869 d->decodeData1<half>(
file, info, layer, width, dx, dy, height, Imf::HALF);
870 break;
872 d->decodeData1<
float>(
file, info, layer, width, dx, dy, height, Imf::FLOAT);
873 break;
876 qFatal("Impossible error");
877 }
878 break;
879 case 3:
880 case 4:
881
884 d->decodeData4<half>(
file, info, layer, width, dx, dy, height, Imf::HALF);
885 break;
887 d->decodeData4<
float>(
file, info, layer, width, dx, dy, height, Imf::FLOAT);
888 break;
891 qFatal("Impossible error");
892 }
893 break;
894 default:
895 qFatal(
"Invalid number of channels: %i", info.
channelMap.size());
896 }
897
901 QMap<QString, KisMetaData::Value> map;
904 values.append(map);
905 }
907 }
908
910 d->image->
addNode(layer, groupLayerParent);
911 } else {
912 dbgFile <<
"No decoding " << info.
name <<
" with " << info.
channelMap.size() <<
" channels, and lack of a color space";
913 }
914 }
915
916
917 if (
d->alphaWasModified) {
918 QString msg =
919 i18nc("@info",
920 "The image contains pixels with zero alpha channel and non-zero "
921 "color channels. Krita has modified those pixels to have "
922 "at least some alpha. The initial values will <i>not</i> "
923 "be reverted on saving the image back."
924 "<br/><br/>"
925 "This will hardly make any visual difference just keep it in mind.");
926 if (
d->showNotifications) {
927 QMessageBox::information(qApp->activeWindow(), i18nc("@title:window", "EXR image has been modified"), msg);
928 } else {
930 }
931 }
932
933 if (!extraLayersInfo.isNull()) {
935 }
936
938
939 } catch (std::exception &e) {
940 dbgFile <<
"Error while reading from the exr file: " << e.what();
941
946 } else {
948 }
949 }
950
952}
QList< QString > QStringList
const QString COMPOSITE_OVER
static bool doesFileExist(QString filepath)
static bool isFileReadable(QString filepath)
QString remap(const QMap< QString, QString > ¤t2original, const QString ¤t)
const KoColorSpace * kisTypeToColorSpace(QString colorModelID, ImageType imageType)
ExrGroupLayerInfo * searchGroup(QList< ExrGroupLayerInfo > *groups, QStringList list, int idx1, int idx2)
ImageType imfTypeToKisType(Imf::PixelType type)
QPainterPath create(const char32_t codepoint, double height)
Creates a tofu missing glyph indicator representing the provided Unicode codepoint.
KisGroupLayerSP groupLayer
const KoColorSpace * colorSpace
const ExrGroupLayerInfo * parent
QMap< QString, QString > channelMap
first is either R, G, B or A second is the EXR channel name
void updateImageType(ImageType channelType)
QList< Remap > remappedChannels
this is used to store in the metadata the mapping between exr channel name, and channels used in Krit...
void setCompositeOpId(const QString &compositeOpId)
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)