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 }