Warning, file /office/calligra/libs/pigment/KoColorConversionSystem.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KoColorConversionSystem.h"
0021 #include "KoColorConversionSystem_p.h"
0022 
0023 #include <QHash>
0024 #include <QString>
0025 
0026 #include "KoColorConversionAlphaTransformation.h"
0027 #include "KoColorConversionTransformation.h"
0028 #include "KoColorProfile.h"
0029 #include "KoColorSpace.h"
0030 #include "KoCopyColorConversionTransformation.h"
0031 #include "KoMultipleColorConversionTransformation.h"
0032 
0033 
0034 KoColorConversionSystem::KoColorConversionSystem() : d(new Private)
0035 {
0036     // Create the Alpha 8bit
0037     d->alphaNode = new Node;
0038     d->alphaNode->modelId = AlphaColorModelID.id();
0039     d->alphaNode->depthId = Integer8BitsColorDepthID.id();
0040     d->alphaNode->crossingCost = 1000000;
0041     d->alphaNode->isInitialized = true;
0042     d->alphaNode->isGray = true; // <- FIXME: it's a little bit hacky as alpha doesn't really have color information
0043     d->graph.insert(NodeKey(d->alphaNode->modelId, d->alphaNode->depthId, "default"), d->alphaNode);
0044 
0045     Vertex* v = createVertex(d->alphaNode, d->alphaNode);
0046     v->setFactoryFromSrc(new KoCopyColorConversionTransformationFactory(AlphaColorModelID.id(), Integer8BitsColorDepthID.id(), "default"));
0047 }
0048 
0049 KoColorConversionSystem::~KoColorConversionSystem()
0050 {
0051     qDeleteAll(d->graph);
0052     qDeleteAll(d->vertexes);
0053     delete d;
0054 }
0055 
0056 void KoColorConversionSystem::connectToEngine(Node* _node, Node* _engine)
0057 {
0058     Vertex* v1 = createVertex(_node, _engine);
0059     Vertex* v2 = createVertex(_engine, _node);
0060     v1->conserveColorInformation = !_node->isGray;
0061     v2->conserveColorInformation = !_node->isGray;
0062     v1->conserveDynamicRange = _engine->isHdr;
0063     v2->conserveDynamicRange = _engine->isHdr;
0064 }
0065 
0066 KoColorConversionSystem::Node* KoColorConversionSystem::insertEngine(const KoColorSpaceEngine* engine)
0067 {
0068     NodeKey key(engine->id(), engine->id(), engine->id());
0069     Node* n = new Node;
0070     n->modelId = engine->id();
0071     n->depthId = engine->id();
0072     n->profileName = engine->id();
0073     n->referenceDepth = 64; // engine don't have reference depth,
0074     d->graph.insert(key, n);
0075     n->init(engine);
0076     return n;
0077 }
0078 
0079 
0080 void KoColorConversionSystem::insertColorSpace(const KoColorSpaceFactory* csf)
0081 {
0082     dbgPigment << "Inserting color space " << csf->name() << " (" << csf->id() << ") Model: " << csf->colorModelId() << " Depth: " << csf->colorDepthId() << " into the CCS";
0083     const QList<const KoColorProfile*> profiles = KoColorSpaceRegistry::instance()->profilesFor(csf);
0084     QString modelId = csf->colorModelId().id();
0085     QString depthId = csf->colorDepthId().id();
0086     if (profiles.isEmpty()) { // There is no profile for this CS, create a node without profile name if the color engine isn't icc-based
0087         if (csf->colorSpaceEngine() != "icc") {
0088             Node* n = nodeFor(modelId, depthId, "default");
0089             n->init(csf);
0090         }
0091         else {
0092             dbgPigment << "Cannot add node for " << csf->name() << ", since there are no profiles available";
0093         }
0094     } else {
0095         // Initialise the nodes
0096         foreach(const KoColorProfile* profile, profiles) {
0097             Node* n = nodeFor(modelId, depthId, profile->name());
0098             n->init(csf);
0099             if (!csf->colorSpaceEngine().isEmpty()) {
0100                 KoColorSpaceEngine* engine = KoColorSpaceEngineRegistry::instance()->get(csf->colorSpaceEngine());
0101                 Q_ASSERT(engine);
0102                 NodeKey engineKey(engine->id(), engine->id(), engine->id());
0103                 Node* engineNode = 0;
0104                 QHash<NodeKey, Node*>::ConstIterator it = d->graph.constFind(engineKey);
0105                 if (it != d->graph.constEnd()) {
0106                     engineNode = it.value();
0107                 } else {
0108                     engineNode = insertEngine(engine);
0109                 }
0110                 connectToEngine(n, engineNode);
0111             }
0112         }
0113     }
0114     // Construct a link for "custom" transformation
0115     const QList<KoColorConversionTransformationFactory*> cctfs = csf->colorConversionLinks();
0116     foreach(KoColorConversionTransformationFactory* cctf, cctfs) {
0117         Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile());
0118         Q_ASSERT(srcNode);
0119         Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile());
0120         Q_ASSERT(dstNode);
0121         // Check if the two nodes are already connected
0122         Vertex* v = vertexBetween(srcNode, dstNode);
0123         // If the vertex doesn't already exist, then create it
0124         if (!v) {
0125             v = createVertex(srcNode, dstNode);
0126         }
0127         Q_ASSERT(v); // we should have one now
0128         if (dstNode->modelId == modelId && dstNode->depthId == depthId) {
0129             v->setFactoryFromDst(cctf);
0130         }
0131         if (srcNode->modelId == modelId && srcNode->depthId == depthId) {
0132             v->setFactoryFromSrc(cctf);
0133         }
0134     }
0135 }
0136 
0137 void KoColorConversionSystem::insertColorProfile(const KoColorProfile* _profile)
0138 {
0139     dbgPigmentCCS << _profile->name();
0140     const QList< const KoColorSpaceFactory* >& factories = KoColorSpaceRegistry::instance()->colorSpacesFor(_profile);
0141     foreach(const KoColorSpaceFactory* factory, factories) {
0142         QString modelId = factory->colorModelId().id();
0143         QString depthId = factory->colorDepthId().id();
0144         Node* n = nodeFor(modelId, depthId, _profile->name());
0145         n->init(factory);
0146         if (!factory->colorSpaceEngine().isEmpty()) {
0147             KoColorSpaceEngine* engine = KoColorSpaceEngineRegistry::instance()->get(factory->colorSpaceEngine());
0148             Q_ASSERT(engine);
0149             Node* engineNode = d->graph[ NodeKey(engine->id(), engine->id(), engine->id())];
0150             Q_ASSERT(engineNode);
0151             connectToEngine(n, engineNode);
0152         }
0153         const QList<KoColorConversionTransformationFactory*> cctfs = factory->colorConversionLinks();
0154         foreach(KoColorConversionTransformationFactory* cctf, cctfs) {
0155             Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile());
0156             Q_ASSERT(srcNode);
0157             Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile());
0158             Q_ASSERT(dstNode);
0159             if (srcNode == n || dstNode == n) {
0160                 // Check if the two nodes are already connected
0161                 Vertex* v = vertexBetween(srcNode, dstNode);
0162                 // If the vertex doesn't already exist, then create it
0163                 if (!v) {
0164                     v = createVertex(srcNode, dstNode);
0165                 }
0166                 Q_ASSERT(v); // we should have one now
0167                 if (dstNode->modelId == modelId && dstNode->depthId == depthId) {
0168                     v->setFactoryFromDst(cctf);
0169                 }
0170                 if (srcNode->modelId == modelId && srcNode->depthId == depthId) {
0171                     v->setFactoryFromSrc(cctf);
0172                 }
0173             }
0174         }
0175     }
0176 }
0177 
0178 const KoColorSpace* KoColorConversionSystem::defaultColorSpaceForNode(const Node* node) const
0179 {
0180     return KoColorSpaceRegistry::instance()->colorSpace(node->modelId, node->depthId, node->profileName);
0181 }
0182 
0183 KoColorConversionSystem::Node* KoColorConversionSystem::createNode(const QString& _modelId, const QString& _depthId, const QString& _profileName)
0184 {
0185     Node* n = new Node;
0186     n->modelId = _modelId;
0187     n->depthId = _depthId;
0188     n->profileName = _profileName;
0189     d->graph.insert(NodeKey(_modelId, _depthId, _profileName), n);
0190     Q_ASSERT(vertexBetween(d->alphaNode, n) == 0); // The two color spaces should not be connected yet
0191     Vertex* vFromAlpha = createVertex(d->alphaNode, n);
0192     vFromAlpha->setFactoryFromSrc(new KoColorConversionFromAlphaTransformationFactory(_modelId, _depthId, _profileName));
0193     Q_ASSERT(vertexBetween(n, d->alphaNode) == 0); // The two color spaces should not be connected yet
0194     Vertex* vToAlpha = createVertex(n, d->alphaNode);
0195     vToAlpha->setFactoryFromDst(new KoColorConversionToAlphaTransformationFactory(_modelId, _depthId, _profileName));
0196     return n;
0197 }
0198 
0199 const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const KoColorSpace* _colorSpace) const
0200 {
0201     const KoColorProfile* profile = _colorSpace->profile();
0202     return nodeFor(_colorSpace->colorModelId().id(), _colorSpace->colorDepthId().id(),
0203                    profile ? profile->name() : "default");
0204 }
0205 
0206 const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName) const
0207 {
0208     //dbgPigmentCCS << "Look for node: " << _colorModelId << " " << _colorDepthId << " " << _profileName;
0209     return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName));
0210 }
0211 
0212 const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const NodeKey& key) const
0213 {
0214     //dbgPigmentCCS << "Look for node: " << key.modelId << " " << key.depthId << " " << key.profileName << " " << d->graph.value(key);
0215     return d->graph.value(key);
0216 }
0217 
0218 KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName)
0219 {
0220     return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName));
0221 }
0222 
0223 KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const KoColorConversionSystem::NodeKey& key)
0224 {
0225     QHash<NodeKey, Node*>::ConstIterator it = d->graph.constFind(key);
0226     if (it != d->graph.constEnd()) {
0227         return it.value();
0228     } else {
0229         return createNode(key.modelId, key.depthId, key.profileName);
0230     }
0231 }
0232 
0233 QList<KoColorConversionSystem::Node*> KoColorConversionSystem::nodesFor(const QString& _modelId, const QString& _depthId)
0234 {
0235     QList<Node*> nodes;
0236     foreach(Node* node, d->graph) {
0237         if (node->modelId == _modelId &&  node->depthId == _depthId) {
0238             nodes << node;
0239         }
0240     }
0241     return nodes;
0242 }
0243 
0244 KoColorConversionTransformation* KoColorConversionSystem::createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0245 {
0246     if (*srcColorSpace == *dstColorSpace) {
0247         return new KoCopyColorConversionTransformation(srcColorSpace);
0248     }
0249     Q_ASSERT(srcColorSpace);
0250     Q_ASSERT(dstColorSpace);
0251     dbgPigmentCCS << srcColorSpace->id() << (srcColorSpace->profile() ? srcColorSpace->profile()->name() : "default");
0252     dbgPigmentCCS << dstColorSpace->id() << (dstColorSpace->profile() ? dstColorSpace->profile()->name() : "default");
0253     Path path = findBestPath(
0254                      nodeFor(srcColorSpace),
0255                      nodeFor(dstColorSpace));
0256     Q_ASSERT(path.length() > 0);
0257     KoColorConversionTransformation* transfo = createTransformationFromPath(path, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
0258     Q_ASSERT(*transfo->srcColorSpace() == *srcColorSpace);
0259     Q_ASSERT(*transfo->dstColorSpace() == *dstColorSpace);
0260     Q_ASSERT(transfo);
0261     return transfo;
0262 }
0263 
0264 void KoColorConversionSystem::createColorConverters(const KoColorSpace* colorSpace, const QList< QPair<KoID, KoID> >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const
0265 {
0266     // TODO This function currently only select the best conversion only based on the transformation
0267     // from colorSpace to one of the color spaces in the list, but not the other way around
0268     // it might be worth to look also the return path.
0269     const Node* csNode = nodeFor(colorSpace);
0270     PathQualityChecker pQC(csNode->referenceDepth, !csNode->isHdr, !csNode->isGray);
0271     // Look for a color conversion
0272     Path bestPath;
0273     typedef QPair<KoID, KoID> KoID2KoID;
0274     foreach(const KoID2KoID & possibility, possibilities) {
0275         const KoColorSpaceFactory* csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(KoColorSpaceRegistry::instance()->colorSpaceId(possibility.first.id(), possibility.second.id()));
0276         if (csf) {
0277             Path path = findBestPath(csNode, nodeFor(csf->colorModelId().id(), csf->colorDepthId().id(), csf->defaultProfile()));
0278             Q_ASSERT(path.length() > 0);
0279             path.isGood = pQC.isGoodPath(path);
0280 
0281             if (bestPath.isEmpty()) {
0282                 bestPath = path;
0283             } else if ((!bestPath.isGood &&  path.isGood) || pQC.lessWorseThan(path, bestPath)) {
0284                 bestPath = path;
0285             }
0286         }
0287     }
0288     Q_ASSERT(!bestPath.isEmpty());
0289     const KoColorSpace* endColorSpace = defaultColorSpaceForNode(bestPath.endNode());
0290     fromCS = createTransformationFromPath(bestPath, colorSpace, endColorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
0291     Path returnPath = findBestPath(bestPath.endNode(), csNode);
0292     Q_ASSERT(!returnPath.isEmpty());
0293     toCS = createTransformationFromPath(returnPath, endColorSpace, colorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
0294     Q_ASSERT(*toCS->dstColorSpace() == *fromCS->srcColorSpace());
0295     Q_ASSERT(*fromCS->dstColorSpace() == *toCS->srcColorSpace());
0296 }
0297 
0298 KoColorConversionTransformation* KoColorConversionSystem::createTransformationFromPath(const Path &path, const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0299 {
0300     Q_ASSERT(srcColorSpace->colorModelId().id() == path.startNode()->modelId);
0301     Q_ASSERT(srcColorSpace->colorDepthId().id() == path.startNode()->depthId);
0302     Q_ASSERT(dstColorSpace->colorModelId().id() == path.endNode()->modelId);
0303     Q_ASSERT(dstColorSpace->colorDepthId().id() == path.endNode()->depthId);
0304 
0305     KoColorConversionTransformation* transfo;
0306 
0307     const QList< Path::node2factory > pathOfNode = path.compressedPath();
0308 
0309     if (pathOfNode.size() == 2) { // Direct connection
0310         transfo = pathOfNode[1].second->createColorTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
0311     }
0312     else {
0313 
0314         KoMultipleColorConversionTransformation* mccTransfo = new KoMultipleColorConversionTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
0315 
0316         transfo = mccTransfo;
0317 
0318         // Get the first intermediary color space
0319         dbgPigmentCCS << pathOfNode[ 0 ].first->id() << " to " << pathOfNode[ 1 ].first->id();
0320 
0321         const KoColorSpace* intermCS =
0322             defaultColorSpaceForNode(pathOfNode[1].first);
0323 
0324         mccTransfo->appendTransfo(pathOfNode[1].second->createColorTransformation(srcColorSpace, intermCS, renderingIntent, conversionFlags));
0325 
0326         for (int i = 2; i < pathOfNode.size() - 1; i++) {
0327             dbgPigmentCCS << pathOfNode[ i - 1 ].first->id() << " to " << pathOfNode[ i ].first->id();
0328             const KoColorSpace* intermCS2 = defaultColorSpaceForNode(pathOfNode[i].first);
0329             Q_ASSERT(intermCS2);
0330             mccTransfo->appendTransfo(pathOfNode[i].second->createColorTransformation(intermCS, intermCS2, renderingIntent, conversionFlags));
0331             intermCS = intermCS2;
0332         }
0333 
0334         dbgPigmentCCS << pathOfNode[ pathOfNode.size() - 2 ].first->id() << " to " << pathOfNode[ pathOfNode.size() - 1 ].first->id();
0335         mccTransfo->appendTransfo(pathOfNode.last().second->createColorTransformation(intermCS, dstColorSpace, renderingIntent, conversionFlags));
0336     }
0337     return transfo;
0338 }
0339 
0340 
0341 KoColorConversionSystem::Vertex* KoColorConversionSystem::vertexBetween(KoColorConversionSystem::Node* srcNode, KoColorConversionSystem::Node* dstNode)
0342 {
0343     foreach(Vertex* oV, srcNode->outputVertexes) {
0344         if (oV->dstNode == dstNode) {
0345             return oV;
0346         }
0347     }
0348     return 0;
0349 }
0350 
0351 KoColorConversionSystem::Vertex* KoColorConversionSystem::createVertex(Node* srcNode, Node* dstNode)
0352 {
0353     Vertex* v = new Vertex(srcNode, dstNode);
0354     srcNode->outputVertexes.append(v);
0355     d->vertexes.append(v);
0356     return v;
0357 }
0358 
0359 // -- Graph visualization functions --
0360 
0361 QString KoColorConversionSystem::vertexToDot(KoColorConversionSystem::Vertex* v, const QString &options) const
0362 {
0363     return QString("  \"%1\" -> \"%2\" %3\n").arg(v->srcNode->id(), v->dstNode->id(), options);
0364 }
0365 
0366 QString KoColorConversionSystem::toDot() const
0367 {
0368     QString dot = "digraph CCS {\n";
0369     foreach(Vertex* oV, d->vertexes) {
0370         dot += vertexToDot(oV, "default") ;
0371     }
0372     dot += "}\n";
0373     return dot;
0374 }
0375 
0376 bool KoColorConversionSystem::existsPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const
0377 {
0378     //dbgPigmentCCS << "srcModelId = " << srcModelId << " srcDepthId = " << srcDepthId << " srcProfileName = " << srcProfileName << " dstModelId = " << dstModelId << " dstDepthId = " << dstDepthId << " dstProfileName = " << dstProfileName;
0379     const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName);
0380     const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName);
0381     if (srcNode == dstNode) return true;
0382     if (!srcNode) return false;
0383     if (!dstNode) return false;
0384     Path path = findBestPath(srcNode, dstNode);
0385     bool exist = !path.isEmpty();
0386     return exist;
0387 }
0388 
0389 bool KoColorConversionSystem::existsGoodPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const
0390 {
0391     const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName);
0392     const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName);
0393     if (srcNode == dstNode) return true;
0394     if (!srcNode) return false;
0395     if (!dstNode) return false;
0396     Path path = findBestPath(srcNode, dstNode);
0397     bool existAndGood = path.isGood;
0398     return existAndGood;
0399 }
0400 
0401 
0402 QString KoColorConversionSystem::bestPathToDot(const QString& srcKey, const QString& dstKey) const
0403 {
0404     const Node* srcNode = 0;
0405     const Node* dstNode = 0;
0406     foreach(Node* node, d->graph) {
0407         if (node->id() == srcKey) {
0408             srcNode = node;
0409         }
0410         if (node->id() == dstKey) {
0411             dstNode = node;
0412         }
0413     }
0414     Path p = findBestPath(srcNode, dstNode);
0415     Q_ASSERT(!p.isEmpty());
0416     QString dot = "digraph CCS {\n" +
0417                   QString("  \"%1\" [color=red]\n").arg(srcNode->id()) +
0418                   QString("  \"%1\" [color=red]\n").arg(dstNode->id());
0419     foreach(Vertex* oV, d->vertexes) {
0420         QString options;
0421         if (p.vertexes.contains(oV)) {
0422             options = "[color=red]";
0423         }
0424         dot += vertexToDot(oV, options) ;
0425     }
0426     dot += "}\n";
0427     return dot;
0428 }
0429 
0430 inline KoColorConversionSystem::Path KoColorConversionSystem::findBestPathImpl2(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode, bool ignoreHdr, bool ignoreColorCorrectness) const
0431 {
0432     PathQualityChecker pQC(qMin(srcNode->referenceDepth, dstNode->referenceDepth), ignoreHdr, ignoreColorCorrectness);
0433     Node2PathHash node2path; // current best path to reach a given node
0434     QList<Path> possiblePaths; // list of all paths
0435     // Generate the initial list of paths
0436     foreach(Vertex* v, srcNode->outputVertexes) {
0437         if (v->dstNode->isInitialized) {
0438             Path p;
0439             p.appendVertex(v);
0440             Node* endNode = v->dstNode;
0441             if (endNode == dstNode) {
0442                 Q_ASSERT(pQC.isGoodPath(p));  // <- it's a direct link, it has to be a good path
0443                 p.isGood = true;
0444                 return p;
0445             } else {
0446                 Q_ASSERT(!node2path.contains(endNode));   // That would be a total fuck up if there are two vertexes between two nodes
0447                 node2path.insert(endNode, p);
0448                 possiblePaths.append(p);
0449             }
0450         }
0451     }
0452 
0453     Path currentBestPath;
0454     // Continue while there are any possibilities remaining
0455     while (possiblePaths.size() > 0) {
0456 
0457         // Loop through all paths and explore one step further
0458         const QList<Path> currentPaths = possiblePaths;
0459         for (const Path &p : currentPaths) {
0460             const Node* endNode = p.endNode();
0461             for (Vertex* v : endNode->outputVertexes) {
0462                 if (v->dstNode->isInitialized && !p.contains(v->dstNode)) {
0463                     Path newP = p;  // Candidate
0464                     newP.appendVertex(v);
0465                     Node* newEndNode = v->dstNode;
0466                     if (newEndNode == dstNode) {
0467                         if (pQC.isGoodPath(newP)) { // Victory
0468                             newP.isGood = true;
0469                             return newP;
0470                         } else if (pQC.lessWorseThan(newP, currentBestPath)) {
0471                             Q_ASSERT(newP.startNode()->id() == currentBestPath.startNode()->id());
0472                             Q_ASSERT(newP.endNode()->id() == currentBestPath.endNode()->id());
0473                             // Can we do better than dumping memory values???
0474                             // warnPigment << pQC.lessWorseThan(newP, currentBestPath) << " " << newP << "  " << currentBestPath;
0475                             currentBestPath = newP;
0476                         }
0477                     } else {
0478                         // This is an incomplete path. Check if there's a better way to get to its endpoint.
0479                         Node2PathHash::Iterator it = node2path.find(newEndNode);
0480                         if (it != node2path.end()) {
0481                             Path &p2 = it.value();
0482                             if (pQC.lessWorseThan(newP, p2)) {
0483                                 p2 = newP;
0484                                 possiblePaths.append(newP);
0485                             }
0486                         } else {
0487                             node2path.insert(newEndNode, newP);
0488                             possiblePaths.append(newP);
0489                         }
0490                     }
0491                 }
0492             }
0493             possiblePaths.removeAll(p); // Remove from list of remaining paths
0494         }
0495     }
0496     if (!currentBestPath.isEmpty()) {
0497         warnPigment << "No good path from " << srcNode->id() << " to " << dstNode->id() << " found : length = " << currentBestPath.length() << " cost = " << currentBestPath.cost << " referenceDepth = " << currentBestPath.referenceDepth << " respectColorCorrectness = " << currentBestPath.respectColorCorrectness << " isGood = " << currentBestPath.isGood ;
0498         return currentBestPath;
0499     }
0500     errorPigment << "No path from " << srcNode->id() << " to " << dstNode->id() << " found not ";
0501     return currentBestPath;
0502 }
0503 
0504 inline KoColorConversionSystem::Path KoColorConversionSystem::findBestPathImpl(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode, bool ignoreHdr) const
0505 {
0506     Q_ASSERT(srcNode);
0507     Q_ASSERT(dstNode);
0508     return findBestPathImpl2(srcNode, dstNode, ignoreHdr, (srcNode->isGray || dstNode->isGray));
0509 }
0510 
0511 KoColorConversionSystem::Path KoColorConversionSystem::findBestPath(const KoColorConversionSystem::Node* srcNode, const KoColorConversionSystem::Node* dstNode) const
0512 {
0513     Q_ASSERT(srcNode);
0514     Q_ASSERT(dstNode);
0515     //dbgPigmentCCS << "Find best path between " << srcNode->id() << " and  " << dstNode->id();
0516     if (srcNode->isHdr &&  dstNode->isHdr) {
0517         return findBestPathImpl(srcNode, dstNode, false);
0518     } else {
0519         return findBestPathImpl(srcNode, dstNode, true);
0520     }
0521 }