[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [calligra/calligra/2.9] krita/plugins/formats/exr: BUG:352734 Support top-level RGB channels
From:       Boudewijn Rempt <boud () valdyas ! org>
Date:       2015-09-21 10:03:13
Message-ID: E1ZdxwH-0008US-KH () scm ! kde ! org
[Download RAW message or body]

Git commit 337183f80217471dd2d6056d4597e57c4bc1f956 by Boudewijn Rempt.
Committed on 21/09/2015 at 10:00.
Pushed by rempt into branch 'calligra/2.9'.

BUG:352734 Support top-level RGB channels

Some OpenEXR images have both layers and top-level channels. If
there are top-level channels named R, G, B, A, we should read them
and construct a synthetic layer out of them.

M  +115  -100  krita/plugins/formats/exr/exr_converter.cc
M  +2    -0    krita/plugins/formats/exr/exr_import.cc

http://commits.kde.org/calligra/337183f80217471dd2d6056d4597e57c4bc1f956

diff --git a/krita/plugins/formats/exr/exr_converter.cc \
b/krita/plugins/formats/exr/exr_converter.cc index 0874e65..a38e7b7 100644
--- a/krita/plugins/formats/exr/exr_converter.cc
+++ b/krita/plugins/formats/exr/exr_converter.cc
@@ -89,16 +89,21 @@ enum ImageType {
 };
 
 struct ExrPaintLayerInfo : public ExrLayerInfoBase {
-    ExrPaintLayerInfo() : imageType(IT_UNKNOWN) {
+    ExrPaintLayerInfo()
+        : imageType(IT_UNKNOWN)
+    {
     }
+
     ImageType imageType;
     QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR \
channel name +
     struct Remap {
         Remap(const QString& _original, const QString& _current) : original(_original), \
current(_current) {  }
         QString original;
         QString current;
     };
+
     QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping \
between exr channel name, and channels used in Krita  void updateImageType(ImageType \
channelType);  };
@@ -107,7 +112,8 @@ void ExrPaintLayerInfo::updateImageType(ImageType channelType)
 {
     if (imageType == IT_UNKNOWN) {
         imageType = channelType;
-    } else if (imageType != channelType) {
+    }
+    else if (imageType != channelType) {
         imageType = IT_UNSUPPORTED;
     }
 }
@@ -121,7 +127,7 @@ struct ExrPaintLayerSaveInfo {
 
 struct exrConverter::Private {
     Private() : doc(0), warnedAboutChangedAlpha(false),
-                showNotifications(false) {}
+        showNotifications(false) {}
 
     KisImageSP image;
     KisDocument *doc;
@@ -226,9 +232,9 @@ struct RgbPixelWrapper
         const T alpha = pixel.a;
 
         return alpha >= alphaNoiseThreshold<T>() ||
-            (pixel.r * alpha == mult.r &&
-             pixel.g * alpha == mult.g &&
-             pixel.b * alpha == mult.b);
+                (pixel.r * alpha == mult.r &&
+                 pixel.g * alpha == mult.g &&
+                 pixel.b * alpha == mult.b);
     }
 
     inline void setUnmultiplied(const Rgba<T> &mult, qreal newAlpha) {
@@ -262,7 +268,7 @@ struct GrayPixelWrapper
         const T alpha = pixel.alpha;
 
         return alpha >= alphaNoiseThreshold<T>() ||
-            pixel.gray * alpha == mult.gray;
+                pixel.gray * alpha == mult.gray;
     }
 
     inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) {
@@ -307,20 +313,20 @@ void exrConverter::Private::unmultiplyAlpha(typename \
                WrapperType::pixel_type *pi
         *pixel = dstPixel.pixel;
 
         if (alphaWasModified &&
-            !this->warnedAboutChangedAlpha) {
+                !this->warnedAboutChangedAlpha) {
 
             QString msg =
-                i18nc("@info",
-                      "The image contains pixels with zero alpha channel and non-zero "
-                      "color channels. Krita will have to modify those pixels to have "
-                      "at least some alpha. The initial values will <emphasis>not</emphasis> "
-                      "be reverted on saving the image back."
-                      "<nl/><nl/>"
-                      "This will hardly make any visual difference just keep it in mind."
-                      "<nl/><nl/>"
-                      "<note>Modified alpha will have a range from <numid>%1</numid> to \
                <numid>%2</numid></note>",
-                      alphaEpsilon<channel_type>(),
-                      alphaNoiseThreshold<channel_type>());
+                    i18nc("@info",
+                          "The image contains pixels with zero alpha channel and non-zero "
+                          "color channels. Krita will have to modify those pixels to have "
+                          "at least some alpha. The initial values will \
<emphasis>not</emphasis> " +                          "be reverted on saving the image back."
+                          "<nl/><nl/>"
+                          "This will hardly make any visual difference just keep it in mind."
+                          "<nl/><nl/>"
+                          "<note>Modified alpha will have a range from <numid>%1</numid> to \
<numid>%2</numid></note>", +                          alphaEpsilon<channel_type>(),
+                          alphaNoiseThreshold<channel_type>());
 
             if (this->showNotifications) {
                 QMessageBox::information(0, i18nc("@title:window", "EXR image will be \
modified"), msg); @@ -365,22 +371,22 @@ void exrConverter::Private::decodeData4(Imf::InputFile& \
file, ExrPaintLayerInfo&  Imf::FrameBuffer frameBuffer;
         Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
         frameBuffer.insert(info.channelMap["R"].toLatin1().constData(),
-                           Imf::Slice(ptype, (char *) &frameBufferData->r,
-                                      sizeof(Rgba) * 1,
-                                      sizeof(Rgba) * width));
+                Imf::Slice(ptype, (char *) &frameBufferData->r,
+                           sizeof(Rgba) * 1,
+                           sizeof(Rgba) * width));
         frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
-                           Imf::Slice(ptype, (char *) &frameBufferData->g,
-                                      sizeof(Rgba) * 1,
-                                      sizeof(Rgba) * width));
+                Imf::Slice(ptype, (char *) &frameBufferData->g,
+                           sizeof(Rgba) * 1,
+                           sizeof(Rgba) * width));
         frameBuffer.insert(info.channelMap["B"].toLatin1().constData(),
-                           Imf::Slice(ptype, (char *) &frameBufferData->b,
-                                      sizeof(Rgba) * 1,
-                                      sizeof(Rgba) * width));
+                Imf::Slice(ptype, (char *) &frameBufferData->b,
+                           sizeof(Rgba) * 1,
+                           sizeof(Rgba) * width));
         if (hasAlpha) {
             frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
-                               Imf::Slice(ptype, (char *) &frameBufferData->a,
-                                          sizeof(Rgba) * 1,
-                                          sizeof(Rgba) * width));
+                    Imf::Slice(ptype, (char *) &frameBufferData->a,
+                               sizeof(Rgba) * 1,
+                               sizeof(Rgba) * width));
         }
 
         file.setFrameBuffer(frameBuffer);
@@ -418,7 +424,7 @@ void exrConverter::Private::decodeData1(Imf::InputFile& file, \
ExrPaintLayerInfo&  typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
 
     KIS_ASSERT_RECOVER_RETURN(
-        layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
+                layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
 
     QVector<pixel_type> pixels(width);
 
@@ -433,15 +439,15 @@ void exrConverter::Private::decodeData1(Imf::InputFile& file, \
ExrPaintLayerInfo&  Imf::FrameBuffer frameBuffer;
         pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
         frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
-                           Imf::Slice(ptype, (char *) &frameBufferData->gray,
-                                      sizeof(pixel_type) * 1,
-                                      sizeof(pixel_type) * width));
+                Imf::Slice(ptype, (char *) &frameBufferData->gray,
+                           sizeof(pixel_type) * 1,
+                           sizeof(pixel_type) * width));
 
         if (hasAlpha) {
             frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
-                               Imf::Slice(ptype, (char *) &frameBufferData->alpha,
-                                          sizeof(pixel_type) * 1,
-                                          sizeof(pixel_type) * width));
+                    Imf::Slice(ptype, (char *) &frameBufferData->alpha,
+                               sizeof(pixel_type) * 1,
+                               sizeof(pixel_type) * width));
         }
 
         file.setFrameBuffer(frameBuffer);
@@ -497,7 +503,7 @@ ExrGroupLayerInfo* searchGroup(QList<ExrGroupLayerInfo>* groups, \
QStringList lis  QDomDocument exrConverter::Private::loadExtraLayersInfo(const Imf::Header \
&header)  {
     const Imf::StringAttribute *layersInfoAttribute =
-        header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
+            header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
 
     if (!layersInfoAttribute) return QDomDocument();
 
@@ -525,7 +531,7 @@ bool exrConverter::Private::checkExtraLayersInfoConsistent(const \
QDomDocument &d  el = el.nextSiblingElement();
     }
 
-    bool result = extraInfoLayers == exrLayerNames;
+    bool result = (extraInfoLayers == exrLayerNames);
 
     if (!result) {
         qDebug() << "WARINING: Krita EXR extra layers info is inconsistent!";
@@ -549,6 +555,7 @@ bool exrConverter::Private::checkExtraLayersInfoConsistent(const \
QDomDocument &d  KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
 {
     dbgFile << "Load exr: " << uri << " " << QFile::encodeName(uri.toLocalFile());
+
     Imf::InputFile file(QFile::encodeName(uri.toLocalFile()));
 
     Imath::Box2i dw = file.header().dataWindow();
@@ -559,7 +566,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
 
     // Display the attributes of a file
     for (Imf::Header::ConstIterator it = file.header().begin();
-            it != file.header().end(); ++it) {
+         it != file.header().end(); ++it) {
         dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName();
     }
 
@@ -578,70 +585,78 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
     channels.layers(layerNames);
 
     if (!extraLayersInfo.isNull() &&
-        !m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
+            !m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
 
         // it is inconsistent anyway
         extraLayersInfo = QDomDocument();
     }
 
-    // Test if it is a multilayer EXR or singlelayer
-    if (layerNames.empty()) {
-        dbgFile << "Single layer:";
-        ExrPaintLayerInfo info;
-        info.name = i18n("HDR Layer");
-        for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
-            const Imf::Channel &channel = i.channel();
-            dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
+    // Check if there are A, R, G, B channels
 
-            info.updateImageType(imfTypeToKisType(channel.type));
+    dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer \
images:"; +    ExrPaintLayerInfo info;
+    bool topLevelRGBFound = false;
+    info.name = i18n("HDR Layer");
 
-            QString qname = i.name();
-            if (qname != "A" && qname != "R" && qname != "G" && qname != "B") {
-                dbgFile << "Unknow: " << i.name();
-                info.imageType = IT_UNSUPPORTED;
-            } else {
-                info.channelMap[qname] = qname;
-            }
+    for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
+        const Imf::Channel &channel = i.channel();
+        dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
+
+        QString qname = i.name();
+        QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B";
+        if (topLevelChannelNames.contains(qname)) {
+            topLevelRGBFound = true;
+            dbgFile << "Found top-level channel" << qname;
+            info.channelMap[qname] = qname;
+            info.updateImageType(imfTypeToKisType(channel.type));
+        }
+        else if (qname.startsWith('.') || !qname.contains('.')) {
+            warnFile << "Found a top-level channel that is not part of the rendered image" << \
qname << ". Krita will not load this channel.";  }
+    }
+    if (topLevelRGBFound) {
+        dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer \
type" << info.imageType;  informationObjects.push_back(info);
         imageType = info.imageType;
-    } else {
-        dbgFile << "Multi layers:";
-        for (std::set<std::string>::const_iterator i = layerNames.begin();
-                i != layerNames.end(); ++i) {
-            ExrPaintLayerInfo info;
-            dbgFile << "layer name = " << i->c_str();
-            info.name = i->c_str();
-            Imf::ChannelList::ConstIterator layerBegin, layerEnd;
-            channels.channelsInLayer(*i, layerBegin, layerEnd);
-            for (Imf::ChannelList::ConstIterator j = layerBegin;
-                    j != layerEnd; ++j) {
-                const Imf::Channel &channel = j.channel();
-                dbgFile << "\tchannel " << j.name() << " type = " << channel.type;
-
-                info.updateImageType(imfTypeToKisType(channel.type));
-
-                QString qname = j.name();
-                QStringList list = qname.split('.');
-                QString layersuffix = list.last();
-
-                if (list.size() > 1) {
-                    info.name = list[list.size()-2];
-                    info.parent = searchGroup(&groups, list, 0, list.size() - 3);
-                }
+    }
+
+    dbgFile << "Extra layers:" << layerNames.size();
 
-                info.channelMap[layersuffix] = qname;
+    for (std::set<std::string>::const_iterator i = layerNames.begin();
+         i != layerNames.end(); ++i) {
+        dbgFile << "layer name = " << i->c_str();
+        info.name = i->c_str();
+        Imf::ChannelList::ConstIterator layerBegin, layerEnd;
+        channels.channelsInLayer(*i, layerBegin, layerEnd);
+        for (Imf::ChannelList::ConstIterator j = layerBegin;
+             j != layerEnd; ++j) {
+            const Imf::Channel &channel = j.channel();
+            dbgFile << "\tchannel " << j.name() << " type = " << channel.type;
+
+            info.updateImageType(imfTypeToKisType(channel.type));
+
+            QString qname = j.name();
+            QStringList list = qname.split('.');
+            QString layersuffix = list.last();
+
+            if (list.size() > 1) {
+                info.name = list[list.size()-2];
+                info.parent = searchGroup(&groups, list, 0, list.size() - 3);
             }
-            if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
-                informationObjects.push_back(info);
-                if (imageType < info.imageType) {
-                    imageType = info.imageType;
-                }
+
+            info.channelMap[layersuffix] = qname;
+        }
+        if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
+            informationObjects.push_back(info);
+            if (imageType < info.imageType) {
+                imageType = info.imageType;
             }
         }
     }
 
-    dbgFile << "File has " << informationObjects.size() << " layers";
+
+    dbgFile << "File has" << informationObjects.size() << "layer(s)";
+
     // Set the colorspaces
     for (int i = 0; i < informationObjects.size(); ++i) {
         ExrPaintLayerInfo& info = informationObjects[i];
@@ -672,7 +687,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
             }
 
             info.remappedChannels.push_back(
-                ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
+                        ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
 
             QString failingChannelValue = info.channelMap[failingChannelKey];
             info.channelMap.remove(failingChannelKey);
@@ -919,7 +934,7 @@ void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
 
 Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width)
 {
-//     bool hasAlpha = info.layer->colorSpace()->channelCount() != \
info.layer->colorSpace()->colorChannelCount(); +    //     bool hasAlpha = \
info.layer->colorSpace()->channelCount() != info.layer->colorSpace()->colorChannelCount();  \
switch (info.layer->colorSpace()->channelCount()) {  case 1: {
         if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
@@ -1009,9 +1024,9 @@ KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, \
KisPaintLayerSP  
     if(pixelType >= Imf::NUM_PIXELTYPES)
     {
-      return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
+        return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
     }
-      
+
 
     header.channels().insert("R", Imf::Channel(pixelType));
     header.channels().insert("G", Imf::Channel(pixelType));
@@ -1070,9 +1085,9 @@ void \
exrConverter::Private::makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& i  
             for (; it != end; ++it) {
                 QString newName =
-                    QString("%1_%2.")
-                    .arg(strippedName)
-                    .arg(nameCounter++);
+                        QString("%1_%2.")
+                        .arg(strippedName)
+                        .arg(nameCounter++);
 
                 it.value()->name = newName;
 
@@ -1167,10 +1182,10 @@ void exrConverter::Private::reportLayersNotSaved(const QSet<KisNodeSP> \
&layersNo  }
 
     QString msg =
-        i18nc("@info",
-              "<para>The following layers have a type that is not supported by EXR \
                format:</para>"
-              "<para><list>%1</list></para>"
-              "<para><warning>these layers will NOT be saved to the final EXR \
file</warning></para>", layersList); +            i18nc("@info",
+                  "<para>The following layers have a type that is not supported by EXR \
format:</para>" +                  "<para><list>%1</list></para>"
+                  "<para><warning>these layers will NOT be saved to the final EXR \
file</warning></para>", layersList);  
     if (this->showNotifications) {
         QMessageBox::information(0, i18nc("@title:window", "Layers will be lost"), msg);
diff --git a/krita/plugins/formats/exr/exr_import.cc b/krita/plugins/formats/exr/exr_import.cc
index cb57ad1..6ea9f1c 100644
--- a/krita/plugins/formats/exr/exr_import.cc
+++ b/krita/plugins/formats/exr/exr_import.cc
@@ -69,6 +69,7 @@ KisImportExportFilter::ConversionStatus exrImport::convert(const QByteArray&, \
co  
         switch (ib.buildImage(url)) {
         case KisImageBuilder_RESULT_UNSUPPORTED:
+        case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
             doc->setErrorMessage(i18n("Krita does support this type of EXR file."));
             return KisImportExportFilter::NotImplemented;
 
@@ -94,6 +95,7 @@ KisImportExportFilter::ConversionStatus exrImport::convert(const QByteArray&, \
co  Q_ASSERT(ib.image());
             doc -> setCurrentImage(ib.image());
             return KisImportExportFilter::OK;
+
         default:
             break;
         }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic