Warning, file /office/calligra/libs/pigment/KoColorSpace.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KoColorSpace.h"
0021 #include "KoColorSpace_p.h"
0022 
0023 #include "KoChannelInfo.h"
0024 #include "DebugPigment.h"
0025 #include "KoCompositeOp.h"
0026 #include "KoColorTransformation.h"
0027 #include "KoColorTransformationFactory.h"
0028 #include "KoColorTransformationFactoryRegistry.h"
0029 #include "KoColorConversionCache.h"
0030 #include "KoColorConversionSystem.h"
0031 #include "KoColorSpaceRegistry.h"
0032 #include "KoColorProfile.h"
0033 #include "KoCopyColorConversionTransformation.h"
0034 #include "KoFallBackColorTransformation.h"
0035 #include "KoUniqueNumberForIdServer.h"
0036 #include "KoMixColorsOp.h"
0037 #include "KoConvolutionOp.h"
0038 #include "KoCompositeOpRegistry.h"
0039 
0040 #include <QThreadStorage>
0041 #include <QByteArray>
0042 #include <QBitArray>
0043 #include <QPolygonF>
0044 #include <QPointF>
0045 
0046 #include <math.h>
0047 
0048 KoColorSpace::KoColorSpace()
0049         : d(new Private())
0050 {
0051 }
0052 
0053 KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp)
0054         : d(new Private())
0055 {
0056     d->id = id;
0057     d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id);
0058     d->name = name;
0059     d->mixColorsOp = mixColorsOp;
0060     d->convolutionOp = convolutionOp;
0061     d->transfoToRGBA16 = 0;
0062     d->transfoFromRGBA16 = 0;
0063     d->transfoToLABA16 = 0;
0064     d->transfoFromLABA16 = 0;
0065     d->gamutXYY = QPolygonF();
0066     d->TRCXYY = QPolygonF();
0067     d->colorants = QVector <qreal> (0);
0068     d->lumaCoefficients = QVector <qreal> (0);
0069     d->deletability = NotOwnedByRegistry;
0070 }
0071 
0072 KoColorSpace::~KoColorSpace()
0073 {
0074     Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete);
0075 
0076     qDeleteAll(d->compositeOps);
0077     foreach(KoChannelInfo * channel, d->channels) {
0078         delete channel;
0079     }
0080     if (d->deletability == NotOwnedByRegistry) {
0081         KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache();
0082         if (cache) {
0083             cache->colorSpaceIsDestroyed(this);
0084         }
0085     }
0086     delete d->mixColorsOp;
0087     delete d->convolutionOp;
0088     delete d->transfoToRGBA16;
0089     delete d->transfoFromRGBA16;
0090     delete d->transfoToLABA16;
0091     delete d->transfoFromLABA16;
0092     delete d;
0093 }
0094 
0095 bool KoColorSpace::operator==(const KoColorSpace& rhs) const
0096 {
0097     const KoColorProfile* p1 = rhs.profile();
0098     const KoColorProfile* p2 = profile();
0099     return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2));
0100 }
0101 
0102 QString KoColorSpace::id() const
0103 {
0104     return d->id;
0105 }
0106 
0107 QString KoColorSpace::name() const
0108 {
0109     return d->name;
0110 }
0111 
0112 //Color space info stuff.
0113 QPolygonF KoColorSpace::gamutXYY() const
0114 {
0115     if (d->gamutXYY.empty()) {
0116         //now, let's decide on the boundary. This is a bit tricky because icc profiles can be both matrix-shaper and cLUT at once if the maker so pleases.
0117         //first make a list of colors.
0118         qreal max = 1.0;
0119         if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") {
0120             //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general.
0121             max = this->channels().at(0)->getUIMax();
0122             
0123         }
0124         int samples = 5;//amount of samples in our color space.
0125         QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF32")->defaultProfile();
0126         const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32", name);
0127         quint8 *data = new quint8[pixelSize()];
0128         quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel.
0129         //QVector <qreal> sampleCoordinates(pow(colorChannelCount(),samples));
0130         //sampleCoordinates.fill(0.0);
0131 
0132         // This is fixed to 5 since the maximum number of channels are 5 for CMYKA
0133         QVector <qreal> channelValues(5);//for getting the coordinates.
0134 
0135         for(int x=0;x<samples;x++){
0136             if (colorChannelCount()==1) {//gray
0137                 channelValues[0]=(max/(samples-1))*(x);
0138                 channelValues[1]=max;
0139                 fromNormalisedChannelsValue(data, channelValues);
0140                 convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
0141                 xyzColorSpace->normalisedChannelsValue(data2, channelValues);
0142                 qreal x = channelValues[0]/(channelValues[0]+channelValues[1]+channelValues[2]);
0143                 qreal y = channelValues[1]/(channelValues[0]+channelValues[1]+channelValues[2]);
0144                 d->gamutXYY << QPointF(x,y);
0145             } else {
0146                 for(int y=0;y<samples;y++){
0147                     for(int z=0;z<samples;z++){
0148                         if (colorChannelCount()==4) {
0149                             for(int k=0;k<samples;k++){
0150                                 channelValues[0] = (max / (samples - 1)) * (x);
0151                                 channelValues[1] = (max / (samples - 1)) * (y);
0152                                 channelValues[2] = (max / (samples - 1)) * (z);
0153                                 channelValues[3] = (max / (samples - 1)) * (k);
0154                                 channelValues[4] = max;
0155                                 fromNormalisedChannelsValue(data, channelValues);
0156                                 convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags());
0157                                 xyzColorSpace->normalisedChannelsValue(data2, channelValues);
0158                                 qreal x = channelValues[0] / (channelValues[0] + channelValues[1] + channelValues[2]);
0159                                 qreal y = channelValues[1] / (channelValues[0] + channelValues[1] + channelValues[2]);
0160                                 d->gamutXYY<< QPointF(x,y);
0161                             }
0162                         } else {
0163                             channelValues[0]=(max/(samples-1))*(x);
0164                             channelValues[1]=(max/(samples-1))*(y);
0165                             channelValues[2]=(max/(samples-1))*(z);
0166                             channelValues[3]=max;
0167                             if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz.
0168                                 fromNormalisedChannelsValue(data, channelValues);
0169                                 convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric,         KoColorConversionTransformation::adjustmentConversionFlags());
0170                                 xyzColorSpace->normalisedChannelsValue(data2,channelValues);
0171                             }
0172                             qreal x = channelValues[0]/(channelValues[0]+channelValues[1]+channelValues[2]);
0173                             qreal y = channelValues[1]/(channelValues[0]+channelValues[1]+channelValues[2]);
0174                             d->gamutXYY<< QPointF(x,y);
0175                         }
0176                     }
0177                 }
0178                 
0179             }
0180         }
0181         delete[] data;
0182         //if we ever implement a boundary-checking thing I'd add it here.
0183         return d->gamutXYY;
0184     } else {
0185         return d->gamutXYY;
0186     }
0187 }
0188 
0189 QPolygonF KoColorSpace::estimatedTRCXYY() const
0190 {
0191     if (d->TRCXYY.empty()){
0192         qreal max = 1.0;
0193         if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") {
0194             //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general.
0195             max = this->channels().at(0)->getUIMax();
0196         }
0197         QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF16")->defaultProfile();
0198         const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F16", name);
0199         quint8 *data = new quint8[pixelSize()];
0200         quint8 data2[8]; // xyza is 8 bytes per pixel.
0201 
0202         // This is fixed to 5 since the maximum number of channels are 5 for CMYKA
0203         QVector <qreal> channelValues(5);//for getting the coordinates.
0204 
0205         for (quint32 i=0; i<colorChannelCount(); i++) {
0206             qreal colorantY=1.0;
0207             if (colorModelId().id()!="CMYKA") {
0208                 for (int j=5; j>0; j--){
0209                     channelValues.fill(0.0);
0210                     channelValues[i] = ((max/4)*(5-j));
0211 
0212                     if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz.
0213                         fromNormalisedChannelsValue(data, channelValues);
0214                         convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric,         KoColorConversionTransformation::adjustmentConversionFlags());
0215                         xyzColorSpace->normalisedChannelsValue(data2,channelValues);
0216                     }
0217 
0218                     if (j==0) {
0219                         colorantY = channelValues[1];
0220                         if (d->colorants.size()<2){
0221                             d->colorants.resize(3*colorChannelCount());
0222                             d->colorants[i]  = channelValues[0]/(channelValues[0]+channelValues[1]+channelValues[2]);
0223                             d->colorants[i+1]= channelValues[1]/(channelValues[0]+channelValues[1]+channelValues[2]);
0224                             d->colorants[i+2]= channelValues[1];
0225                         }
0226                     }
0227                     d->TRCXYY << QPointF(channelValues[1]/colorantY, ((1.0/4)*(5-j)));
0228                 }
0229             } else {
0230                 for (int j=0; j<5; j++){
0231                     channelValues.fill(0.0);
0232                     channelValues[i] = ((max/4)*(j));
0233                     
0234                     fromNormalisedChannelsValue(data, channelValues);
0235 
0236                     convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric,         KoColorConversionTransformation::adjustmentConversionFlags());
0237 
0238                     xyzColorSpace->normalisedChannelsValue(data2,channelValues);
0239 
0240                     if (j==0) {
0241                         colorantY = channelValues[1];
0242                         if (d->colorants.size()<2){
0243                             d->colorants.resize(3*colorChannelCount());
0244                             d->colorants[i]  = channelValues[0]/(channelValues[0]+channelValues[1]+channelValues[2]);
0245                             d->colorants[i+1]= channelValues[1]/(channelValues[0]+channelValues[1]+channelValues[2]);
0246                             d->colorants[i+2]= channelValues[1];
0247                         }
0248                     }
0249                     d->TRCXYY << QPointF(channelValues[1]/colorantY, ((1.0/4)*(j)));
0250                 }
0251             }
0252         }
0253 
0254         delete[] data;
0255         return d->TRCXYY;
0256     } else {
0257         return d->TRCXYY;
0258     }
0259 }
0260 
0261 QVector <qreal> KoColorSpace::colorants() const
0262 {
0263     if (d->colorants.size()>1){
0264         return d->colorants;
0265     } else if (profile() && profile()->hasColorants()) {
0266         d->colorants.resize(3*colorChannelCount());
0267         d->colorants = profile()->getColorantsxyY();
0268         return d->colorants;
0269     } else {
0270         estimatedTRCXYY();
0271         return d->colorants;
0272     }
0273 }
0274 QVector <qreal> KoColorSpace::lumaCoefficients() const
0275 {
0276     if (d->lumaCoefficients.size()>1){
0277         return d->lumaCoefficients;
0278     } else {
0279         d->lumaCoefficients.resize(3);
0280         if (colorModelId().id()!="RGBA") {
0281             d->lumaCoefficients.fill(0.33);
0282         } else {
0283             colorants();
0284             if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) {
0285                 d->lumaCoefficients[0]=0.2126;
0286                 d->lumaCoefficients[1]=0.7152;
0287                 d->lumaCoefficients[2]=0.0722;
0288             } else {
0289                 d->lumaCoefficients[0]=d->colorants[2];
0290                 d->lumaCoefficients[1]=d->colorants[5];
0291                 d->lumaCoefficients[2]=d->colorants[8];
0292             }
0293         }
0294         return d->lumaCoefficients;
0295     }
0296 }
0297 
0298 QList<KoChannelInfo *> KoColorSpace::channels() const
0299 {
0300     return d->channels;
0301 }
0302 
0303 QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const
0304 {
0305     QBitArray ba(d->channels.size());
0306     if (!color && !alpha) return ba;
0307 
0308     for (int i = 0; i < d->channels.size(); ++i) {
0309         KoChannelInfo * channel = d->channels.at(i);
0310         if ((color && channel->channelType() == KoChannelInfo::COLOR) ||
0311                 (alpha && channel->channelType() == KoChannelInfo::ALPHA))
0312             ba.setBit(i, true);
0313     }
0314     return ba;
0315 }
0316 
0317 void KoColorSpace::addChannel(KoChannelInfo * ci)
0318 {
0319     d->channels.push_back(ci);
0320 }
0321 bool KoColorSpace::hasCompositeOp(const QString& id) const
0322 {
0323     return d->compositeOps.contains(id);
0324 }
0325 
0326 QList<KoCompositeOp*> KoColorSpace::compositeOps() const
0327 {
0328     return d->compositeOps.values();
0329 }
0330 
0331 KoMixColorsOp* KoColorSpace::mixColorsOp() const
0332 {
0333     return d->mixColorsOp;
0334 }
0335 
0336 
0337 KoConvolutionOp* KoColorSpace::convolutionOp() const
0338 {
0339     return d->convolutionOp;
0340 }
0341 
0342 const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
0343 {
0344     const QHash<QString, KoCompositeOp*>::ConstIterator it = d->compositeOps.constFind(id);
0345     if (it != d->compositeOps.constEnd()) {
0346         return it.value();
0347     }
0348     else {
0349         warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
0350         return d->compositeOps.value(COMPOSITE_OVER);
0351     }
0352 }
0353 
0354 void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
0355 {
0356     if (op->colorSpace()->id() == id()) {
0357         d->compositeOps.insert(op->id(), const_cast<KoCompositeOp*>(op));
0358     }
0359 }
0360 
0361 const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
0362 {
0363     if (!d->transfoToLABA16) {
0364         d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
0365     }
0366     return d->transfoToLABA16;
0367 }
0368 
0369 const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
0370 {
0371     if (!d->transfoFromLABA16) {
0372         d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(""), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
0373     }
0374     return d->transfoFromLABA16;
0375 }
0376 const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
0377 {
0378     if (!d->transfoToRGBA16) {
0379         d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
0380     }
0381     return d->transfoToRGBA16;
0382 }
0383 const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
0384 {
0385     if (!d->transfoFromRGBA16) {
0386         d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16("") , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ;
0387     }
0388     return d->transfoFromRGBA16;
0389 }
0390 
0391 void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
0392 {
0393     toLabA16Converter()->transform(src, dst, nPixels);
0394 }
0395 
0396 void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
0397 {
0398     fromLabA16Converter()->transform(src, dst, nPixels);
0399 }
0400 
0401 void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
0402 {
0403     toRgbA16Converter()->transform(src, dst, nPixels);
0404 }
0405 
0406 void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
0407 {
0408     fromRgbA16Converter()->transform(src, dst, nPixels);
0409 }
0410 
0411 KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
0412 {
0413     if (*this == *dstColorSpace) {
0414         return new KoCopyColorConversionTransformation(this);
0415     } else {
0416         return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags);
0417     }
0418 }
0419 
0420 bool KoColorSpace::convertPixelsTo(const quint8 * src,
0421                                    quint8 * dst,
0422                                    const KoColorSpace * dstColorSpace,
0423                                    quint32 numPixels,
0424                                    KoColorConversionTransformation::Intent renderingIntent,
0425                                    KoColorConversionTransformation::ConversionFlags conversionFlags) const
0426 {
0427     if (*this == *dstColorSpace) {
0428         if (src != dst) {
0429             memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize());
0430         }
0431     } else {
0432         KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags);
0433         cct.transformation()->transform(src, dst, numPixels);
0434     }
0435     return true;
0436 }
0437 
0438 
0439 void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
0440                           KoColorConversionTransformation::Intent renderingIntent,
0441                           KoColorConversionTransformation::ConversionFlags conversionFlags) const
0442 {
0443     Q_ASSERT_X(*op->colorSpace() == *this, "KoColorSpace::bitBlt", QString("Composite op is for color space %1 (%2) while this is %3 (%4)").arg(op->colorSpace()->id()).arg(op->colorSpace()->profile()->name()).arg(id()).arg(profile()->name()).toLatin1());
0444 
0445     if(params.rows <= 0 || params.cols <= 0)
0446         return;
0447 
0448     if(!(*this == *srcSpace)) {
0449          if (preferCompositionInSourceColorSpace() &&
0450              srcSpace->hasCompositeOp(op->id())) {
0451 
0452              quint32           conversionDstBufferStride = params.cols * srcSpace->pixelSize();
0453              QVector<quint8> * conversionDstCache        = threadLocalConversionCache(params.rows * conversionDstBufferStride);
0454              quint8*           conversionDstData         = conversionDstCache->data();
0455 
0456              for(qint32 row=0; row<params.rows; row++) {
0457                  convertPixelsTo(params.dstRowStart + row * params.dstRowStride,
0458                                  conversionDstData  + row * conversionDstBufferStride, srcSpace, params.cols,
0459                                  renderingIntent, conversionFlags);
0460              }
0461 
0462              // FIXME: do not calculate the otherOp every time
0463              const KoCompositeOp *otherOp = srcSpace->compositeOp(op->id());
0464 
0465              KoCompositeOp::ParameterInfo paramInfo(params);
0466              paramInfo.dstRowStart  = conversionDstData;
0467              paramInfo.dstRowStride = conversionDstBufferStride;
0468              otherOp->composite(paramInfo);
0469 
0470              for(qint32 row=0; row<params.rows; row++) {
0471                  srcSpace->convertPixelsTo(conversionDstData  + row * conversionDstBufferStride,
0472                                            params.dstRowStart + row * params.dstRowStride, this, params.cols,
0473                                            renderingIntent, conversionFlags);
0474              }
0475 
0476         } else {
0477             quint32           conversionBufferStride = params.cols * pixelSize();
0478             QVector<quint8> * conversionCache        = threadLocalConversionCache(params.rows * conversionBufferStride);
0479             quint8*           conversionData         = conversionCache->data();
0480 
0481             for(qint32 row=0; row<params.rows; row++) {
0482                 srcSpace->convertPixelsTo(params.srcRowStart + row * params.srcRowStride,
0483                                           conversionData     + row * conversionBufferStride, this, params.cols,
0484                                           renderingIntent, conversionFlags);
0485             }
0486 
0487             KoCompositeOp::ParameterInfo paramInfo(params);
0488             paramInfo.srcRowStart  = conversionData;
0489             paramInfo.srcRowStride = conversionBufferStride;
0490             op->composite(paramInfo);
0491         }
0492     }
0493     else {
0494         op->composite(params);
0495     }
0496 }
0497 
0498 
0499 QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
0500 {
0501     QVector<quint8> * ba = 0;
0502     if (!d->conversionCache.hasLocalData()) {
0503         ba = new QVector<quint8>(size, '0');
0504         d->conversionCache.setLocalData(ba);
0505     } else {
0506         ba = d->conversionCache.localData();
0507         if ((quint8)ba->size() < size)
0508             ba->resize(size);
0509     }
0510     return ba;
0511 }
0512 
0513 KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
0514 {
0515     KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
0516     if (!factory) return 0;
0517     QPair<KoID, KoID> model(colorModelId(), colorDepthId());
0518     QList< QPair<KoID, KoID> > models = factory->supportedModels();
0519     if (models.isEmpty() || models.contains(model)) {
0520         return factory->createTransformation(this, parameters);
0521     } else {
0522         // Find the best solution
0523         // TODO use the color conversion cache
0524         KoColorConversionTransformation* csToFallBack = 0;
0525         KoColorConversionTransformation* fallBackToCs = 0;
0526         KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs);
0527         Q_ASSERT(csToFallBack);
0528         Q_ASSERT(fallBackToCs);
0529         KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters);
0530         return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
0531     }
0532 }
0533 
0534 void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{
0535     int channelnumber = channelCount();
0536     QVector <qreal> channelValues(channelnumber);
0537     normalisedChannelsValue(pixel, channelValues);
0538     if (profile()->hasTRC()){
0539         //only linearise and crunch the luma if there's a TRC
0540         profile()->linearizeFloatValue(channelValues);
0541         qreal hue, sat, luma = 0.0;
0542         toHSY(channelValues, &hue, &sat, &luma);
0543         luma = pow(luma, 1/2.2);
0544         luma = qMin<qreal>(1.0, luma + step);
0545         luma = pow(luma, 2.2);
0546         channelValues = fromHSY(&hue, &sat, &luma);
0547         profile()->delinearizeFloatValue(channelValues);
0548     } else {
0549         qreal hue, sat, luma = 0.0;
0550         toHSY(channelValues, &hue, &sat, &luma);
0551         luma = qMin<qreal>(1.0, luma + step);
0552         channelValues = fromHSY(&hue, &sat, &luma);
0553     }
0554     fromNormalisedChannelsValue(pixel, channelValues);
0555     setOpacity(pixel, qreal(1.0), 1);
0556 }
0557 void KoColorSpace::decreaseLuminosity(quint8 * pixel, qreal step) const {
0558     int channelnumber = channelCount();
0559     QVector <qreal> channelValues(channelnumber);
0560     normalisedChannelsValue(pixel, channelValues);
0561     if (profile()->hasTRC()){
0562         //only linearise and crunch the luma if there's a TRC
0563         profile()->linearizeFloatValue(channelValues);
0564         qreal hue, sat, luma = 0.0;
0565         toHSY(channelValues, &hue, &sat, &luma);
0566         luma = pow(luma, 1/2.2);
0567         if (luma-step<0.0) {
0568             luma=0.0;
0569         } else {
0570             luma -= step;
0571         }
0572         luma = pow(luma, 2.2);
0573         channelValues = fromHSY(&hue, &sat, &luma);
0574         profile()->delinearizeFloatValue(channelValues);
0575     } else {
0576         qreal hue, sat, luma = 0.0;
0577         toHSY(channelValues, &hue, &sat, &luma);
0578         if (luma-step<0.0) {
0579             luma=0.0;
0580         } else {
0581             luma -= step;
0582         }
0583         channelValues = fromHSY(&hue, &sat, &luma);
0584     }
0585     fromNormalisedChannelsValue(pixel, channelValues);
0586     setOpacity(pixel, qreal(1.0), 1);
0587 }
0588 void KoColorSpace::increaseSaturation(quint8 * pixel, qreal step) const{
0589     int channelnumber = channelCount();
0590     QVector <qreal> channelValues(channelnumber);
0591     normalisedChannelsValue(pixel, channelValues);
0592     profile()->linearizeFloatValue(channelValues);
0593     qreal hue, sat, luma = 0.0;
0594     toHSY(channelValues, &hue, &sat, &luma);
0595     sat += step;
0596     sat = qBound<qreal>(0.0, sat, 1.0);
0597     channelValues = fromHSY(&hue, &sat, &luma);
0598     profile()->delinearizeFloatValue(channelValues);
0599     fromNormalisedChannelsValue(pixel, channelValues);
0600     setOpacity(pixel, qreal(1.0), 1);
0601 }
0602 void KoColorSpace::decreaseSaturation(quint8 * pixel, qreal step) const{
0603     int channelnumber = channelCount();
0604     QVector <qreal> channelValues(channelnumber);
0605     normalisedChannelsValue(pixel, channelValues);
0606     profile()->linearizeFloatValue(channelValues);
0607     qreal hue, sat, luma = 0.0;
0608     toHSY(channelValues, &hue, &sat, &luma);
0609     sat -= step;
0610     sat = qBound<qreal>(0.0, sat, 1.0);
0611     channelValues = fromHSY(&hue, &sat, &luma);
0612     profile()->delinearizeFloatValue(channelValues);
0613     fromNormalisedChannelsValue(pixel, channelValues);
0614     setOpacity(pixel, qreal(1.0), 1);
0615 }
0616 void KoColorSpace::increaseHue(quint8 * pixel, qreal step) const{
0617     int channelnumber = channelCount(); //doesn't work for cmyka...
0618     QVector <qreal> channelValues(channelnumber);
0619     normalisedChannelsValue(pixel, channelValues);
0620     profile()->linearizeFloatValue(channelValues);
0621     qreal hue, sat, luma = 0.0;
0622     toHSY(channelValues, &hue, &sat, &luma);
0623     if (hue+step>1.0){
0624         hue=(hue+step)- 1.0;
0625     } else {
0626         hue += step;
0627     }
0628     channelValues = fromHSY(&hue, &sat, &luma);
0629     profile()->delinearizeFloatValue(channelValues);
0630     fromNormalisedChannelsValue(pixel, channelValues);
0631     setOpacity(pixel, qreal(1.0), 1);
0632 }
0633 void KoColorSpace::decreaseHue(quint8 * pixel, qreal step) const{
0634     int channelnumber = channelCount();
0635     QVector <qreal> channelValues(channelnumber);
0636     normalisedChannelsValue(pixel, channelValues);
0637     profile()->linearizeFloatValue(channelValues);
0638     qreal hue, sat, luma = 0.0;
0639     toHSY(channelValues, &hue, &sat, &luma);
0640     if (hue-step<0.0){
0641         hue=1.0-(step-hue);
0642     } else {
0643         hue -= step;
0644     }
0645     channelValues = fromHSY(&hue, &sat, &luma);
0646     profile()->delinearizeFloatValue(channelValues);
0647     fromNormalisedChannelsValue(pixel, channelValues);
0648     setOpacity(pixel, qreal(1.0), 1);
0649 }
0650 
0651 void KoColorSpace::increaseRed(quint8 * pixel, qreal step) const{
0652     int channelnumber = channelCount();
0653     QVector <qreal> channelValues(channelnumber);
0654     normalisedChannelsValue(pixel, channelValues);
0655     profile()->linearizeFloatValue(channelValues);
0656     qreal y, u, v = 0.0;
0657     toYUV(channelValues, &y, &u, &v);
0658     u += step;
0659     u = qBound<qreal>(0.0, u, 1.0);
0660     channelValues = fromYUV(&y, &u, &v);
0661     profile()->delinearizeFloatValue(channelValues);
0662     fromNormalisedChannelsValue(pixel, channelValues);
0663     setOpacity(pixel, qreal(1.0), 1);
0664 }
0665 void KoColorSpace::increaseGreen(quint8 * pixel, qreal step) const{
0666     int channelnumber = channelCount();
0667     QVector <qreal> channelValues(channelnumber);
0668     normalisedChannelsValue(pixel, channelValues);
0669     profile()->linearizeFloatValue(channelValues);
0670     qreal y, u, v = 0.0;
0671     toYUV(channelValues, &y, &u, &v);
0672     u -= step;
0673     u = qBound<qreal>(0.0, u, 1.0);
0674     channelValues = fromYUV(&y, &u, &v);
0675     profile()->delinearizeFloatValue(channelValues);
0676     fromNormalisedChannelsValue(pixel, channelValues);
0677     setOpacity(pixel, qreal(1.0), 1);
0678 }
0679 void KoColorSpace::increaseBlue(quint8 * pixel, qreal step) const{
0680     int channelnumber = channelCount();
0681     QVector <qreal> channelValues(channelnumber);
0682     normalisedChannelsValue(pixel, channelValues);
0683     profile()->linearizeFloatValue(channelValues);
0684     qreal y, u, v = 0.0;
0685     toYUV(channelValues, &y, &u, &v);
0686     v += step;
0687     v = qBound<qreal>(0.0, v, 1.0);
0688     channelValues = fromYUV(&y, &u, &v);
0689     profile()->delinearizeFloatValue(channelValues);
0690     fromNormalisedChannelsValue(pixel, channelValues);
0691     setOpacity(pixel, qreal(1.0), 1);
0692 }
0693 void KoColorSpace::increaseYellow(quint8 * pixel, qreal step) const{
0694     int channelnumber = channelCount();
0695     QVector <qreal> channelValues(channelnumber);
0696     normalisedChannelsValue(pixel, channelValues);
0697     profile()->linearizeFloatValue(channelValues);
0698     qreal y, u, v = 0.0;
0699     toYUV(channelValues, &y, &u, &v);
0700     v -= step;
0701     v = qBound<qreal>(0.0, v, 1.0);
0702     channelValues = fromYUV(&y, &u, &v);
0703     profile()->delinearizeFloatValue(channelValues);
0704     fromNormalisedChannelsValue(pixel, channelValues);
0705     setOpacity(pixel, qreal(1.0), 1);
0706 }
0707 QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
0708                                      const KoColorProfile *dstProfile,
0709                                      KoColorConversionTransformation::Intent renderingIntent,
0710                                      KoColorConversionTransformation::ConversionFlags conversionFlags) const
0711 
0712 {
0713     QImage img = QImage(width, height, QImage::Format_ARGB32);
0714 
0715     const KoColorSpace * dstCS = KoColorSpaceRegistry::instance()->rgb8(dstProfile);
0716 
0717     if (data)
0718         this->convertPixelsTo(const_cast<quint8 *>(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags);
0719 
0720     return img;
0721 }
0722 
0723 bool KoColorSpace::preferCompositionInSourceColorSpace() const
0724 {
0725     return false;
0726 }