File indexing completed on 2024-05-19 04:27:20
0001 /* 0002 * SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org> 0004 * SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "KoColor.h" 0010 0011 #include <QColor> 0012 0013 #include <QDomDocument> 0014 #include <QRegExp> 0015 0016 #include "DebugPigment.h" 0017 0018 #include "KoColorModelStandardIds.h" 0019 #include "KoColorProfile.h" 0020 #include "KoColorSpace.h" 0021 #include "KoColorSpaceRegistry.h" 0022 #include "KoChannelInfo.h" 0023 #include "kis_assert.h" 0024 #include "kis_dom_utils.h" 0025 0026 #include <QGlobalStatic> 0027 0028 #include <KoConfig.h> 0029 #ifdef HAVE_OPENEXR 0030 #include <half.h> 0031 #endif 0032 0033 namespace { 0034 0035 struct DefaultKoColorInitializer 0036 { 0037 DefaultKoColorInitializer() { 0038 const KoColorSpace *defaultColorSpace = KoColorSpaceRegistry::instance()->rgb16(0); 0039 KIS_ASSERT(defaultColorSpace); 0040 0041 value = new KoColor(Qt::black, defaultColorSpace); 0042 #ifndef NODEBUG 0043 #ifndef QT_NO_DEBUG 0044 // warn about rather expensive checks in assertPermanentColorspace(). 0045 qWarning() << "KoColor debug runtime checks are active."; 0046 #endif 0047 #endif 0048 0049 initializeMetatype(); 0050 } 0051 0052 void initializeMetatype() { 0053 qRegisterMetaType<KoColor>(); 0054 0055 /** 0056 * We want KoColor to be comparable inside QVariant, 0057 * so we should generate comparators. 0058 */ 0059 QMetaType::registerEqualsComparator<KoColor>(); 0060 } 0061 0062 ~DefaultKoColorInitializer() { 0063 delete value; 0064 } 0065 0066 KoColor *value = 0; 0067 }; 0068 0069 Q_GLOBAL_STATIC(DefaultKoColorInitializer, s_defaultKoColor) 0070 0071 } 0072 0073 KoColor::KoColor() 0074 : m_colorSpace(s_defaultKoColor->value->m_colorSpace) 0075 , m_size(s_defaultKoColor->value->m_size) 0076 { 0077 memcpy(m_data, s_defaultKoColor->value->m_data, m_size); 0078 } 0079 0080 KoColor::KoColor(const KoColorSpace * colorSpace) 0081 { 0082 Q_ASSERT(colorSpace); 0083 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); 0084 m_size = m_colorSpace->pixelSize(); 0085 Q_ASSERT(m_size <= MAX_PIXEL_SIZE); 0086 memset(m_data, 0, m_size); 0087 } 0088 0089 KoColor::KoColor(const QColor & color, const KoColorSpace * colorSpace) 0090 { 0091 Q_ASSERT(color.isValid()); 0092 Q_ASSERT(colorSpace); 0093 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); 0094 0095 m_size = m_colorSpace->pixelSize(); 0096 Q_ASSERT(m_size <= MAX_PIXEL_SIZE); 0097 memset(m_data, 0, m_size); 0098 0099 m_colorSpace->fromQColor(color, m_data); 0100 } 0101 0102 KoColor::KoColor(const quint8 * data, const KoColorSpace * colorSpace) 0103 { 0104 Q_ASSERT(colorSpace); 0105 Q_ASSERT(data); 0106 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); 0107 m_size = m_colorSpace->pixelSize(); 0108 Q_ASSERT(m_size <= MAX_PIXEL_SIZE); 0109 memmove(m_data, data, m_size); 0110 } 0111 0112 0113 KoColor::KoColor(const KoColor &src, const KoColorSpace * colorSpace) 0114 { 0115 Q_ASSERT(colorSpace); 0116 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); 0117 m_size = m_colorSpace->pixelSize(); 0118 Q_ASSERT(m_size <= MAX_PIXEL_SIZE); 0119 memset(m_data, 0, m_size); 0120 0121 src.colorSpace()->convertPixelsTo(src.m_data, m_data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); 0122 } 0123 0124 bool KoColor::operator==(const KoColor &other) const { 0125 if (*colorSpace() != *other.colorSpace()) { 0126 return false; 0127 } 0128 if (m_size != other.m_size) { 0129 return false; 0130 } 0131 return memcmp(m_data, other.m_data, m_size) == 0; 0132 } 0133 0134 void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) 0135 { 0136 //dbgPigment <<"Our colormodel:" << d->colorSpace->id().name() 0137 // << ", new colormodel: " << cs->id().name() << "\n"; 0138 0139 if (*m_colorSpace == *cs) 0140 return; 0141 0142 quint8 data[MAX_PIXEL_SIZE]; 0143 const size_t size = cs->pixelSize(); 0144 Q_ASSERT(size <= MAX_PIXEL_SIZE); 0145 memset(data, 0, size); 0146 0147 m_colorSpace->convertPixelsTo(m_data, data, cs, 1, renderingIntent, conversionFlags); 0148 0149 memcpy(m_data, data, size); 0150 m_size = size; 0151 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); 0152 } 0153 0154 void KoColor::convertTo(const KoColorSpace * cs) 0155 { 0156 convertTo(cs, 0157 KoColorConversionTransformation::internalRenderingIntent(), 0158 KoColorConversionTransformation::internalConversionFlags()); 0159 } 0160 0161 KoColor KoColor::convertedTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const 0162 { 0163 KoColor result(*this); 0164 result.convertTo(cs, renderingIntent, conversionFlags); 0165 return result; 0166 } 0167 0168 KoColor KoColor::convertedTo(const KoColorSpace *cs) const 0169 { 0170 return convertedTo(cs, 0171 KoColorConversionTransformation::internalRenderingIntent(), 0172 KoColorConversionTransformation::internalConversionFlags()); 0173 } 0174 0175 void KoColor::setProfile(const KoColorProfile *profile) 0176 { 0177 const KoColorSpace *dstColorSpace = 0178 KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); 0179 if (!dstColorSpace) return; 0180 0181 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(dstColorSpace); 0182 } 0183 0184 void KoColor::setColor(const quint8 * data, const KoColorSpace * colorSpace) 0185 { 0186 Q_ASSERT(colorSpace); 0187 0188 m_size = colorSpace->pixelSize(); 0189 Q_ASSERT(m_size <= MAX_PIXEL_SIZE); 0190 0191 memcpy(m_data, data, m_size); 0192 m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); 0193 } 0194 0195 // To save the user the trouble of doing color->colorSpace()->toQColor(color->data(), &c, &a, profile 0196 void KoColor::toQColor(QColor *c) const 0197 { 0198 Q_ASSERT(c); 0199 if (m_colorSpace) { 0200 m_colorSpace->toQColor(m_data, c); 0201 } 0202 } 0203 0204 QColor KoColor::toQColor() const 0205 { 0206 QColor c; 0207 toQColor(&c); 0208 return c; 0209 } 0210 0211 void KoColor::fromQColor(const QColor& c) 0212 { 0213 if (m_colorSpace) { 0214 m_colorSpace->fromQColor(c, m_data); 0215 } 0216 } 0217 0218 void KoColor::subtract(const KoColor &value) 0219 { 0220 KIS_SAFE_ASSERT_RECOVER_RETURN(*m_colorSpace == *value.colorSpace()); 0221 0222 QVector<float> channels1(m_colorSpace->channelCount()); 0223 QVector<float> channels2(m_colorSpace->channelCount()); 0224 0225 m_colorSpace->normalisedChannelsValue(m_data, channels1); 0226 m_colorSpace->normalisedChannelsValue(value.data(), channels2); 0227 0228 for (int i = 0; i < channels1.size(); i++) { 0229 channels1[i] -= channels2[i]; 0230 } 0231 0232 m_colorSpace->fromNormalisedChannelsValue(m_data, channels1); 0233 } 0234 0235 KoColor KoColor::subtracted(const KoColor &value) const 0236 { 0237 KoColor result(*this); 0238 result.subtract(value); 0239 return result; 0240 } 0241 0242 void KoColor::add(const KoColor &value) 0243 { 0244 KIS_SAFE_ASSERT_RECOVER_RETURN(*m_colorSpace == *value.colorSpace()); 0245 0246 QVector<float> channels1(m_colorSpace->channelCount()); 0247 QVector<float> channels2(m_colorSpace->channelCount()); 0248 0249 m_colorSpace->normalisedChannelsValue(m_data, channels1); 0250 m_colorSpace->normalisedChannelsValue(value.data(), channels2); 0251 0252 for (int i = 0; i < channels1.size(); i++) { 0253 channels1[i] += channels2[i]; 0254 } 0255 0256 m_colorSpace->fromNormalisedChannelsValue(m_data, channels1); 0257 } 0258 0259 KoColor KoColor::added(const KoColor &value) const 0260 { 0261 KoColor result(*this); 0262 result.add(value); 0263 return result; 0264 } 0265 0266 #ifndef NDEBUG 0267 void KoColor::dump() const 0268 { 0269 dbgPigment <<"KoColor (" << this <<")," << m_colorSpace->id() <<""; 0270 QList<KoChannelInfo *> channels = m_colorSpace->channels(); 0271 0272 QList<KoChannelInfo *>::const_iterator begin = channels.constBegin(); 0273 QList<KoChannelInfo *>::const_iterator end = channels.constEnd(); 0274 0275 for (QList<KoChannelInfo *>::const_iterator it = begin; it != end; ++it) { 0276 KoChannelInfo * ch = (*it); 0277 // XXX: setNum always takes a byte. 0278 if (ch->size() == sizeof(quint8)) { 0279 // Byte 0280 dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(m_data[ch->pos()]) <<""; 0281 } else if (ch->size() == sizeof(quint16)) { 0282 // Short (may also by an nvidia half) 0283 dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(m_data+ch->pos()))) <<""; 0284 } else if (ch->size() == sizeof(quint32)) { 0285 // Integer (may also be float... Find out how to distinguish these!) 0286 dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(m_data+ch->pos()))) <<""; 0287 } 0288 } 0289 } 0290 #endif 0291 0292 void KoColor::fromKoColor(const KoColor& src) 0293 { 0294 src.colorSpace()->convertPixelsTo(src.m_data, m_data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); 0295 } 0296 0297 const KoColorProfile *KoColor::profile() const 0298 { 0299 return m_colorSpace->profile(); 0300 } 0301 0302 void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const 0303 { 0304 m_colorSpace->colorToXML(m_data, doc, colorElt); 0305 0306 for (QString key : m_metadata.keys()) { 0307 0308 QDomElement e = doc.createElement("metadata"); 0309 e.setAttribute("name", QString(key.toLatin1())); 0310 QVariant v = m_metadata.value(key); 0311 e.setAttribute("type", v.typeName()); 0312 0313 QString attrName = "value"; 0314 if(v.type() == QVariant::String ) { 0315 e.setAttribute(attrName, v.toString()); 0316 e.setAttribute("type", "string"); 0317 } else if(v.type() == QVariant::Int ) { 0318 e.setAttribute(attrName, v.toInt()); 0319 } else if(v.type() == QVariant::Double ) { 0320 e.setAttribute(attrName, v.toDouble()); 0321 } else if(v.type() == QVariant::Bool ) { 0322 e.setAttribute(attrName, v.toBool()); 0323 } else { 0324 qWarning() << "no KoColor serialization for QVariant type:" << v.type(); 0325 } 0326 colorElt.appendChild(e); 0327 } 0328 0329 } 0330 0331 void KoColor::setOpacity(quint8 alpha) 0332 { 0333 m_colorSpace->setOpacity(m_data, alpha, 1); 0334 } 0335 void KoColor::setOpacity(qreal alpha) 0336 { 0337 m_colorSpace->setOpacity(m_data, alpha, 1); 0338 } 0339 quint8 KoColor::opacityU8() const 0340 { 0341 return m_colorSpace->opacityU8(m_data); 0342 } 0343 qreal KoColor::opacityF() const 0344 { 0345 return m_colorSpace->opacityF(m_data); 0346 } 0347 0348 KoColor KoColor::fromXML(const QDomElement& elt, const QString& channelDepthId) 0349 { 0350 bool ok; 0351 return fromXML(elt, channelDepthId, &ok); 0352 } 0353 0354 KoColor KoColor::fromXML(const QDomElement &elt, const QString &channelDepthId, bool *ok) 0355 { 0356 *ok = true; 0357 0358 QString modelId; 0359 QString modelName = elt.tagName(); 0360 if (modelName == "CMYK") { 0361 modelId = CMYKAColorModelID.id(); 0362 } else if (modelName == "RGB") { 0363 modelId = RGBAColorModelID.id(); 0364 } else if (modelName == "sRGB") { 0365 modelId = RGBAColorModelID.id(); 0366 } else if (modelName == "Lab") { 0367 modelId = LABAColorModelID.id(); 0368 } else if (modelName == "XYZ") { 0369 modelId = XYZAColorModelID.id(); 0370 } else if (modelName == "Gray") { 0371 modelId = GrayAColorModelID.id(); 0372 } else if (modelName == "YCbCr") { 0373 modelId = YCbCrAColorModelID.id(); 0374 } 0375 0376 KoColorSpaceRegistry *colorSpaceRegistry = KoColorSpaceRegistry::instance(); 0377 0378 QString profileName; 0379 if (modelName == "sRGB") { 0380 const KoColorProfile *profile = colorSpaceRegistry->p709SRGBProfile(); 0381 if (profile) { 0382 profileName = profile->name(); 0383 } 0384 } else { 0385 profileName = elt.attribute("space", ""); 0386 if (!colorSpaceRegistry->profileByName(profileName)) { 0387 profileName.clear(); 0388 } 0389 } 0390 0391 const KoColorSpace* cs = colorSpaceRegistry->colorSpace(modelId, channelDepthId, profileName); 0392 if (!cs) { 0393 QList<KoID> list = colorSpaceRegistry->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces); 0394 if (!list.empty()) { 0395 cs = colorSpaceRegistry->colorSpace(modelId, list[0].id(), profileName); 0396 } 0397 } 0398 0399 if (!cs) { 0400 *ok = false; 0401 return KoColor(); 0402 } 0403 0404 KoColor c(cs); 0405 // TODO: Provide a way for colorFromXML() to notify the caller if parsing failed. 0406 // Currently it returns default values on failure. 0407 cs->colorFromXML(c.data(), elt); 0408 0409 QDomElement e = elt.nextSiblingElement("metadata"); 0410 for (; !e.isNull(); e = e.nextSiblingElement("metadata")) { 0411 const QString name = e.attribute("name"); 0412 const QString type = e.attribute("type"); 0413 const QString value = e.attribute("value"); 0414 0415 QVariant v; 0416 if (type == "string") { 0417 v = KisDomUtils::toString(value); 0418 } else if (type == "int") { 0419 v = KisDomUtils::toInt(value); 0420 } else if (type == "double") { 0421 v = KisDomUtils::toDouble(value); 0422 } else if (type == "bool") { 0423 v = KisDomUtils::toInt(value); 0424 } else { 0425 continue; 0426 } 0427 0428 c.addMetadata(name , v); 0429 } 0430 0431 return c; 0432 } 0433 0434 QString KoColor::toXML() const 0435 { 0436 QDomDocument cdataDoc = QDomDocument("color"); 0437 QDomElement cdataRoot = cdataDoc.createElement("color"); 0438 cdataDoc.appendChild(cdataRoot); 0439 cdataRoot.setAttribute("channeldepth", colorSpace()->colorDepthId().id()); 0440 toXML(cdataDoc, cdataRoot); 0441 return cdataDoc.toString(); 0442 } 0443 0444 KoColor KoColor::fromXML(const QString &xml) 0445 { 0446 KoColor c; 0447 QDomDocument doc; 0448 if (!doc.setContent(xml)) { 0449 return c; 0450 } 0451 0452 QDomElement root = doc.documentElement(); 0453 QDomElement child = root.firstChildElement(); 0454 QString channelDepthID = root.attribute("channeldepth", Integer16BitsColorDepthID.id()); 0455 0456 bool ok; 0457 if (child.hasAttribute("space") || child.tagName().toLower() == "srgb") { 0458 c = KoColor::fromXML(child, channelDepthID, &ok); 0459 } else if (root.hasAttribute("space") || root.tagName().toLower() == "srgb"){ 0460 c = KoColor::fromXML(root, channelDepthID, &ok); 0461 } else { 0462 qWarning() << "Cannot parse color from xml" << xml; 0463 } 0464 0465 return c; 0466 } 0467 0468 QString KoColor::toSVG11(QHash<QString, const KoColorProfile *> *profileList) const 0469 { 0470 QStringList colorDefinitions; 0471 colorDefinitions.append(toQColor().name()); 0472 0473 QVector<float> channelValues(colorSpace()->channelCount()); 0474 channelValues.fill(0.0); 0475 colorSpace()->normalisedChannelsValue(data(), channelValues); 0476 0477 bool sRGB = false; 0478 if (colorSpace() && colorSpace()->profile() 0479 && colorSpace()->profile()->getColorPrimaries() == ColorPrimaries::PRIMARIES_ITU_R_BT_709_5 0480 && colorSpace()->profile()->getTransferCharacteristics() != TransferCharacteristics::TRC_LINEAR) { 0481 sRGB = true; 0482 } 0483 0484 // We don't write a icc-color definition for XYZ and 8bit sRGB. 0485 if (!(sRGB && colorSpace()->colorDepthId() == Integer8BitsColorDepthID) && 0486 colorSpace()->colorModelId() != XYZAColorModelID) { 0487 QStringList iccColor; 0488 QString csName = colorSpace()->profile()->name(); 0489 // remove forbidden characters 0490 // https://www.w3.org/TR/SVG11/types.html#DataTypeName 0491 csName.remove(QRegExp("[\\(\\),\\s]")); 0492 0493 //reuse existing name if possible. We're looking for the color profile, because svg doesn't care about depth. 0494 csName = profileList->key(colorSpace()->profile(), csName); 0495 0496 if (sRGB) { 0497 csName = "sRGB"; 0498 } 0499 0500 iccColor.append(csName); 0501 0502 if (colorSpace()->colorModelId() == LABAColorModelID) { 0503 QDomDocument doc; 0504 QDomElement el = doc.createElement("color"); 0505 toXML(doc, el); 0506 QDomElement lab = el.firstChildElement(); 0507 iccColor.append(lab.attribute("L", "0.0")); 0508 iccColor.append(lab.attribute("a", "0.0")); 0509 iccColor.append(lab.attribute("b", "0.0")); 0510 } 0511 else { 0512 for (int i = 0; i < channelValues.size(); i++) { 0513 int location = KoChannelInfo::displayPositionToChannelIndex(i, colorSpace()->channels()); 0514 if (i != int(colorSpace()->alphaPos())) { 0515 iccColor.append(QString::number(channelValues.at(location), 'g', 10)); 0516 } 0517 } 0518 } 0519 colorDefinitions.append(QString("icc-color(%1)").arg(iccColor.join(", "))); 0520 if (!profileList->contains(csName) && !sRGB) { 0521 profileList->insert(csName, colorSpace()->profile()); 0522 } 0523 } 0524 0525 return colorDefinitions.join(" "); 0526 } 0527 0528 KoColor KoColor::fromSVG11(const QString value, QHash<QString, const KoColorProfile *> profileList, KoColor current) 0529 { 0530 KoColor parsed(KoColorSpaceRegistry::instance()->rgb16(KoColorSpaceRegistry::instance()->p709SRGBProfile())); 0531 parsed.setOpacity(1.0); 0532 0533 if (value.toLower() == "none") { 0534 return parsed; 0535 } 0536 0537 // add the sRGB default name. 0538 profileList.insert("sRGB", KoColorSpaceRegistry::instance()->p709SRGBProfile()); 0539 // first, try to split at \w\d\) space. 0540 // we want to split up a string like... colorcolor none rgb(0.8, 0.1, 200%) #ff0000 icc-color(blah, 0.0, 1.0, 1.0, 0.0); 0541 QRegExp splitDefinitions("(#?\\w+|[\\w\\-]*\\(.+\\))\\s"); 0542 int pos = 0; 0543 int pos2 = 0; 0544 QStringList colorDefinitions; 0545 QString valueAdjust = value.split(";").first(); 0546 valueAdjust.append(" "); 0547 while ((pos2 = splitDefinitions.indexIn(valueAdjust, pos)) != -1) { 0548 colorDefinitions.append(splitDefinitions.cap(1).trimmed()); 0549 pos = pos2 + splitDefinitions.matchedLength(); 0550 } 0551 if (pos < value.length()) { 0552 QString remainder = value.right(value.length()-pos); 0553 remainder.remove(";"); 0554 colorDefinitions.append(remainder); 0555 } 0556 dbgPigment << "Color definitions found during svg11parsing" << colorDefinitions; 0557 0558 for (QString def : colorDefinitions) { 0559 if (def.toLower() == "currentcolor") { 0560 parsed = current; 0561 } else if (QColor::isValidColor(def)) { 0562 parsed.fromQColor(QColor(def)); 0563 } else if (def.toLower().startsWith("rgb")) { 0564 QString parse = def.trimmed(); 0565 QStringList colors = parse.split(','); 0566 QString r = colors[0].right((colors[0].length() - 4)).trimmed(); 0567 QString g = colors[1].trimmed(); 0568 QString b = colors[2].left((colors[2].length() - 1)).trimmed(); 0569 0570 if (r.contains('%')) { 0571 r = r.left(r.length() - 1); 0572 r = QString::number(int((double(255 * r.toDouble()) / 100.0))); 0573 } 0574 0575 if (g.contains('%')) { 0576 g = g.left(g.length() - 1); 0577 g = QString::number(int((double(255 * g.toDouble()) / 100.0))); 0578 } 0579 0580 if (b.contains('%')) { 0581 b = b.left(b.length() - 1); 0582 b = QString::number(int((double(255 * b.toDouble()) / 100.0))); 0583 } 0584 parsed.fromQColor(QColor(r.toInt(), g.toInt(), b.toInt())); 0585 0586 } else if (def.toLower().startsWith("icc-color")) { 0587 QStringList values = def.split(","); 0588 QString iccprofilename = values.first().split("(").last(); 0589 values.removeFirst(); 0590 0591 // svg11 docs say that searching the name should be caseinsentive. 0592 QStringList entry = QStringList(profileList.keys()).filter(iccprofilename, Qt::CaseInsensitive); 0593 if (entry.empty()) { 0594 continue; 0595 } 0596 const KoColorProfile *profile = profileList.value(entry.first()); 0597 if (!profile) { 0598 continue; 0599 } 0600 QString colormodel = profile->colorModelID(); 0601 QString depth = "F32"; 0602 if (colormodel == LABAColorModelID.id()) { 0603 // let our xml handling deal with lab 0604 QVector<float> labV(3); 0605 for (int i = 0; i < values.size(); i++) { 0606 if (i<labV.size()) { 0607 QString entry = values.at(i); 0608 entry = entry.split(")").first(); 0609 labV[i] = entry.toDouble(); 0610 } 0611 } 0612 QString lab = QString("<Lab space='%1' L='%2' a='%3' b='%4' />") 0613 .arg(profile->name()) 0614 .arg(labV[0]) 0615 .arg(labV[1]) 0616 .arg(labV[2]); 0617 QDomDocument doc; 0618 doc.setContent(lab); 0619 parsed = KoColor::fromXML(doc.documentElement(), "U16"); 0620 continue; 0621 } else if (colormodel == CMYKAColorModelID.id()) { 0622 depth = "U16"; 0623 } else if (colormodel == XYZAColorModelID.id()) { 0624 // Inkscape decided to have X and Z go from 0 to 2, and I can't for the live of me figure out why. 0625 // So we're just not parsing XYZ. 0626 continue; 0627 } 0628 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->colorSpace(colormodel, depth, profile); 0629 if (!cs) { 0630 continue; 0631 } 0632 parsed = KoColor(cs); 0633 QVector<float> channelValues(parsed.colorSpace()->channelCount()); 0634 channelValues.fill(0.0); 0635 channelValues[parsed.colorSpace()->alphaPos()] = 1.0; 0636 for (int channel = 0; channel < values.size(); channel++) { 0637 int location = KoChannelInfo::displayPositionToChannelIndex(channel, parsed.colorSpace()->channels()); 0638 QString entry = values.at(channel); 0639 entry = entry.split(")").first(); 0640 channelValues[location] = entry.toFloat(); 0641 } 0642 parsed.colorSpace()->fromNormalisedChannelsValue(parsed.data(), channelValues); 0643 } 0644 } 0645 0646 return parsed; 0647 } 0648 0649 QString KoColor::toQString(const KoColor &color) 0650 { 0651 QStringList ls; 0652 Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(color.colorSpace()->channels())) { 0653 int realIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), color.colorSpace()->channels()); 0654 ls << channel->name(); 0655 ls << color.colorSpace()->channelValueText(color.data(), realIndex); 0656 } 0657 return ls.join(" "); 0658 } 0659 0660 void KoColor::addMetadata(QString key, QVariant value) 0661 { 0662 m_metadata.insert(key, value); 0663 } 0664 0665 QMap<QString, QVariant> KoColor::metadata() const 0666 { 0667 return m_metadata; 0668 } 0669 0670 void KoColor::clearMetadata() 0671 { 0672 m_metadata.clear(); 0673 } 0674 0675 KoColor KoColor::createTransparent(const KoColorSpace *cs) 0676 { 0677 KoColor result; 0678 0679 result.m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); 0680 result.m_size = cs->pixelSize(); 0681 cs->transparentColor(result.m_data, 1); 0682 0683 return result; 0684 } 0685 0686 QDebug operator<<(QDebug dbg, const KoColor &color) 0687 { 0688 dbg.nospace() << "KoColor (" << color.colorSpace()->id(); 0689 0690 const QList<KoChannelInfo*> channels = color.colorSpace()->channels(); 0691 for (auto it = channels.constBegin(); it != channels.constEnd(); ++it) { 0692 0693 KoChannelInfo *ch = (*it); 0694 0695 dbg.nospace() << ", " << ch->name() << ":"; 0696 0697 switch (ch->channelValueType()) { 0698 case KoChannelInfo::UINT8: { 0699 const quint8 *ptr = reinterpret_cast<const quint8*>(color.data() + ch->pos()); 0700 dbg.nospace() << *ptr; 0701 break; 0702 } case KoChannelInfo::UINT16: { 0703 const quint16 *ptr = reinterpret_cast<const quint16*>(color.data() + ch->pos()); 0704 dbg.nospace() << *ptr; 0705 break; 0706 } case KoChannelInfo::UINT32: { 0707 const quint32 *ptr = reinterpret_cast<const quint32*>(color.data() + ch->pos()); 0708 dbg.nospace() << *ptr; 0709 break; 0710 } case KoChannelInfo::FLOAT16: { 0711 0712 #ifdef HAVE_OPENEXR 0713 const half *ptr = reinterpret_cast<const half*>(color.data() + ch->pos()); 0714 dbg.nospace() << *ptr; 0715 #else 0716 const quint16 *ptr = reinterpret_cast<const quint16*>(color.data() + ch->pos()); 0717 dbg.nospace() << "UNSUPPORTED_F16(" << *ptr << ")"; 0718 #endif 0719 break; 0720 } case KoChannelInfo::FLOAT32: { 0721 const float *ptr = reinterpret_cast<const float*>(color.data() + ch->pos()); 0722 dbg.nospace() << *ptr; 0723 break; 0724 } case KoChannelInfo::FLOAT64: { 0725 const double *ptr = reinterpret_cast<const double*>(color.data() + ch->pos()); 0726 dbg.nospace() << *ptr; 0727 break; 0728 } case KoChannelInfo::INT8: { 0729 const qint8 *ptr = reinterpret_cast<const qint8*>(color.data() + ch->pos()); 0730 dbg.nospace() << *ptr; 0731 break; 0732 } case KoChannelInfo::INT16: { 0733 const qint16 *ptr = reinterpret_cast<const qint16*>(color.data() + ch->pos()); 0734 dbg.nospace() << *ptr; 0735 break; 0736 } case KoChannelInfo::OTHER: { 0737 const quint8 *ptr = reinterpret_cast<const quint8*>(color.data() + ch->pos()); 0738 dbg.nospace() << "undef(" << *ptr << ")"; 0739 break; 0740 } 0741 } 0742 } 0743 dbg.nospace() << ")"; 0744 return dbg.space(); 0745 }