File indexing completed on 2024-05-12 15:59:32

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