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 }