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 #ifndef KOCOLORCONVERSIONSYSTEM_P_H 0008 #define KOCOLORCONVERSIONSYSTEM_P_H 0009 0010 #include "DebugPigment.h" 0011 #include "KoColorSpaceRegistry.h" 0012 #include "KoColorModelStandardIds.h" 0013 #include "KoColorConversionTransformationFactory.h" 0014 #include "KoColorSpaceEngine.h" 0015 #include "KoColorConversionSystem.h" 0016 #include <boost/operators.hpp> 0017 0018 #include <QList> 0019 0020 enum NodeCapability { 0021 None = 0x0, 0022 HasColor = 0x1, 0023 HasAlpha = 0x2, 0024 HasHdr = 0x4 0025 }; 0026 0027 Q_DECLARE_FLAGS(NodeCapabilities, NodeCapability) 0028 Q_DECLARE_OPERATORS_FOR_FLAGS(NodeCapabilities) 0029 0030 struct KoColorConversionSystem::Node : boost::equality_comparable<KoColorConversionSystem::Node> 0031 { 0032 0033 Node() 0034 : isInitialized(false) 0035 , referenceDepth(0) 0036 , crossingCost(1) 0037 , colorSpaceFactory(0) 0038 , isEngine(false) 0039 , engine(0) {} 0040 0041 void init(const KoColorSpaceFactory* _colorSpaceFactory) { 0042 dbgPigment << "Initialise " << modelId << " " << depthId << " " << profileName; 0043 0044 if (isInitialized) { 0045 dbgPigment << "Re-initializing node. Old factory" << colorSpaceFactory << "new factory" << _colorSpaceFactory; 0046 } 0047 isInitialized = true; 0048 0049 if (_colorSpaceFactory) { 0050 { 0051 const bool isGray = modelId == GrayColorModelID.id() || 0052 modelId == GrayAColorModelID.id(); 0053 0054 const bool isAlpha = modelId == AlphaColorModelID.id(); 0055 0056 if (_colorSpaceFactory->isHdr()) { 0057 m_capabilities |= HasHdr; 0058 } 0059 0060 if (!isGray && !isAlpha) { 0061 m_capabilities |= HasColor; 0062 } 0063 0064 if (!isAlpha && modelId != GrayColorModelID.id()) { 0065 m_capabilities |= HasAlpha; 0066 } 0067 } 0068 0069 colorSpaceFactory = _colorSpaceFactory; 0070 referenceDepth = _colorSpaceFactory->referenceDepth(); 0071 crossingCost = _colorSpaceFactory->crossingCost(); 0072 } 0073 } 0074 0075 void init(const KoColorSpaceEngine* _engine) { 0076 Q_ASSERT(!isInitialized); 0077 isEngine = true; 0078 isInitialized = true; 0079 engine = _engine; 0080 m_capabilities = HasAlpha | HasColor | HasHdr; 0081 } 0082 0083 QString id() const { 0084 return modelId + " " + depthId + " " + profileName; 0085 } 0086 0087 NodeKey key() const; 0088 0089 friend bool operator==(const Node &lhs, const Node &rhs) { 0090 return lhs.modelId == rhs.modelId && 0091 lhs.depthId == rhs.depthId && 0092 lhs.profileName == rhs.profileName; 0093 } 0094 0095 NodeCapabilities capabilities() const { 0096 return m_capabilities; 0097 } 0098 0099 QString modelId; 0100 QString depthId; 0101 QString profileName; 0102 bool isInitialized; 0103 int referenceDepth; 0104 QList<Vertex*> outputVertexes; 0105 int crossingCost; 0106 const KoColorSpaceFactory* colorSpaceFactory; 0107 bool isEngine; 0108 const KoColorSpaceEngine* engine; 0109 NodeCapabilities m_capabilities = None; 0110 }; 0111 0112 Q_DECLARE_TYPEINFO(KoColorConversionSystem::Node, Q_MOVABLE_TYPE); 0113 0114 QDebug operator<<(QDebug dbg, const KoColorConversionSystem::Node &node) 0115 { 0116 dbg.nospace() << "Node(" << node.modelId << ", " << node.depthId << ", " << node.profileName << ")"; 0117 return dbg.space(); 0118 } 0119 0120 struct KoColorConversionSystem::Vertex { 0121 0122 Vertex(Node* _srcNode, Node* _dstNode) 0123 : srcNode(_srcNode) 0124 , dstNode(_dstNode) 0125 , factoryFromSrc(0) 0126 , factoryFromDst(0) { 0127 } 0128 0129 ~Vertex() { 0130 if (factoryFromSrc == factoryFromDst) { 0131 delete factoryFromSrc; 0132 } else { 0133 delete factoryFromSrc; 0134 delete factoryFromDst; 0135 } 0136 } 0137 0138 void setFactoryFromSrc(KoColorConversionTransformationFactory* factory) { 0139 factoryFromSrc = factory; 0140 } 0141 0142 void setFactoryFromDst(KoColorConversionTransformationFactory* factory) { 0143 factoryFromDst = factory; 0144 } 0145 0146 KoColorConversionTransformationFactory* factory() { 0147 if (factoryFromSrc) return factoryFromSrc; 0148 return factoryFromDst; 0149 } 0150 0151 Node* srcNode; 0152 Node* dstNode; 0153 0154 private: 0155 0156 KoColorConversionTransformationFactory* factoryFromSrc; // Factory provided by the destination node 0157 KoColorConversionTransformationFactory* factoryFromDst; // Factory provided by the destination node 0158 0159 }; 0160 0161 struct KoColorConversionSystem::NodeKey 0162 : public boost::equality_comparable<NodeKey> 0163 { 0164 0165 NodeKey(const QString &_modelId, const QString &_depthId, const QString &_profileName) 0166 : modelId(_modelId) 0167 , depthId(_depthId) 0168 , profileName(_profileName) {} 0169 0170 bool operator==(const KoColorConversionSystem::NodeKey& rhs) const { 0171 return modelId == rhs.modelId && depthId == rhs.depthId && profileName == rhs.profileName; 0172 } 0173 0174 QString modelId; 0175 QString depthId; 0176 QString profileName; 0177 }; 0178 Q_DECLARE_TYPEINFO(KoColorConversionSystem::NodeKey, Q_MOVABLE_TYPE); 0179 0180 QDebug operator<<(QDebug dbg, const KoColorConversionSystem::NodeKey &key) 0181 { 0182 dbg.nospace() << "NodeKey(" << key.modelId << ", " << key.depthId << ", " << key.profileName << ")"; 0183 return dbg.space(); 0184 } 0185 0186 0187 inline KoColorConversionSystem::NodeKey KoColorConversionSystem::Node::key() const 0188 { 0189 return NodeKey(modelId, depthId, profileName); 0190 } 0191 0192 struct KoColorConversionSystem::Path { 0193 0194 Path() 0195 : referenceDepth(0) 0196 , isGood(false) 0197 , cost(0) {} 0198 0199 Node* startNode() { 0200 return vertexes.size() > 0 ? 0201 (vertexes.first())->srcNode 0202 : 0; 0203 } 0204 0205 bool operator==(const Path &other) const { 0206 return other.vertexes == vertexes; 0207 } 0208 0209 0210 const Node* startNode() const { 0211 return vertexes.size() > 0 ? 0212 (vertexes.first())->srcNode 0213 : 0; 0214 } 0215 0216 Node* endNode() { 0217 return vertexes.size() > 0 ? 0218 (vertexes.last())->dstNode 0219 : 0; 0220 } 0221 0222 const Node* endNode() const { 0223 return vertexes.size() > 0 ? 0224 (vertexes.last())->dstNode 0225 : 0; 0226 } 0227 0228 bool isEmpty() const { 0229 return vertexes.isEmpty(); 0230 } 0231 0232 void appendVertex(Vertex* v) { 0233 if (vertexes.empty()) { 0234 referenceDepth = v->srcNode->referenceDepth; 0235 commonNodeCapabilities = v->srcNode->capabilities(); 0236 } 0237 0238 commonNodeCapabilities &= v->dstNode->capabilities(); 0239 0240 vertexes.append(v); 0241 0242 referenceDepth = qMin(referenceDepth, v->dstNode->referenceDepth); 0243 cost += v->dstNode->crossingCost; 0244 } 0245 0246 NodeCapabilities unsupportedCapabilities() const { 0247 NodeCapabilities minimalCaps = 0248 !vertexes.isEmpty() ? 0249 vertexes.first()->srcNode->capabilities() & 0250 vertexes.last()->dstNode->capabilities() : 0251 None; 0252 0253 return (minimalCaps & commonNodeCapabilities) ^ minimalCaps; 0254 } 0255 0256 // Compress path to hide the Engine node and correctly select the factory 0257 typedef QPair<Node*, const KoColorConversionTransformationAbstractFactory* > node2factory; 0258 QList< node2factory > compressedPath() const { 0259 QList< node2factory > nodes; 0260 nodes.push_back(node2factory(vertexes.first()->srcNode , vertexes.first()->factory())); 0261 const KoColorConversionTransformationAbstractFactory* previousFactory = 0; 0262 Q_FOREACH (Vertex* vertex, vertexes) { // Unless the node is the icc node, add it to the path 0263 Node* n = vertex->dstNode; 0264 if (n->isEngine) { 0265 previousFactory = n->engine; 0266 } else { 0267 nodes.push_back( 0268 node2factory(n, 0269 previousFactory ? previousFactory : vertex->factory())); 0270 previousFactory = 0; 0271 } 0272 } 0273 return nodes; 0274 } 0275 0276 int length() const { 0277 return vertexes.size(); 0278 } 0279 0280 bool contains(Node* n) const { 0281 Q_FOREACH (Vertex* v, vertexes) { 0282 if (v->srcNode == n || v->dstNode == n) { 0283 return true; 0284 } 0285 } 0286 return false; 0287 } 0288 0289 QList<Vertex*> vertexes; 0290 int referenceDepth; 0291 bool isGood; 0292 int cost; 0293 NodeCapabilities commonNodeCapabilities = None; 0294 0295 }; 0296 Q_DECLARE_TYPEINFO(KoColorConversionSystem::Path, Q_MOVABLE_TYPE); 0297 0298 0299 inline QDebug operator<<(QDebug dbg, const KoColorConversionSystem::Path &path) 0300 { 0301 bool havePrintedFirst = false; 0302 0303 Q_FOREACH (const KoColorConversionSystem::Vertex *v, path.vertexes) { 0304 if (!havePrintedFirst) { 0305 dbg.nospace() << v->srcNode->id(); 0306 havePrintedFirst = true; 0307 } 0308 0309 dbg.nospace() << "->" << v->dstNode->id(); 0310 } 0311 0312 return dbg.space(); 0313 } 0314 0315 0316 typedef QHash<KoColorConversionSystem::Node*, KoColorConversionSystem::Path > Node2PathHash; 0317 0318 0319 uint qHash(const KoColorConversionSystem::NodeKey &key) 0320 { 0321 return qHash(key.modelId) + qHash(key.depthId); 0322 } 0323 0324 struct Q_DECL_HIDDEN KoColorConversionSystem::Private { 0325 0326 Private(RegistryInterface *_registryInterface) : registryInterface(_registryInterface) {} 0327 0328 QHash<NodeKey, Node*> graph; 0329 QList<Vertex*> vertexes; 0330 RegistryInterface *registryInterface; 0331 }; 0332 0333 struct PathQualityChecker { 0334 0335 PathQualityChecker(int _referenceDepth) 0336 : referenceDepth(_referenceDepth) 0337 {} 0338 0339 /// @return true if the path maximize all the criteria (except length) 0340 inline bool isGoodPath(const KoColorConversionSystem::Path & path) const { 0341 return path.unsupportedCapabilities() == None && 0342 path.referenceDepth >= referenceDepth; 0343 } 0344 0345 /** 0346 * Compare two paths. 0347 */ 0348 inline bool lessWorseThan(const KoColorConversionSystem::Path &path1, const KoColorConversionSystem::Path &path2) const { 0349 // There is no point in comparing two paths which doesn't start from the same node or doesn't end at the same node 0350 0351 NodeCapabilities unsupported1 = path1.unsupportedCapabilities(); 0352 NodeCapabilities unsupported2 = path2.unsupportedCapabilities(); 0353 0354 if (!unsupported1.testFlag(HasHdr) && unsupported2.testFlag(HasHdr)) { 0355 return true; 0356 } 0357 0358 if (!unsupported1.testFlag(HasColor) && unsupported2.testFlag(HasColor)) { 0359 return true; 0360 } 0361 0362 if (!unsupported1.testFlag(HasAlpha) && unsupported2.testFlag(HasAlpha)) { 0363 return true; 0364 } 0365 0366 if (path1.referenceDepth == path2.referenceDepth) { 0367 return path1.cost < path2.cost; // if they have the same cost, well anyway you have to choose one, and there is no point in keeping one and not the other 0368 } 0369 return path1.referenceDepth > path2.referenceDepth; 0370 } 0371 int referenceDepth; 0372 }; 0373 0374 #undef CHECK_ONE_AND_NOT_THE_OTHER 0375 0376 #endif