File indexing completed on 2024-05-12 15:59:31
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 QString modelId; 0358 if (elt.tagName() == "CMYK") { 0359 modelId = CMYKAColorModelID.id(); 0360 } else if (elt.tagName() == "RGB") { 0361 modelId = RGBAColorModelID.id(); 0362 } else if (elt.tagName() == "sRGB") { 0363 modelId = RGBAColorModelID.id(); 0364 } else if (elt.tagName() == "Lab") { 0365 modelId = LABAColorModelID.id(); 0366 } else if (elt.tagName() == "XYZ") { 0367 modelId = XYZAColorModelID.id(); 0368 } else if (elt.tagName() == "Gray") { 0369 modelId = GrayAColorModelID.id(); 0370 } else if (elt.tagName() == "YCbCr") { 0371 modelId = YCbCrAColorModelID.id(); 0372 } 0373 QString profileName; 0374 if (elt.tagName() != "sRGB") { 0375 profileName = elt.attribute("space", ""); 0376 if (!KoColorSpaceRegistry::instance()->profileByName(profileName)) { 0377 profileName.clear(); 0378 } 0379 } else { 0380 const KoColorProfile *profile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); 0381 if (profile) { 0382 profileName = profile->name(); 0383 } 0384 } 0385 const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, channelDepthId, profileName); 0386 if (cs == 0) { 0387 QList<KoID> list = KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces); 0388 if (!list.empty()) { 0389 cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, list[0].id(), profileName); 0390 } 0391 } 0392 if (cs) { 0393 KoColor c(cs); 0394 // TODO: Provide a way for colorFromXML() to notify the caller if parsing failed. Currently it returns default values on failure. 0395 cs->colorFromXML(c.data(), elt); 0396 0397 QDomElement e = elt; 0398 while (!e.nextSiblingElement("metadata").isNull()) { 0399 e = e.nextSiblingElement("metadata"); 0400 0401 const QString name = e.attribute("name"); 0402 const QString type = e.attribute("type"); 0403 const QString value = e.text(); 0404 QVariant v; 0405 if (type == "string") { 0406 v = KisDomUtils::toString(e.attribute("value")); 0407 c.addMetadata(name , v); 0408 } else if (type == "int") { 0409 v = KisDomUtils::toInt(e.attribute("value")); 0410 c.addMetadata(name , v); 0411 } else if (type == "double") { 0412 v = KisDomUtils::toDouble(e.attribute("value")); 0413 c.addMetadata(name , v); 0414 } else if (type == "bool") { 0415 v = KisDomUtils::toInt(e.attribute("value")); 0416 c.addMetadata(name , v); 0417 } 0418 0419 } 0420 return c; 0421 } else { 0422 *ok = false; 0423 return KoColor(); 0424 } 0425 } 0426 0427 QString KoColor::toXML() const 0428 { 0429 QDomDocument cdataDoc = QDomDocument("color"); 0430 QDomElement cdataRoot = cdataDoc.createElement("color"); 0431 cdataDoc.appendChild(cdataRoot); 0432 cdataRoot.setAttribute("channeldepth", colorSpace()->colorDepthId().id()); 0433 toXML(cdataDoc, cdataRoot); 0434 return cdataDoc.toString(); 0435 } 0436 0437 KoColor KoColor::fromXML(const QString &xml) 0438 { 0439 KoColor c; 0440 QDomDocument doc; 0441 if (doc.setContent(xml)) { 0442 QDomElement e = doc.documentElement().firstChild().toElement(); 0443 QString channelDepthID = doc.documentElement().attribute("channeldepth", Integer16BitsColorDepthID.id()); 0444 bool ok; 0445 if (e.hasAttribute("space") || e.tagName().toLower() == "srgb") { 0446 c = KoColor::fromXML(e, channelDepthID, &ok); 0447 } else if (doc.documentElement().hasAttribute("space") || doc.documentElement().tagName().toLower() == "srgb"){ 0448 c = KoColor::fromXML(doc.documentElement(), channelDepthID, &ok); 0449 } else { 0450 qWarning() << "Cannot parse color from xml" << xml; 0451 } 0452 } 0453 return c; 0454 } 0455 0456 QString KoColor::toSVG11(QHash<QString, const KoColorProfile *> *profileList) const 0457 { 0458 QStringList colorDefinitions; 0459 colorDefinitions.append(toQColor().name()); 0460 0461 QVector<float> channelValues(colorSpace()->channelCount()); 0462 channelValues.fill(0.0); 0463 colorSpace()->normalisedChannelsValue(data(), channelValues); 0464 0465 bool sRGB = false; 0466 if (colorSpace() && colorSpace()->profile() 0467 && colorSpace()->profile()->getColorPrimaries() == ColorPrimaries::PRIMARIES_ITU_R_BT_709_5 0468 && colorSpace()->profile()->getTransferCharacteristics() != TransferCharacteristics::TRC_LINEAR) { 0469 sRGB = true; 0470 } 0471 0472 // We don't write a icc-color definition for XYZ and 8bit sRGB. 0473 if (!(sRGB && colorSpace()->colorDepthId() == Integer8BitsColorDepthID) && 0474 colorSpace()->colorModelId() != XYZAColorModelID) { 0475 QStringList iccColor; 0476 QString csName = colorSpace()->profile()->name(); 0477 // remove forbidden characters 0478 // https://www.w3.org/TR/SVG11/types.html#DataTypeName 0479 csName.remove(QRegExp("[\\(\\),\\s]")); 0480 0481 //reuse existing name if possible. We're looking for the color profile, because svg doesn't care about depth. 0482 csName = profileList->key(colorSpace()->profile(), csName); 0483 0484 if (sRGB) { 0485 csName = "sRGB"; 0486 } 0487 0488 iccColor.append(csName); 0489 0490 if (colorSpace()->colorModelId() == LABAColorModelID) { 0491 QDomDocument doc; 0492 QDomElement el = doc.createElement("color"); 0493 toXML(doc, el); 0494 QDomElement lab = el.firstChildElement(); 0495 iccColor.append(lab.attribute("L", "0.0")); 0496 iccColor.append(lab.attribute("a", "0.0")); 0497 iccColor.append(lab.attribute("b", "0.0")); 0498 } 0499 else { 0500 for (int i = 0; i < channelValues.size(); i++) { 0501 int location = KoChannelInfo::displayPositionToChannelIndex(i, colorSpace()->channels()); 0502 if (i != int(colorSpace()->alphaPos())) { 0503 iccColor.append(QString::number(channelValues.at(location), 'g', 10)); 0504 } 0505 } 0506 } 0507 colorDefinitions.append(QString("icc-color(%1)").arg(iccColor.join(", "))); 0508 if (!profileList->contains(csName) && !sRGB) { 0509 profileList->insert(csName, colorSpace()->profile()); 0510 } 0511 } 0512 0513 return colorDefinitions.join(" "); 0514 } 0515 0516 KoColor KoColor::fromSVG11(const QString value, QHash<QString, const KoColorProfile *> profileList, KoColor current) 0517 { 0518 KoColor parsed(KoColorSpaceRegistry::instance()->rgb16(KoColorSpaceRegistry::instance()->p709SRGBProfile())); 0519 parsed.setOpacity(1.0); 0520 0521 if (value.toLower() == "none") { 0522 return parsed; 0523 } 0524 0525 // add the sRGB default name. 0526 profileList.insert("sRGB", KoColorSpaceRegistry::instance()->p709SRGBProfile()); 0527 // first, try to split at \w\d\) space. 0528 // 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); 0529 QRegExp splitDefinitions("(#?\\w+|[\\w\\-]*\\(.+\\))\\s"); 0530 int pos = 0; 0531 int pos2 = 0; 0532 QStringList colorDefinitions; 0533 QString valueAdjust = value.split(";").first(); 0534 valueAdjust.append(" "); 0535 while ((pos2 = splitDefinitions.indexIn(valueAdjust, pos)) != -1) { 0536 colorDefinitions.append(splitDefinitions.cap(1).trimmed()); 0537 pos = pos2 + splitDefinitions.matchedLength(); 0538 } 0539 if (pos < value.length()) { 0540 QString remainder = value.right(value.length()-pos); 0541 remainder.remove(";"); 0542 colorDefinitions.append(remainder); 0543 } 0544 dbgPigment << "Color definitions found during svg11parsing" << colorDefinitions; 0545 0546 for (QString def : colorDefinitions) { 0547 if (def.toLower() == "currentcolor") { 0548 parsed = current; 0549 } else if (QColor::isValidColor(def)) { 0550 parsed.fromQColor(QColor(def)); 0551 } else if (def.toLower().startsWith("rgb")) { 0552 QString parse = def.trimmed(); 0553 QStringList colors = parse.split(','); 0554 QString r = colors[0].right((colors[0].length() - 4)).trimmed(); 0555 QString g = colors[1].trimmed(); 0556 QString b = colors[2].left((colors[2].length() - 1)).trimmed(); 0557 0558 if (r.contains('%')) { 0559 r = r.left(r.length() - 1); 0560 r = QString::number(int((double(255 * r.toDouble()) / 100.0))); 0561 } 0562 0563 if (g.contains('%')) { 0564 g = g.left(g.length() - 1); 0565 g = QString::number(int((double(255 * g.toDouble()) / 100.0))); 0566 } 0567 0568 if (b.contains('%')) { 0569 b = b.left(b.length() - 1); 0570 b = QString::number(int((double(255 * b.toDouble()) / 100.0))); 0571 } 0572 parsed.fromQColor(QColor(r.toInt(), g.toInt(), b.toInt())); 0573 0574 } else if (def.toLower().startsWith("icc-color")) { 0575 QStringList values = def.split(","); 0576 QString iccprofilename = values.first().split("(").last(); 0577 values.removeFirst(); 0578 0579 // svg11 docs say that searching the name should be caseinsentive. 0580 QStringList entry = QStringList(profileList.keys()).filter(iccprofilename, Qt::CaseInsensitive); 0581 if (entry.empty()) { 0582 continue; 0583 } 0584 const KoColorProfile *profile = profileList.value(entry.first()); 0585 if (!profile) { 0586 continue; 0587 } 0588 QString colormodel = profile->colorModelID(); 0589 QString depth = "F32"; 0590 if (colormodel == LABAColorModelID.id()) { 0591 // let our xml handling deal with lab 0592 QVector<float> labV(3); 0593 for (int i = 0; i < values.size(); i++) { 0594 if (i<labV.size()) { 0595 QString entry = values.at(i); 0596 entry = entry.split(")").first(); 0597 labV[i] = entry.toDouble(); 0598 } 0599 } 0600 QString lab = QString("<Lab space='%1' L='%2' a='%3' b='%4' />") 0601 .arg(profile->name()) 0602 .arg(labV[0]) 0603 .arg(labV[1]) 0604 .arg(labV[2]); 0605 QDomDocument doc; 0606 doc.setContent(lab); 0607 parsed = KoColor::fromXML(doc.documentElement(), "U16"); 0608 continue; 0609 } else if (colormodel == CMYKAColorModelID.id()) { 0610 depth = "U16"; 0611 } else if (colormodel == XYZAColorModelID.id()) { 0612 // Inkscape decided to have X and Z go from 0 to 2, and I can't for the live of me figure out why. 0613 // So we're just not parsing XYZ. 0614 continue; 0615 } 0616 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->colorSpace(colormodel, depth, profile); 0617 if (!cs) { 0618 continue; 0619 } 0620 parsed = KoColor(cs); 0621 QVector<float> channelValues(parsed.colorSpace()->channelCount()); 0622 channelValues.fill(0.0); 0623 channelValues[parsed.colorSpace()->alphaPos()] = 1.0; 0624 for (int channel = 0; channel < values.size(); channel++) { 0625 int location = KoChannelInfo::displayPositionToChannelIndex(channel, parsed.colorSpace()->channels()); 0626 QString entry = values.at(channel); 0627 entry = entry.split(")").first(); 0628 channelValues[location] = entry.toFloat(); 0629 } 0630 parsed.colorSpace()->fromNormalisedChannelsValue(parsed.data(), channelValues); 0631 } 0632 } 0633 0634 return parsed; 0635 } 0636 0637 QString KoColor::toQString(const KoColor &color) 0638 { 0639 QStringList ls; 0640 Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(color.colorSpace()->channels())) { 0641 int realIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), color.colorSpace()->channels()); 0642 ls << channel->name(); 0643 ls << color.colorSpace()->channelValueText(color.data(), realIndex); 0644 } 0645 return ls.join(" "); 0646 } 0647 0648 void KoColor::addMetadata(QString key, QVariant value) 0649 { 0650 m_metadata.insert(key, value); 0651 } 0652 0653 QMap<QString, QVariant> KoColor::metadata() const 0654 { 0655 return m_metadata; 0656 } 0657 0658 void KoColor::clearMetadata() 0659 { 0660 m_metadata.clear(); 0661 } 0662 0663 QDebug operator<<(QDebug dbg, const KoColor &color) 0664 { 0665 dbg.nospace() << "KoColor (" << color.colorSpace()->id(); 0666 0667 QList<KoChannelInfo*> channels = color.colorSpace()->channels(); 0668 for (auto it = channels.constBegin(); it != channels.constEnd(); ++it) { 0669 0670 KoChannelInfo *ch = (*it); 0671 0672 dbg.nospace() << ", " << ch->name() << ":"; 0673 0674 switch (ch->channelValueType()) { 0675 case KoChannelInfo::UINT8: { 0676 const quint8 *ptr = reinterpret_cast<const quint8*>(color.data() + ch->pos()); 0677 dbg.nospace() << *ptr; 0678 break; 0679 } case KoChannelInfo::UINT16: { 0680 const quint16 *ptr = reinterpret_cast<const quint16*>(color.data() + ch->pos()); 0681 dbg.nospace() << *ptr; 0682 break; 0683 } case KoChannelInfo::UINT32: { 0684 const quint32 *ptr = reinterpret_cast<const quint32*>(color.data() + ch->pos()); 0685 dbg.nospace() << *ptr; 0686 break; 0687 } case KoChannelInfo::FLOAT16: { 0688 0689 #ifdef HAVE_OPENEXR 0690 const half *ptr = reinterpret_cast<const half*>(color.data() + ch->pos()); 0691 dbg.nospace() << *ptr; 0692 #else 0693 const quint16 *ptr = reinterpret_cast<const quint16*>(color.data() + ch->pos()); 0694 dbg.nospace() << "UNSUPPORTED_F16(" << *ptr << ")"; 0695 #endif 0696 break; 0697 } case KoChannelInfo::FLOAT32: { 0698 const float *ptr = reinterpret_cast<const float*>(color.data() + ch->pos()); 0699 dbg.nospace() << *ptr; 0700 break; 0701 } case KoChannelInfo::FLOAT64: { 0702 const double *ptr = reinterpret_cast<const double*>(color.data() + ch->pos()); 0703 dbg.nospace() << *ptr; 0704 break; 0705 } case KoChannelInfo::INT8: { 0706 const qint8 *ptr = reinterpret_cast<const qint8*>(color.data() + ch->pos()); 0707 dbg.nospace() << *ptr; 0708 break; 0709 } case KoChannelInfo::INT16: { 0710 const qint16 *ptr = reinterpret_cast<const qint16*>(color.data() + ch->pos()); 0711 dbg.nospace() << *ptr; 0712 break; 0713 } case KoChannelInfo::OTHER: { 0714 const quint8 *ptr = reinterpret_cast<const quint8*>(color.data() + ch->pos()); 0715 dbg.nospace() << "undef(" << *ptr << ")"; 0716 break; 0717 } 0718 } 0719 } 0720 dbg.nospace() << ")"; 0721 return dbg.space(); 0722 }