Krita Source Code Documentation
Loading...
Searching...
No Matches
KoColorConversionSystem.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007-2008 Cyrille Berger <cberger@cberger.net>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
9
10#include <QHash>
11#include <QString>
12
15#include "KoColorProfile.h"
16#include "KoColorProfileQuery.h"
17#include "KoColorSpace.h"
20
21
23 : d(new Private(registryInterface))
24{
25}
26
28{
29 qDeleteAll(d->graph);
30 qDeleteAll(d->vertexes);
31 delete d;
32}
33
35{
36 Vertex* v1 = createVertex(_node, _engine);
37 Vertex* v2 = createVertex(_engine, _node);
38
39 Q_UNUSED(v1);
40 Q_UNUSED(v2);
41}
42
44{
45 NodeKey key(engine->id(), engine->id(), engine->id());
46 Node* n = new Node;
47 n->modelId = engine->id();
48 n->depthId = engine->id();
49 n->profileName = engine->id();
50 n->referenceDepth = 64; // engine don't have reference depth,
51 d->graph.insert(key, n);
52 n->init(engine);
53 return n;
54}
55
57{
58 QList<KoColorProfileQuery> profileQueries;
59
60 const QList<const KoColorProfile*> existingProfiles = d->registryInterface->profilesFor(csf);
61 Q_FOREACH (const KoColorProfile* profile, existingProfiles) {
62 profileQueries.append(csf->requiredConnectionProfiles(profile));
63 }
64
65 // TODO: make a better 'unique' function without sorting
66 QList<KoColorProfileQuery> uniqueProfileQueries;
67 std::copy_if(profileQueries.begin(),
68 profileQueries.end(),
69 std::back_inserter(uniqueProfileQueries),
70 [&](const KoColorProfileQuery &query) {
71 return !uniqueProfileQueries.contains(query);
72 });
73
74 return uniqueProfileQueries;
75}
76
78{
79 dbgPigment << "Inserting color space " << csf->name() << " (" << csf->id() << ") Model: " << csf->colorModelId() << " Depth: " << csf->colorDepthId() << " into the CCS";
80 const QList<const KoColorProfile*> profiles = d->registryInterface->profilesFor(csf);
81 QString modelId = csf->colorModelId().id();
82 QString depthId = csf->colorDepthId().id();
84 if (profiles.isEmpty()) { // There is no profile for this CS, create a node without profile name if the color engine isn't icc-based
85 if (csf->colorSpaceEngine() != "icc") {
86 Node* n = nodeFor(modelId, depthId, "default");
87 n->init(csf);
88 }
89 else {
90 dbgPigment << "Cannot add node for " << csf->name() << ", since there are no profiles available";
91 }
92 } else {
93 // Initialise the nodes
94 Q_FOREACH (const KoColorProfile* profile, profiles) {
95 Node* n = nodeFor(modelId, depthId, profile->name());
96 cctfs.append(csf->colorConversionLinksFromProfile(profile));
97 n->init(csf);
98 if (!csf->colorSpaceEngine().isEmpty()) {
100 Q_ASSERT(engine);
101 NodeKey engineKey(engine->id(), engine->id(), engine->id());
102 Node* engineNode = 0;
103 QHash<NodeKey, Node*>::ConstIterator it = d->graph.constFind(engineKey);
104 if (it != d->graph.constEnd()) {
105 engineNode = it.value();
106 } else {
107 engineNode = insertEngine(engine);
108 }
109
110 if (engine->supportsColorSpace(modelId, depthId, profile)) {
111 connectToEngine(n, engineNode);
112 }
113 }
114 }
115 }
116 // Construct a link for "custom" transformation
117 cctfs.append(csf->colorConversionLinks());
118 Q_FOREACH (KoColorConversionTransformationFactory* cctf, cctfs) {
119 Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile());
120 Q_ASSERT(srcNode);
121 Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile());
122 Q_ASSERT(dstNode);
123 // Check if the two nodes are already connected
124 Vertex* v = vertexBetween(srcNode, dstNode);
125 // If the vertex doesn't already exist, then create it
126 if (!v) {
127 v = createVertex(srcNode, dstNode);
128 }
129 Q_ASSERT(v); // we should have one now
130 if (dstNode->modelId == modelId && dstNode->depthId == depthId) {
131 v->setFactoryFromDst(cctf);
132 }
133 if (srcNode->modelId == modelId && srcNode->depthId == depthId) {
134 v->setFactoryFromSrc(cctf);
135 }
136 }
137}
138
140{
141 QList<KoColorProfileQuery> profileQueries;
142
143 const QList< const KoColorSpaceFactory* >& factories = d->registryInterface->colorSpacesFor(profile);
144 Q_FOREACH (const KoColorSpaceFactory* factory, factories) {
145 profileQueries.append(factory->requiredConnectionProfiles(profile));
146 }
147
148 // TODO: make a better 'unique' function without sorting
149 QList<KoColorProfileQuery> uniqueProfileQueries;
150 std::copy_if(profileQueries.begin(),
151 profileQueries.end(),
152 std::back_inserter(uniqueProfileQueries),
153 [&](const KoColorProfileQuery &query) {
154 return !uniqueProfileQueries.contains(query);
155 });
156
157 return uniqueProfileQueries;
158}
159
161{
162 dbgPigmentCCS << _profile->name();
163 const QList< const KoColorSpaceFactory* >& factories = d->registryInterface->colorSpacesFor(_profile);
164 Q_FOREACH (const KoColorSpaceFactory* factory, factories) {
165 QString modelId = factory->colorModelId().id();
166 QString depthId = factory->colorDepthId().id();
167 Node* n = nodeFor(modelId, depthId, _profile->name());
168 n->init(factory);
169 if (!factory->colorSpaceEngine().isEmpty()) {
171 Q_ASSERT(engine);
172 Node* engineNode = d->graph[ NodeKey(engine->id(), engine->id(), engine->id())];
173 Q_ASSERT(engineNode);
174
175 if (engine->supportsColorSpace(modelId, depthId, _profile)) {
176 connectToEngine(n, engineNode);
177 }
178 }
180 cctfs.append(factory->colorConversionLinksFromProfile(_profile));
181 Q_FOREACH (KoColorConversionTransformationFactory* cctf, cctfs) {
182 Node* srcNode = nodeFor(cctf->srcColorModelId(), cctf->srcColorDepthId(), cctf->srcProfile());
183 Q_ASSERT(srcNode);
184 Node* dstNode = nodeFor(cctf->dstColorModelId(), cctf->dstColorDepthId(), cctf->dstProfile());
185 Q_ASSERT(dstNode);
186 if (srcNode == n || dstNode == n) {
187 // Check if the two nodes are already connected
188 Vertex* v = vertexBetween(srcNode, dstNode);
189 // If the vertex doesn't already exist, then create it
190 if (!v) {
191 v = createVertex(srcNode, dstNode);
192 }
193 Q_ASSERT(v); // we should have one now
194 if (dstNode->modelId == modelId && dstNode->depthId == depthId) {
195 v->setFactoryFromDst(cctf);
196 }
197 if (srcNode->modelId == modelId && srcNode->depthId == depthId) {
198 v->setFactoryFromSrc(cctf);
199 }
200 }
201 }
202 }
203}
204
206{
207 return d->registryInterface->colorSpace(node->modelId, node->depthId, node->profileName);
208}
209
210KoColorConversionSystem::Node* KoColorConversionSystem::createNode(const QString& _modelId, const QString& _depthId, const QString& _profileName)
211{
212 Node* n = new Node;
213 n->modelId = _modelId;
214 n->depthId = _depthId;
215 n->profileName = _profileName;
216 d->graph.insert(NodeKey(_modelId, _depthId, _profileName), n);
217 return n;
218}
219
221{
222 const KoColorProfile* profile = _colorSpace->profile();
223 return nodeFor(_colorSpace->colorModelId().id(), _colorSpace->colorDepthId().id(),
224 profile ? profile->name() : "default");
225}
226
227const KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName) const
228{
229 dbgPigmentCCS << "Look for node: " << _colorModelId << " " << _colorDepthId << " " << _profileName;
230 return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName));
231}
232
234{
235 dbgPigmentCCS << "Look for node: " << key.modelId << " " << key.depthId << " " << key.profileName << " " << d->graph.value(key);
236 return d->graph.value(key);
237}
238
239KoColorConversionSystem::Node* KoColorConversionSystem::nodeFor(const QString& _colorModelId, const QString& _colorDepthId, const QString& _profileName)
240{
241 return nodeFor(NodeKey(_colorModelId, _colorDepthId, _profileName));
242}
243
245{
246 QHash<NodeKey, Node*>::ConstIterator it = d->graph.constFind(key);
247 if (it != d->graph.constEnd()) {
248 return it.value();
249 } else {
250 return createNode(key.modelId, key.depthId, key.profileName);
251 }
252}
253
254QList<KoColorConversionSystem::Node*> KoColorConversionSystem::nodesFor(const QString& _modelId, const QString& _depthId)
255{
256 QList<Node*> nodes;
257 Q_FOREACH (Node* node, d->graph) {
258 if (node->modelId == _modelId && node->depthId == _depthId) {
259 nodes << node;
260 }
261 }
262 return nodes;
263}
264
265KoColorConversionTransformation* KoColorConversionSystem::createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
266{
267 Q_ASSERT(srcColorSpace);
268 Q_ASSERT(dstColorSpace);
269 if (*srcColorSpace == *dstColorSpace) {
270 return new KoCopyColorConversionTransformation(srcColorSpace);
271 }
272 dbgPigmentCCS << srcColorSpace->id() << (srcColorSpace->profile() ? srcColorSpace->profile()->name() : "default");
273 dbgPigmentCCS << dstColorSpace->id() << (dstColorSpace->profile() ? dstColorSpace->profile()->name() : "default");
274 Path path = findBestPath(
275 nodeFor(srcColorSpace),
276 nodeFor(dstColorSpace));
277 Q_ASSERT(path.length() > 0);
278 KoColorConversionTransformation* transfo = createTransformationFromPath(path, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
279 Q_ASSERT(transfo);
280 Q_ASSERT(*transfo->srcColorSpace() == *srcColorSpace);
281 Q_ASSERT(*transfo->dstColorSpace() == *dstColorSpace);
282 return transfo;
283}
284
285void KoColorConversionSystem::createColorConverters(const KoColorSpace* colorSpace, const QList< QPair<KoID, KoID> >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const
286{
287 // TODO This function currently only select the best conversion only based on the transformation
288 // from colorSpace to one of the color spaces in the list, but not the other way around
289 // it might be worth to look also the return path.
290 const Node* csNode = nodeFor(colorSpace);
292 // Look for a color conversion
293 Path bestPath;
294 typedef QPair<KoID, KoID> KoID2KoID;
295 Q_FOREACH (const KoID2KoID & possibility, possibilities) {
296 const KoColorSpaceFactory* csf = d->registryInterface->colorSpaceFactory(possibility.first.id(), possibility.second.id());
297 if (csf) {
298 Path path = findBestPath(csNode, nodeFor(csf->colorModelId().id(), csf->colorDepthId().id(), csf->defaultProfile()));
299 Q_ASSERT(path.length() > 0);
300 path.isGood = pQC.isGoodPath(path);
301
302 if (bestPath.isEmpty()) {
303 bestPath = path;
304 } else if ((!bestPath.isGood && path.isGood) || pQC.lessWorseThan(path, bestPath)) {
305 bestPath = path;
306 }
307 }
308 }
309 Q_ASSERT(!bestPath.isEmpty());
310 const KoColorSpace* endColorSpace = defaultColorSpaceForNode(bestPath.endNode());
312 Path returnPath = findBestPath(bestPath.endNode(), csNode);
313 Q_ASSERT(!returnPath.isEmpty());
315 Q_ASSERT(*toCS->dstColorSpace() == *fromCS->srcColorSpace());
316 Q_ASSERT(*fromCS->dstColorSpace() == *toCS->srcColorSpace());
317}
318
319KoColorConversionTransformation* KoColorConversionSystem::createTransformationFromPath(const Path &path, const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
320{
321 Q_ASSERT(srcColorSpace->colorModelId().id() == path.startNode()->modelId);
322 Q_ASSERT(srcColorSpace->colorDepthId().id() == path.startNode()->depthId);
323 Q_ASSERT(dstColorSpace->colorModelId().id() == path.endNode()->modelId);
324 Q_ASSERT(dstColorSpace->colorDepthId().id() == path.endNode()->depthId);
325
327
328 const QList< Path::node2factory > pathOfNode = path.compressedPath();
329
330 if (pathOfNode.size() == 2) { // Direct connection
331 transfo = pathOfNode[1].second->createColorTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
332 }
333 else {
334
335 KoMultipleColorConversionTransformation* mccTransfo = new KoMultipleColorConversionTransformation(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
336
337 transfo = mccTransfo;
338
339 // Get the first intermediary color space
340 dbgPigmentCCS << pathOfNode[ 0 ].first->id() << " to " << pathOfNode[ 1 ].first->id();
341
342 const KoColorSpace* intermCS =
343 defaultColorSpaceForNode(pathOfNode[1].first);
344
345 mccTransfo->appendTransfo(pathOfNode[1].second->createColorTransformation(srcColorSpace, intermCS, renderingIntent, conversionFlags));
346
347 for (int i = 2; i < pathOfNode.size() - 1; i++) {
348 dbgPigmentCCS << pathOfNode[ i - 1 ].first->id() << " to " << pathOfNode[ i ].first->id();
349 const KoColorSpace* intermCS2 = defaultColorSpaceForNode(pathOfNode[i].first);
350 Q_ASSERT(intermCS2);
351 mccTransfo->appendTransfo(pathOfNode[i].second->createColorTransformation(intermCS, intermCS2, renderingIntent, conversionFlags));
352 intermCS = intermCS2;
353 }
354
355 dbgPigmentCCS << pathOfNode[ pathOfNode.size() - 2 ].first->id() << " to " << pathOfNode[ pathOfNode.size() - 1 ].first->id();
356 mccTransfo->appendTransfo(pathOfNode.last().second->createColorTransformation(intermCS, dstColorSpace, renderingIntent, conversionFlags));
357 }
358 return transfo;
359}
360
361
363{
364 Q_FOREACH (Vertex* oV, srcNode->outputVertexes) {
365 if (oV->dstNode == dstNode) {
366 return oV;
367 }
368 }
369 return 0;
370}
371
373{
374 Vertex* v = new Vertex(srcNode, dstNode);
375 srcNode->outputVertexes.append(v);
376 d->vertexes.append(v);
377 return v;
378}
379
380// -- Graph visualization functions --
381
383{
384 return QString(" \"%1\" -> \"%2\" %3\n").arg(v->srcNode->id()).arg(v->dstNode->id()).arg(options);
385}
386
388{
389 QString dot = "digraph CCS {\n";
390 Q_FOREACH (Vertex* oV, d->vertexes) {
391 dot += vertexToDot(oV, "default") ;
392 }
393 dot += "}\n";
394 return dot;
395}
396
397bool KoColorConversionSystem::existsPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const
398{
399 dbgPigmentCCS << "srcModelId = " << srcModelId << " srcDepthId = " << srcDepthId << " srcProfileName = " << srcProfileName << " dstModelId = " << dstModelId << " dstDepthId = " << dstDepthId << " dstProfileName = " << dstProfileName;
400 const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName);
401 const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName);
402 if (srcNode == dstNode) return true;
403 if (!srcNode) return false;
404 if (!dstNode) return false;
405 Path path = findBestPath(srcNode, dstNode);
406 bool exist = !path.isEmpty();
407 return exist;
408}
409
410bool KoColorConversionSystem::existsGoodPath(const QString& srcModelId, const QString& srcDepthId, const QString& srcProfileName, const QString& dstModelId, const QString& dstDepthId, const QString& dstProfileName) const
411{
412 const Node* srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName);
413 const Node* dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName);
414 if (srcNode == dstNode) return true;
415 if (!srcNode) return false;
416 if (!dstNode) return false;
417 Path path = findBestPath(srcNode, dstNode);
418 bool existAndGood = path.isGood;
419 return existAndGood;
420}
421
422KoColorConversionSystem::Path KoColorConversionSystem::findBestPath(const QString &srcModelId, const QString &srcDepthId, const QString &srcProfileName, const QString &dstModelId, const QString &dstDepthId, const QString &dstProfileName) const
423{
424 const Node *srcNode = nodeFor(srcModelId, srcDepthId, srcProfileName);
425 const Node *dstNode = nodeFor(dstModelId, dstDepthId, dstProfileName);
426
427 KIS_ASSERT(srcNode);
428 KIS_ASSERT(dstNode);
429
430 return findBestPath(srcNode, dstNode);
431}
432
434{
435 const Node *srcNode = nodeFor(src);
436 const Node *dstNode = nodeFor(dst);
437
438 KIS_ASSERT(srcNode);
439 KIS_ASSERT(dstNode);
440
441 return findBestPath(srcNode, dstNode);
442}
443
444
445QString KoColorConversionSystem::bestPathToDot(const QString& srcKey, const QString& dstKey) const
446{
447 const Node* srcNode = 0;
448 const Node* dstNode = 0;
449 Q_FOREACH (Node* node, d->graph) {
450 if (node->id() == srcKey) {
451 srcNode = node;
452 }
453 if (node->id() == dstKey) {
454 dstNode = node;
455 }
456 }
457 Path p = findBestPath(srcNode, dstNode);
458 Q_ASSERT(!p.isEmpty());
459 QString dot = "digraph CCS {\n" +
460 QString(" \"%1\" [color=red]\n").arg(srcNode->id()) +
461 QString(" \"%1\" [color=red]\n").arg(dstNode->id());
462 Q_FOREACH (Vertex* oV, d->vertexes) {
463 QString options;
464 if (p.vertexes.contains(oV)) {
465 options = "[color=red]";
466 }
467 dot += vertexToDot(oV, options) ;
468 }
469 dot += "}\n";
470 return dot;
471}
472
474{
475 KIS_ASSERT(srcNode);
476 KIS_ASSERT(dstNode);
477
478 dbgPigmentCCS << "Find best path between " << srcNode->id() << " and " << dstNode->id();
479
480 PathQualityChecker pQC(qMin(srcNode->referenceDepth, dstNode->referenceDepth));
481 Node2PathHash node2path; // current best path to reach a given node
482 QList<Path> possiblePaths; // list of all paths
483 // Generate the initial list of paths
484 Q_FOREACH (Vertex* v, srcNode->outputVertexes) {
485 if (v->dstNode->isInitialized) {
486 Path p;
488 Node* endNode = v->dstNode;
489 if (endNode == dstNode) {
490 Q_ASSERT(pQC.isGoodPath(p)); // <- it's a direct link, it has to be a good path
491 p.isGood = true;
492 return p;
493 } else {
494 //Q_ASSERT(!node2path.contains(endNode)); // That would be a total fuck up if there are two vertices between two nodes
495 node2path.insert(endNode, p);
496 possiblePaths.append(p);
497 }
498 }
499 }
500
501 Path currentBestPath;
502 // Continue while there are any possibilities remaining
503 while (possiblePaths.size() > 0) {
504
505 // Loop through all paths and explore one step further
506 const QList<Path> currentPaths = possiblePaths;
507 for (const Path &p : currentPaths) {
508 const Node* endNode = p.endNode();
509 for (Vertex* v : endNode->outputVertexes) {
510 if (v->dstNode->isInitialized && !p.contains(v->dstNode)) {
511 Path newP = p; // Candidate
512 newP.appendVertex(v);
513 Node* newEndNode = v->dstNode;
514 if (newEndNode == dstNode) {
515 if (pQC.isGoodPath(newP)) { // Victory
516 newP.isGood = true;
517 return newP;
518 } else if (pQC.lessWorseThan(newP, currentBestPath)) {
519 if (newP.startNode() && newP.endNode() && currentBestPath.startNode() && currentBestPath.endNode()) {
520 Q_ASSERT(newP.startNode()->id() == currentBestPath.startNode()->id());
521 Q_ASSERT(newP.endNode()->id() == currentBestPath.endNode()->id());
522 // Can we do better than dumping memory values???
523 // warnPigment << pQC.lessWorseThan(newP, currentBestPath) << " " << newP << " " << currentBestPath;
524 currentBestPath = newP;
525 }
526 }
527 } else {
528 // This is an incomplete path. Check if there's a better way to get to its endpoint.
529 Node2PathHash::Iterator it = node2path.find(newEndNode);
530 if (it != node2path.end()) {
531 Path &p2 = it.value();
532 if (pQC.lessWorseThan(newP, p2)) {
533 p2 = newP;
534 possiblePaths.append(newP);
535 }
536 } else {
537 node2path.insert(newEndNode, newP);
538 possiblePaths.append(newP);
539 }
540 }
541 }
542 }
543 possiblePaths.removeAll(p); // Remove from list of remaining paths
544 }
545 }
546
547 if (!currentBestPath.isEmpty()) {
548 warnPigment << "No good path from " << srcNode->id() << " to " << dstNode->id() << " found : length = " << currentBestPath.length() << " cost = " << currentBestPath.cost << " referenceDepth = " << currentBestPath.referenceDepth << " respectColorCorrectness = " << " isGood = " << currentBestPath.isGood ;
549 return currentBestPath;
550 }
551 errorPigment << "No path from " << srcNode->id() << " to " << dstNode->id() << " found not ";
552 return currentBestPath;
553}
#define dbgPigment
#define dbgPigmentCCS
#define warnPigment
#define errorPigment
const Params2D p
qreal v
QPointF p2
QHash< KoColorConversionSystem::Node *, KoColorConversionSystem::Path > Node2PathHash
KoColorConversionTransformation * createTransformationFromPath(const KoColorConversionSystem::Path &path, const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
Vertex * createVertex(Node *srcNode, Node *dstNode)
Node * insertEngine(const KoColorSpaceEngine *engine)
QList< Node * > nodesFor(const QString &_modelId, const QString &_depthId)
void connectToEngine(Node *_node, Node *_engine)
void insertColorSpace(const KoColorSpaceFactory *csf)
bool existsGoodPath(const QString &srcModelId, const QString &srcDepthId, const QString &srcProfileName, const QString &dstModelId, const QString &dstDepthId, const QString &dstProfileName) const
KoColorConversionTransformation * createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
KoColorConversionSystem(RegistryInterface *registryInterface)
Path findBestPath(const QString &srcModelId, const QString &srcDepthId, const QString &srcProfileName, const QString &dstModelId, const QString &dstDepthId, const QString &dstProfileName) const
const KoColorSpace * defaultColorSpaceForNode(const Node *node) const
Vertex * vertexBetween(Node *srcNode, Node *dstNode)
QList< KoColorProfileQuery > requiredConnectionProfilesFor(const KoColorSpaceFactory *csf)
void insertColorProfile(const KoColorProfile *profile)
Node * createNode(const QString &_modelId, const QString &_depthId, const QString &_profileName)
bool existsPath(const QString &srcModelId, const QString &srcDepthId, const QString &srcProfileName, const QString &dstModelId, const QString &dstDepthId, const QString &dstProfileName) const
const Node * nodeFor(const KoColorSpace *) const
QString bestPathToDot(const QString &srcKey, const QString &dstKey) const
QString vertexToDot(Vertex *v, const QString &options) const
void createColorConverters(const KoColorSpace *colorSpace, const QList< QPair< KoID, KoID > > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const
static KoColorSpaceEngineRegistry * instance()
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
T get(const QString &id) const
QString id() const
Definition KoID.cpp:63
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
void init(const KoColorSpaceFactory *_colorSpaceFactory)
The KoColorProfileQuery struct.
virtual bool supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const
virtual KoID colorDepthId() const =0
virtual QString name() const =0
virtual QString defaultProfile() const =0
virtual KoID colorModelId() const =0
virtual QString id() const =0
virtual QList< KoColorConversionTransformationFactory * > colorConversionLinks() const =0
virtual QList< KoColorConversionTransformationFactory * > colorConversionLinksFromProfile(const KoColorProfile *profile) const
colorConversionLinksFromProfile Sometimes, we need to generate special color conversion links based o...
virtual QString colorSpaceEngine() const =0
virtual QList< KoColorProfileQuery > requiredConnectionProfiles(const KoColorProfile *profile) const
void appendTransfo(KoColorConversionTransformation *transfo)
bool lessWorseThan(const KoColorConversionSystem::Path &path1, const KoColorConversionSystem::Path &path2) const
bool isGoodPath(const KoColorConversionSystem::Path &path) const