diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp index a0bea21fe6..d328511ad3 100644 --- a/libs/pigment/KoColorSpace.cpp +++ b/libs/pigment/KoColorSpace.cpp @@ -1,825 +1,826 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSpace.h" #include "KoColorSpace_p.h" #include "KoChannelInfo.h" #include "DebugPigment.h" #include "KoCompositeOp.h" #include "KoColorTransformation.h" #include "KoColorTransformationFactory.h" #include "KoColorTransformationFactoryRegistry.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "KoColorSpaceRegistry.h" #include "KoColorProfile.h" #include "KoCopyColorConversionTransformation.h" #include "KoFallBackColorTransformation.h" #include "KoUniqueNumberForIdServer.h" #include "KoMixColorsOp.h" #include "KoConvolutionOp.h" #include "KoCompositeOpRegistry.h" #include "KoColorSpaceEngine.h" #include #include #include #include #include #include KoColorSpace::KoColorSpace() : d(new Private()) { } KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp) : d(new Private()) { d->id = id; d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id); d->name = name; d->mixColorsOp = mixColorsOp; d->convolutionOp = convolutionOp; d->transfoToRGBA16 = 0; d->transfoFromRGBA16 = 0; d->transfoToLABA16 = 0; d->transfoFromLABA16 = 0; d->gamutXYY = QPolygonF(); d->TRCXYY = QPolygonF(); d->colorants = QVector (0); d->lumaCoefficients = QVector (0); d->iccEngine = 0; d->deletability = NotOwnedByRegistry; } KoColorSpace::~KoColorSpace() { Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete); qDeleteAll(d->compositeOps); Q_FOREACH (KoChannelInfo * channel, d->channels) { delete channel; } if (d->deletability == NotOwnedByRegistry) { KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache(); if (cache) { cache->colorSpaceIsDestroyed(this); } } delete d->mixColorsOp; delete d->convolutionOp; delete d->transfoToRGBA16; delete d->transfoFromRGBA16; delete d->transfoToLABA16; delete d->transfoFromLABA16; delete d; } bool KoColorSpace::operator==(const KoColorSpace& rhs) const { const KoColorProfile* p1 = rhs.profile(); const KoColorProfile* p2 = profile(); return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2)); } QString KoColorSpace::id() const { return d->id; } QString KoColorSpace::name() const { return d->name; } //Color space info stuff. QPolygonF KoColorSpace::gamutXYY() const { if (d->gamutXYY.empty()) { //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. //first make a list of colors. qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } int samples = 5;//amount of samples in our color space. QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF32")->defaultProfile(); const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32", name); quint8 *data = new quint8[pixelSize()]; quint8 data2[16]; // xyza f32 is 4 floats, that is 16 bytes per pixel. //QVector sampleCoordinates(pow(colorChannelCount(),samples)); //sampleCoordinates.fill(0.0); // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for(int x=0;xnormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY << QPointF(x,y); } else { for(int y=0;ynormalisedChannelsValue(data2, channelValuesF); qreal x = channelValuesF[0] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); qreal y = channelValuesF[1] / (channelValuesF[0] + channelValuesF[1] + channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } else { channelValuesF[0]=(max/(samples-1))*(x); channelValuesF[1]=(max/(samples-1))*(y); channelValuesF[2]=(max/(samples-1))*(z); channelValuesF[3]=max; if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } qreal x = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); qreal y = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->gamutXYY<< QPointF(x,y); } } } } } delete[] data; //if we ever implement a boundary-checking thing I'd add it here. return d->gamutXYY; } else { return d->gamutXYY; } } QPolygonF KoColorSpace::estimatedTRCXYY() const { if (d->TRCXYY.empty()){ qreal max = 1.0; if ((colorModelId().id()=="CMYKA" || colorModelId().id()=="LABA") && colorDepthId().id()=="F32") { //boundaries for cmyka/laba have trouble getting the max values for Float, and are pretty awkward in general. max = this->channels()[0]->getUIMax(); } QString name = KoColorSpaceRegistry::instance()->colorSpaceFactory("XYZAF16")->defaultProfile(); const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F16", name); quint8 *data = new quint8[pixelSize()]; quint8 data2[8]; // xyza is 8 bytes per pixel. // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. for (quint32 i=0; i0; j--){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(5-j)); if (colorModelId().id()!="XYZA") { //no need for conversion when using xyz. fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); } if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(5-j))); } } else { for (int j=0; j<5; j++){ channelValuesF.fill(0.0); channelValuesF[i] = ((max/4)*(j)); fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); if (j==0) { colorantY = channelValuesF[1]; if (d->colorants.size()<2){ d->colorants.resize(3*colorChannelCount()); d->colorants[i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+1]= channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[i+2]= channelValuesF[1]; } } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/4)*(j))); } } } delete[] data; return d->TRCXYY; } else { return d->TRCXYY; } } QVector KoColorSpace::colorants() const { if (d->colorants.size()>1){ return d->colorants; } else if (profile() && profile()->hasColorants()) { d->colorants.resize(3*colorChannelCount()); d->colorants = profile()->getColorantsxyY(); return d->colorants; } else { estimatedTRCXYY(); return d->colorants; } } QVector KoColorSpace::lumaCoefficients() const { if (d->lumaCoefficients.size()>1){ return d->lumaCoefficients; } else { d->lumaCoefficients.resize(3); if (colorModelId().id()!="RGBA") { d->lumaCoefficients.fill(0.33); } else { colorants(); if (d->colorants[2]<0 || d->colorants[5]<0 || d->colorants[8]<0) { d->lumaCoefficients[0]=0.2126; d->lumaCoefficients[1]=0.7152; d->lumaCoefficients[2]=0.0722; } else { d->lumaCoefficients[0]=d->colorants[2]; d->lumaCoefficients[1]=d->colorants[5]; d->lumaCoefficients[2]=d->colorants[8]; } } return d->lumaCoefficients; } } QList KoColorSpace::channels() const { return d->channels; } QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const { QBitArray ba(d->channels.size()); if (!color && !alpha) return ba; for (int i = 0; i < d->channels.size(); ++i) { KoChannelInfo * channel = d->channels.at(i); if ((color && channel->channelType() == KoChannelInfo::COLOR) || (alpha && channel->channelType() == KoChannelInfo::ALPHA)) ba.setBit(i, true); } return ba; } void KoColorSpace::addChannel(KoChannelInfo * ci) { d->channels.push_back(ci); } bool KoColorSpace::hasCompositeOp(const QString& id) const { return d->compositeOps.contains(id); } QList KoColorSpace::compositeOps() const { return d->compositeOps.values(); } KoMixColorsOp* KoColorSpace::mixColorsOp() const { return d->mixColorsOp; } KoConvolutionOp* KoColorSpace::convolutionOp() const { return d->convolutionOp; } const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const { const QHash::ConstIterator it = d->compositeOps.constFind(id); if (it != d->compositeOps.constEnd()) { return it.value(); } else { warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER; return d->compositeOps.value(COMPOSITE_OVER); } } void KoColorSpace::addCompositeOp(const KoCompositeOp * op) { if (op->colorSpace()->id() == id()) { d->compositeOps.insert(op->id(), const_cast(op)); } } const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const { if (!d->transfoToLABA16) { d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToLABA16; } const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const { if (!d->transfoFromLABA16) { d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(""), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromLABA16; } const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const { if (!d->transfoToRGBA16) { d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(""), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToRGBA16; } const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const { if (!d->transfoFromRGBA16) { d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16("") , this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromRGBA16; } void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromLabA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { toRgbA16Converter()->transform(src, dst, nPixels); } void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const { fromRgbA16Converter()->transform(src, dst, nPixels); } KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { return new KoCopyColorConversionTransformation(this); } else { return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent, conversionFlags); } } bool KoColorSpace::convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { if (*this == *dstColorSpace) { if (src != dst) { memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize()); } } else { KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, conversionFlags); cct.transformation()->transform(src, dst, numPixels); } return true; } bool KoColorSpace::proofPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, const KoColorSpace * proofingSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { /*if (*this == *dstColorSpace) { if (src != dst) { memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize()); } return true; }*/ // Only the icc engine can do this kind of stuff //Can we cache this, maybe??? - if (d->iccEngine) { + if (!d->iccEngine) { d->iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); - //qDebug() << ">>>>>>>>>>>>>>>>>>>> we got a proofing engine"; + qDebug() << ">>>>>>>>>>>>>>>>>>>> we got a proofing engine"; } if (!d->iccEngine) return false; KoColorConversionTransformation *transform = d->iccEngine->createColorProofingTransformation(this, dstColorSpace, proofingSpace, renderingIntent, conversionFlags); - Q_UNUSED(transform); + //Q_UNUSED(transform); + transform->transform(src, dst, numPixels); delete transform; return true; } void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { 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()); if(params.rows <= 0 || params.cols <= 0) return; if(!(*this == *srcSpace)) { if (preferCompositionInSourceColorSpace() && srcSpace->hasCompositeOp(op->id())) { quint32 conversionDstBufferStride = params.cols * srcSpace->pixelSize(); QVector * conversionDstCache = threadLocalConversionCache(params.rows * conversionDstBufferStride); quint8* conversionDstData = conversionDstCache->data(); for(qint32 row=0; rowcompositeOp(op->id()); KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.dstRowStart = conversionDstData; paramInfo.dstRowStride = conversionDstBufferStride; otherOp->composite(paramInfo); for(qint32 row=0; rowconvertPixelsTo(conversionDstData + row * conversionDstBufferStride, params.dstRowStart + row * params.dstRowStride, this, params.cols, renderingIntent, conversionFlags); } } else { quint32 conversionBufferStride = params.cols * pixelSize(); QVector * conversionCache = threadLocalConversionCache(params.rows * conversionBufferStride); quint8* conversionData = conversionCache->data(); for(qint32 row=0; rowconvertPixelsTo(params.srcRowStart + row * params.srcRowStride, conversionData + row * conversionBufferStride, this, params.cols, renderingIntent, conversionFlags); } KoCompositeOp::ParameterInfo paramInfo(params); paramInfo.srcRowStart = conversionData; paramInfo.srcRowStride = conversionBufferStride; op->composite(paramInfo); } } else { op->composite(params); } } QVector * KoColorSpace::threadLocalConversionCache(quint32 size) const { QVector * ba = 0; if (!d->conversionCache.hasLocalData()) { ba = new QVector(size, '0'); d->conversionCache.setLocalData(ba); } else { ba = d->conversionCache.localData(); if ((quint8)ba->size() < size) ba->resize(size); } return ba; } KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash & parameters) const { KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id); if (!factory) return 0; QPair model(colorModelId(), colorDepthId()); QList< QPair > models = factory->supportedModels(); if (models.isEmpty() || models.contains(model)) { return factory->createTransformation(this, parameters); } else { // Find the best solution // TODO use the color conversion cache KoColorConversionTransformation* csToFallBack = 0; KoColorConversionTransformation* fallBackToCs = 0; KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs); Q_ASSERT(csToFallBack); Q_ASSERT(fallBackToCs); KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters); return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo); } } void KoColorSpace::increaseLuminosity(quint8 * pixel, qreal step) const{ int channelnumber = channelCount(); QVector channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); luma = qMin(1.0, luma + step); luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = qMin(1.0, luma + step); channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ihasTRC()){ //only linearise and crunch the luma if there's a TRC profile()->linearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); luma = pow(luma, 1/2.2); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } luma = pow(luma, 2.2); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); } else { qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (luma-step<0.0) { luma=0.0; } else { luma -= step; } channelValues = fromHSY(&hue, &sat, &luma); } for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat += step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); sat -= step; sat = qBound(0.0, sat, 1.0); channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue+step>1.0){ hue=(hue+step)- 1.0; } else { hue += step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal hue, sat, luma = 0.0; toHSY(channelValues, &hue, &sat, &luma); if (hue-step<0.0){ hue=1.0-(step-hue); } else { hue -= step; } channelValues = fromHSY(&hue, &sat, &luma); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u += step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); u -= step; u = qBound(0.0, u, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v += step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;i channelValues(channelnumber); QVector channelValuesF(channelnumber); normalisedChannelsValue(pixel, channelValuesF); for (int i=0;ilinearizeFloatValue(channelValues); qreal y, u, v = 0.0; toYUV(channelValues, &y, &u, &v); v -= step; v = qBound(0.0, v, 1.0); channelValues = fromYUV(&y, &u, &v); profile()->delinearizeFloatValue(channelValues); for (int i=0;irgb8(dstProfile); if (data) this->convertPixelsTo(const_cast(data), img.bits(), dstCS, width * height, renderingIntent, conversionFlags); return img; } bool KoColorSpace::preferCompositionInSourceColorSpace() const { return false; } diff --git a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp index f15570d83f..49b38ed96c 100644 --- a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp +++ b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp @@ -1,310 +1,311 @@ /* * Copyright (c) 2007-2008 Cyrille Berger * Copyright (c) 2011 Srikanth Tiyyagura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "IccColorSpaceEngine.h" #include "KoColorModelStandardIds.h" #include #include "LcmsColorSpace.h" #include // -- KoLcmsColorConversionTransformation -- class KoLcmsColorConversionTransformation : public KoColorConversionTransformation { public: KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, Intent renderingIntent, ConversionFlags conversionFlags) : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags) , m_transform(0) { Q_ASSERT(srcCs); Q_ASSERT(dstCs); Q_ASSERT(renderingIntent < 4); if (srcCs->colorDepthId() == Integer8BitsColorDepthID || srcCs->colorDepthId() == Integer16BitsColorDepthID) { if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) || dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) && !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { conversionFlags |= KoColorConversionTransformation::NoOptimization; } } m_transform = cmsCreateTransform(srcProfile->lcmsProfile(), srcColorSpaceType, dstProfile->lcmsProfile(), dstColorSpaceType, renderingIntent, conversionFlags); Q_ASSERT(m_transform); } ~KoLcmsColorConversionTransformation() { cmsDeleteTransform(m_transform); } public: virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const { Q_ASSERT(m_transform); qint32 srcPixelSize = srcColorSpace()->pixelSize(); qint32 dstPixelSize = dstColorSpace()->pixelSize(); cmsDoTransform(m_transform, const_cast(src), dst, numPixels); - // Lcms does nothing to the destination alpha channel so we must convert that manually. while (numPixels > 0) { qreal alpha = srcColorSpace()->opacityF(src); dstColorSpace()->setOpacity(dst, alpha, 1); src += srcPixelSize; dst += dstPixelSize; numPixels--; } } private: mutable cmsHTRANSFORM m_transform; }; class KoLcmsColorProofingConversionTransformation : public KoColorProofingConversionTransformation { public: KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, const KoColorSpace *proofingSpace, Intent renderingIntent, ConversionFlags conversionFlags ) : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, conversionFlags) , m_transform(0) { Q_ASSERT(srcCs); Q_ASSERT(dstCs); Q_ASSERT(renderingIntent < 4); if (srcCs->colorDepthId() == Integer8BitsColorDepthID || srcCs->colorDepthId() == Integer16BitsColorDepthID) { if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) || dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) && !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { conversionFlags |= KoColorConversionTransformation::NoOptimization; } } - quint16 alarm[4];//cyan! - alarm[0] = 65535; - alarm[1] = 0; - alarm[2] = 0; - alarm[3] = 65535; + quint16 alarm[cmsMAXCHANNELS];//cyan! + alarm[0] = 0; + alarm[1] = 65535; + alarm[2] = 65535; cmsSetAlarmCodes(alarm); + qDebug()<<"Test gamut check"; + qDebug()<lcmsProfile(), srcColorSpaceType, dstProfile->lcmsProfile(), dstColorSpaceType, dynamic_cast(proofingSpace->profile())->asLcms()->lcmsProfile(), renderingIntent, renderingIntent, conversionFlags); Q_ASSERT(m_transform); } ~KoLcmsColorProofingConversionTransformation() { cmsDeleteTransform(m_transform); } public: virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const { Q_ASSERT(m_transform); qint32 srcPixelSize = srcColorSpace()->pixelSize(); qint32 dstPixelSize = dstColorSpace()->pixelSize(); cmsDoTransform(m_transform, const_cast(src), dst, numPixels); - // Lcms does nothing to the destination alpha channel so we must convert that manually. while (numPixels > 0) { qreal alpha = srcColorSpace()->opacityF(src); dstColorSpace()->setOpacity(dst, alpha, 1); src += srcPixelSize; dst += dstPixelSize; numPixels--; } } private: mutable cmsHTRANSFORM m_transform; }; struct IccColorSpaceEngine::Private { }; IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private) { } IccColorSpaceEngine::~IccColorSpaceEngine() { delete d; } void IccColorSpaceEngine::addProfile(const QString &filename) { KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); // this our own loading code; sometimes it fails because of an lcms error profile->load(); // and then lcms can read the profile from file itself without problems, // quite often, and we can initialize it if (!profile->valid()) { cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r"); profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp); } if (profile->valid()) { qDebug() << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfile(profile); } else { qDebug() << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; } } void IccColorSpaceEngine::removeProfile(const QString &filename) { KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); profile->load(); if (profile->valid() && registry->profileByName(profile->name())) { registry->removeProfile(profile); } } KoColorConversionTransformation *IccColorSpaceEngine::createColorTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); return new KoLcmsColorConversionTransformation( srcColorSpace, computeColorSpaceType(srcColorSpace), dynamic_cast(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), dynamic_cast(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags); } KoColorProofingConversionTransformation *IccColorSpaceEngine::createColorProofingTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags ) const { Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); return new KoLcmsColorProofingConversionTransformation( srcColorSpace, computeColorSpaceType(srcColorSpace), dynamic_cast(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), dynamic_cast(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, conversionFlags ); } quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace *cs) const { Q_ASSERT(cs); if (const KoLcmsInfo *lcmsInfo = dynamic_cast(cs)) { return lcmsInfo->colorSpaceType(); } else { QString modelId = cs->colorModelId().id(); QString depthId = cs->colorDepthId().id(); // Compute the depth part of the type quint32 depthType; if (depthId == Integer8BitsColorDepthID.id()) { depthType = BYTES_SH(1); } else if (depthId == Integer16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float32BitsColorDepthID.id()) { depthType = BYTES_SH(4); } else if (depthId == Float64BitsColorDepthID.id()) { depthType = BYTES_SH(0); } else { qWarning() << "Unknow bit depth"; return 0; } // Compute the model part of the type quint32 modelType = 0; if (modelId == RGBAColorModelID.id()) { if (depthId.startsWith(QLatin1Char('U'))) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1)); } else if (depthId.startsWith(QLatin1Char('F'))) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3)); } } else if (modelId == XYZAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == LABAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == CMYKAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4)); } else if (modelId == GrayAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1)); } else if (modelId == GrayColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1)); } else if (modelId == YCbCrAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3)); } else { qWarning() << "Cannot convert colorspace to lcms modeltype"; return 0; } return depthType | modelType; } }